Objective-c objects, OC objects for short, fall into three main categories
1. Instance object
instance
An object is an object that comes out of the alloc class, and every time you call alloc, a new instance object is created- The code performance
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
Copy the code
- Object1, object2 are NSObject
instance
Object (The instanceObject) - They are two different objects occupying two different pieces of memory
instace
Object stores information in memory includingisa
Pointer to the- other
Member variables
2. Class object
- The code performance
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = [NSObject class];
Class objectClass4 = object_getClass(object1); // Runtime
Class objectClass5 = object_getClass(object2); // Runtime
Copy the code
- ObjectClass1 ~ objectClass5 are all NSObject
class
Object (class object), the class method always returnsclass
object - They’re all the same object. Each class has one and only one in memory
class
object class
Object stores information in memory mainly includingisa
Pointer to thesuperclass
Pointer to the- Of the classattributeInformation (
@property
), the classObject methodsInformation (instance method
) - Of the classagreementInformation (
protocol
), the classMember variablesInformation (ivar
)
3. Meta-class objects (metaclass objects)
- The code performance
Class objectMetaClass = object_getClass([NSObject class]); // Runtime
Copy the code
- ObjectMetaClass is
NSObject
themeta-class
object - Each class has one and only one in memory
meta-class
object meta-class
The objects andclass
The memory structure of objects is the same, but the purpose is different. The information stored in the memory mainly includesisa
Pointer to thesuperclass
Pointer to the- Class method information for class (
class method
)
- To view
Class
Whether it ismeta-class
BOOL result = class_isMetaClass([NSObject class]); // Runtime
Copy the code
Object_getClass internal implementation
- Some of the underlying implementations have changed due to objective-C and Swift mixing issues
Class objc_getClass(const char *aClassName)
{
if(! aClassName)return Nil;
// NO unconnected, YES class handler
return look_up_class(aClassName, NO.YES);
}
Class look_up_class(const char *name,
bool includeUnconnected __attribute__((unused)),
bool includeClassHandler __attribute__((unused)))
{
// Return nil if the class name is empty
if(! name)return nil;
// Define the Class result result
Class result;
// The variable unrealized is not realized
bool unrealized;
{
/ / lock
runtimeLock.lock();
// Get the non-swift Class
result = getClassExceptSomeSwift(name);
// If result has a value and the result class method is implementedunrealized = result && ! result->isRealized();if (unrealized) {
/ / unrealized, would call realizeClassMaybeSwiftAndUnlock implementation, at the same time to unlock
result = realizeClassMaybeSwiftAndUnlock(result, runtimeLock);
// runtimeLock is now unlocked
} else {
/ / unlockruntimeLock.unlock(); }}if(! result) {// Ask Swift about its un-instantiated classes.
// We use thread-local storage to prevent infinite recursion
// if the hook function provokes another lookup of the same name
// (for example, if the hook calls objc_allocateClassPair)
auto *tls = _objc_fetch_pthread_data(true);
// Stop if this thread is already looking up this name.
for (unsigned i = 0; i < tls->classNameLookupsUsed; i++) {
if (0 == strcmp(name, tls->classNameLookups[i])) {
return nil; }}// Save this lookup in tls.
if (tls->classNameLookupsUsed == tls->classNameLookupsAllocated) {
tls->classNameLookupsAllocated =
(tls->classNameLookupsAllocated * 2? :1);
size_t size = tls->classNameLookupsAllocated *
sizeof(tls->classNameLookups[0]);
tls->classNameLookups = (const char **)
realloc(tls->classNameLookups, size);
}
tls->classNameLookups[tls->classNameLookupsUsed++] = name;
// Call the hook.
Class swiftcls = nil;
if (GetClassHook.get()(name, &swiftcls)) {
ASSERT(swiftcls->isRealized());
result = swiftcls;
}
// Erase the name from tls.
unsigned slot = --tls->classNameLookupsUsed;
ASSERT(slot >= 0 && slot < tls->classNameLookupsAllocated);
ASSERT(name == tls->classNameLookups[slot]);
tls->classNameLookups[slot] = nil;
}
return result;
}
Copy the code
Tell me about the isa
instance
The object’sisa
Point to theclass
- When calling
Object methods
When, throughinstance
theisa
findclass
And finally foundObject methods
The implementation of the
- When calling
class
theisa
Point to themeta-class
- When calling
Class method
When, throughclass
theisa
findmeta-class
And finally foundClass method
The implementation of the
- When calling
meta-class
theisa
Point to theThe base classthemeta-class
Tell me something about the superclass
class
thesuperclass
Point to themeta-class
- If there is no parent class,
superclass
A pointer tonil
- If there is no parent class,
meta-class
thesuperclass
Point to theThe base classthemeta-class
- The base class
meta-class
thesuperclass
Points to the base classclass
- The base class
- In example 1(the instance object calls the parent’s object method),Student’s instance calls Person’s object method flow
- In example 2(the class object calls the class method of the parent class),Student’s class object calls the class method flow of the Person class
Classic ISA and Superclass diagrams
- Inttance Indicates the path of calling an object method
- When isa finds a method that does not exist, it uses superclass to find a method that does not exist. When isa finds a method that does not exist, it uses superclass to find a method that does not exist. When isa finds a method that does not exist, it uses superclass to find a method that does not exist
unrecognized selector sent to instance 0xxxxxxxxx
- When isa finds a method that does not exist, it uses superclass to find a method that does not exist. When isa finds a method that does not exist, it uses superclass to find a method that does not exist. When isa finds a method that does not exist, it uses superclass to find a method that does not exist
- Class calls the trace of a class method
- Superclass = superclass; superclass = superclass
- This has a special case 🙊, find meta-class is not found, because
meta-class
thesuperclass
Point to theThe base classthemeta-class
Is calledThe base classthemeta-class
This is not surprising when you call a class method and end up calling an object method of the base class.- Sample code is as follows
#import <Foundation/Foundation.h> #import <objc/objc.h> @interface XYPerson : NSObject + (void)test; @end @implementation XYPerson @end @interface NSObject (Test) + (void)test; @end @implementation NSObject (Test) - (void)test { NSLog(@"-[NSObject test] - %p".self); } @end int main(int argc, const char * argv[]) { @autoreleasepool { // [XYPerson class] - 0x1000041e0 NSLog(@"[XYPerson class] - %p", [XYPerson class]); // [NSObject class] - 0x7fff90dd4118 NSLog(@"[NSObject class] - %p"[NSObject class]); // -[NSObject test] - 0x1000041e0 [XYPerson test]; // objc_msgSend([XYPerson class], @selector(test)); // -[NSObject test] - 0x7fff90dd4118 [NSObject test]; // objc_msgSend([NSObject class], @selector(test)); } return 0; } Copy the code
Talk about ISA and Superclass in detail
- Code signal
// Address of the MJPerson class object: 0x00000001000014c8
// Isa of the MJPerson instance object: 0x001D8001000014c9
// isa & ISA_MASK: 0x00000001000014c8
MJPerson *person = [[MJPerson alloc] init];
Class personClass = [MJPerson class];
Class personMetaClass = object_getClass(personClass);
Copy the code
- Starting from 64bit,isa requires a bit operation to calculate the actual ISA address value
- isa & ISA_MASK
- The value of the ISA_MASK
superclass
You do not need to perform this operation, but directly use the corresponding address valueobjc_msgSend
Is to useassembly
Because the call is very frequent, useassembly
Can improve the efficiency of execution
Isa and superclass-class and meta-class structures
- The code structure
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
struct objc_class : objc_object {
/ /...
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() const {
returnbits.data(); }... }struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic<uintptr_t> ro_or_rw_ext;
Class firstSubclass;
Class nextSiblingClass;
};
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
union {
const uint8_t * ivarLayout;
Class nonMetaclass;
};
explicit_atomic<const char *> name;
// With ptrauth, this is signed if it points to a small list, but
// may be unsigned if it points to a big list.
void *baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
};
Copy the code
- Image structure
Simulate the structure inside a class
#import <Foundation/Foundation.h>
#ifndef MJClassInfo_h
#define MJClassInfo_h
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# endif
#if __LP64__
typedef uint32_t mask_t;
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;
struct bucket_t {
cache_key_t _key;
IMP _imp;
};
struct cache_t {
bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
};
struct entsize_list_tt {
uint32_t entsizeAndFlags;
uint32_t count;
};
struct method_t {
SEL name;
const char *types;
IMP imp;
};
struct method_list_t : entsize_list_tt {
method_t first;
};
struct ivar_t {
int32_t *offset;
const char *name;
const char *type;
uint32_t alignment_raw;
uint32_t size;
};
struct ivar_list_t : entsize_list_tt {
ivar_t first;
};
struct property_t {
const char *name;
const char *attributes;
};
struct property_list_t : entsize_list_tt {
property_t first;
};
struct chained_property_list {
chained_property_list *next;
uint32_t count;
property_t list[0];
};
typedef uintptr_t protocol_ref_t;
struct protocol_list_t {
uintptr_t count;
protocol_ref_t list[0];
};
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize; // The memory space occupied by the instance object
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name; / / the name of the class
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars; // List of member variables
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
};
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_list_t * methods; // List of methods
property_list_t *properties; // Attribute list
const protocol_list_t * protocols; // Protocol list
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
};
#define FAST_DATA_MASK 0x00007ffffffffff8UL
struct class_data_bits_t {
uintptr_t bits;
public:
class_rw_t* data() {
return(class_rw_t *)(bits & FAST_DATA_MASK); }};/* OC object */
struct mj_objc_object {
void *isa;
};
/* Class object */
struct mj_objc_class : mj_objc_object {
Class superclass;
cache_t cache;
class_data_bits_t bits;
public:
class_rw_t* data() {
return bits.data();
}
mj_objc_class* metaClass() {
return (mj_objc_class *)((long long)isa & ISA_MASK); }};#endif /* MJClassInfo_h */
// objective-c++
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "MJClassInfo.h"
// MJPerson
@interface MJPerson : NSObject <NSCopying>
{
@public
int _age;
}
@property (nonatomic.assign) int no;
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end
@implementation MJPerson
- (void)test
{
}
- (void)personInstanceMethod
{
}
+ (void)personClassMethod
{
}
- (id)copyWithZone:(NSZone *)zone
{
return nil;
}
@end
// MJStudent
@interface MJStudent : MJPerson <NSCoding>
{
@public
int _weight;
}
@property (nonatomic.assign) int height;
- (void)studentInstanceMethod;
+ (void)studentClassMethod;
@end
@implementation MJStudent
- (void)test
{
}
- (void)studentInstanceMethod
{
}
+ (void)studentClassMethod
{
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
return nil;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJStudent *stu = [[MJStudent alloc] init];
stu->_weight = 10;
mj_objc_class *studentClass = (__bridge mj_objc_class *)([MJStudent class]);
mj_objc_class *personClass = (__bridge mj_objc_class *)([MJPerson class]);
class_rw_t *studentClassData = studentClass->data();
class_rw_t *personClassData = personClass->data();
class_rw_t *studentMetaClassData = studentClass->metaClass()->data();
class_rw_t *personMetaClassData = personClass->metaClass()->data();
NSLog(@ "1111");
}
return 0;
}
Copy the code
The interview questions
- Where does the isa pointer to the object point?
instance
The object’sisaPoint to theclass
objectclass
The object’sisaPoint to themeta-class
objectmeta-class
The object’sisaPoint to theThe base class
themeta-class
object
- Where is the OC class information stored?
Object methods
, attributes, member variables, protocol information, stored inclass
In the objectClass method
And stored inmeta-class
In the objectMember variables
The specific value is stored ininstance
object