A list,
Before Swift, iOS development used Objective-C. Objective-c is a superset of C, and the bottom of Objective-C code is implemented by C/C++ code. Objective-c objects and classes are implemented primarily based on C/C++ constructs. Let’s look at the nature of OC objects.
The nature of OC objects
Create an NSObject object and point to it with an obj pointer.
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
}
return 0;
}
Copy the code
We can use the Clang compiler to convert Objective-C code into C/C++ code, which is compiled C/C++ code, not runtime code per se, but can also help us explore the underlying implementation
- Open the terminal, go to the destination source file directory, and run the following command to obtain the output CPP file.
Xcrun - SDK iphoneos clang -arch arm64-rewrite-objc target OC source file -o Output CPP file main.m as an example -> main-arm64. CPP xcrun - SDK iphoneos clang -arch arm64 -rewrite-objc main.m -o mani-arm64.cppCopy the code
- Let’s go in through Xcode
NSObject
In the declaration file, take a lookNSObject
How is it defined- in
NSObject
In the definition of, there are members namelyisa
, it isClass
The type of Class
By keywordtypedef
Rename the type as can be seenClass
The essence of type isstruct objc_class *
Type, which is a pointer to a structure, which meansisa
It’s one that points to a structurePointer to the- Inside each object there will be one
isa
Pointer to the
- in
@interface NSObject { Class isa; }... @end typedef struct objc_class *Class;Copy the code
- Let’s find it in the generated CPP file
NSObject
The implementation of: can be seenNSObject
The compiled class is passedThe structure of the bodyThe implementation. There’s one member of the structure, which is thetaisa
struct NSObject_IMPL {
Class isa;
};
Copy the code
To sum up, we can conclude that the NSObject class is essentially a struct implementation with an ISA pointer in it, and Pointers are eight bytes in a 64-bit environment.
- We can also use some of the functions provided by the system to see how much memory is being used.
#import <objc/runtime.h> #import <malloc/malloc.h> int main(int argc, const char * argv[]) { @autoreleasepool { NSObject *obj = [[NSObject alloc] init]; NSLog(@"%zd", class_getInstanceSize([NSObject class])); NSLog(@"%zd", malloc_size((__bridge const void *)obj)); NSLog(@"%zd", malloc_size((__bridge const void *)obj)); // 16 } return 0; }Copy the code
Use the class_getInstanceSize(Class CLS) function to see how much memory is used by a member variable in a Class. Note: This returns the memory size of the aligned member variable
We already know from above that there is only one ISA pointer in the NSObject class, so the print shows that instances in the NSObject class take up 8 bytes.
Extern size_t malloc_size(const void * PTR); This function, which passes a pointer, returns the size of the memory used by the object.
It can be found that the system isNSObject
The object is allocated 16 bytes of storage, the first 8 bytes of which hold isa Pointers.
Objc4-781 can be explored from the source code:
- find
class_getInstanceSize
Function :(take the NSObject class as an example)- call
alignedInstanceSize
function - call
word_align
Function, passed inunalignedInstanceSize()
, instance objectsize
Is 8. word_align
The result is 8.
- call
// objc-class.mm file size_t class_getInstanceSize(class CLS) {if (! cls) return 0; return cls->alignedInstanceSize(); } // objc-runtime-new.h // Class's ivar size rounded up to a pointer-size boundary. uint32_t alignedInstanceSize() const { return word_align(unalignedInstanceSize()); } uint32_t unalignedInstanceSize() const { ASSERT(isRealized()); return data()->ro()->instanceSize; } // objc-os.h # define WORD_MASK 7UL static inline size_t word_align(size_t x) { return (x + WORD_MASK) & ~WORD_MASK; }Copy the code
- explore
alloc
Function: callalloc
Function forNSObject
Object allocation creates storage space
// NSObject.mm
+ (id)alloc {
return _objc_rootAlloc(self);
}
// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
// Call [cls alloc] or [cls allocWithZone:nil], with appropriate
// shortcutting optimizations.
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
if (slowpath(checkNil && !cls)) return nil;
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// No shortcuts available.
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
// objc-runtime-new.mm
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
// objc-runtime-new.mm
static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
ASSERT(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size;
size = cls->instanceSize(extraBytes);
...
}
// objc-runtime-new.h
size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
Copy the code
3. Classification of OC objects
OC objects can be divided into three main categories:
instance
Object (Instance object)class
Object (Class object)meta-class
Object (metaclass object)
Instance object (instance object)
In exploring the nature of NSObject objects, we’re exploring instance objects. Instance objects are created by class instantiation, and each call to alloc creates a new object.
Instance objects store information in memory including isa Pointers and other member variables. Does not include any methods. That is, the method’s memory is not stored in the object’s memory.
Class object (Class object)
- A class object is the class of an instance object. Why is it also an object, because there is one in a class object
isa
Pointer to the
NSObject *obj1 = [[NSObject alloc] init]; NSObject *obj2 = [[NSObject alloc] init]; Class objectClass1 = [obj1 Class]; Class objectClass2 = [obj2 class]; // Class objectClass3 = object_getClass(obj1); Class objectClass4 = object_getClass(obj2); Class objectClass5 = [NSObject class]; NSLog(@"%p %p %p %p %p", objectClass1, objectClass2, objectClass3, objectClass4, objectClass5); // 0x7fff8d86e118 0x7fff8d86e118 0x7fff8d86e118 0x7fff8d86e118 0x7fff8d86e118Copy the code
Class objects can be obtained through the object’s class method, the class method of the class, and the object_getClass function in the Runtime. In the above code, we find that the addresses of the five class objects we get are the same. This means that each class has only one class object in memory.
- Class objects store information in memory mainly including
- Isa pointer
- Superclass pointer
- Property information of a class, object method information of a class
- Class protocol information, class member variable information, note not the value of the member variable
- .
Meta – class object
How do I get a metaclass object? To get a metaclass object, you can only get it using the object_getClass function, passing in the class object as an argument.
Class objectMetaClass = object_getClass([NSObject class]);
Copy the code
How do we prove that we are getting a metaclass object? The runtime provides the corresponding API
NSLog(@"%d", c b
Copy the code
Each class has only one meta-class object in memory.
Meta-class objects have the same memory structure as class objects, but their purpose is different. In other words, they store different things. Meta-class object storage includes the following information:
- Isa pointer
- Superclass pointer
- Class method information for a class
- .
That is, the meta-class object also contains the class attribute information in its memory structure, but the contents are null
Note: [[NSObject class] class]; The object is not a metaclass object but a class object, which can be determined by class_isMetaClass(), which returns NO.
You can also print [[NSObject class] class]; The returned object address and [NSObject Class]; Return the address, you can see that they are the same.
4. The direction of ISA
The direction of isa is very convoluted in words, and there isa very classic picture as follows:
- Dotted line represents the
isa
The solid line representssuperclass
The point to superclass
Needless to say, this pointer points to its parent class- If it’s an instance object
isa
Refers to its class object, its class objectisa
Points to its metaclass object
Note: The image above is not an accurate representation at this point. As Apple continues to optimize, Isa no longer refers directly to a class object/metaclass object, but instead finds a class object/metaclass object through the value of &mask, which Apple optimized using the concepts of union and bitfield. But the picture above can help us better understand where ISA is pointing.
The KVO technology takes advantage of the dynamic nature of OC to dynamically add classes and modify the isa pointing at run time by knowing what isa pointing is.