in iOS ~ read.

Objective-C - a NSObject

Mitchell

Convert OC to C++
  • Transformation way: We can assign the platform to iphoneos with arm64 architecture, while it will output with multi platform architecture automatically.
//It will automatically generate main.cpp file
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m  
//Assign your output file name
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o fileName.cpp  
//if your have code contain __weak, you may have the problem like cannot create __weak reference because the current deployment target does not support weak references
//To solve the problem you can do as followed:
clang -rewrite-objc -fobjc-arc -stdlib=libc++ -mmacosx-version-min=10.7 -fobjc-runtime=macosx-10.7 -Wno-deprecated-declarations file.m  

The Origin of a simple instance of NSObject
  • View the NSObject's structure
//C++ code help us to generate a NSObject_IMPL structure
struct NSObject_IMPL {  
    Class isa;
};

The origin of Class
  • Class is a pointer of structure,which will occupy 8 bytes with 64bit architecture system.
typedef struct objc_class *Class;  
struct objc_class {  
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;

四、How much a NSObject instance size is ?
  • We can use method class_getInstanceSize and malloc_size to check how much a NSObject instance size is respectively.
// output:8 bytes
NSLog(@"%zd",class_getInstanceSize([NSObject class]));  
// output:16 bytes
NSLog(@"%zd",malloc_size((__bridge const void*)obj));  
  • We can view the source code to identify the difference between them
1、We can see the call stack of class_getInstanceSize,  
class_getInstanceSize -> alignedInstanceSize -> word_align, we can see that the value of class_getInstanceSize method returned is class's ivars size.  
size_t class_getInstanceSize(Class cls)  
{
    if (!cls) return 0;
    return cls->alignedInstanceSize();
}
uint32_t alignedInstanceSize() {  
    return word_align(unalignedInstanceSize());
}
//2.The value of malloc_size method return is the pointer's size which the method receives.
extern size_t malloc_size(const void *ptr);  
    /* Returns size of given ptr */
  • Conclusion:
//1. class_getInstanceSize return the size of a Class's ivars occupy and it's output is 8 bytes. 
NSLog(@"%zd",class_getInstanceSize([NSObject class]));  
//2.malloc_size method return the size of object pointer's size in the memory and it output is 16 bytes.
NSLog(@"%zd",malloc_size((__bridge const void*)obj));  

五、Why 16 bytes?
  • We can see the source code of alloc method's call stack
//1.alloc 
+ (id)alloc {
    return _objc_rootAlloc(self);
}
//2._objc_rootAlloc
id  
_objc_rootAlloc(Class cls)  
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
//3.callAlloc
static ALWAYS_INLINE id  
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)  
{
    if (slowpath(checkNil && !cls)) return nil;

#if __OBJC2__
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        // No alloc/allocWithZone implementation. Go straight to the allocator.
        // fixme store hasCustomAWZ in the non-meta class and 
        // add it to canAllocFast's summary
        if (fastpath(cls->canAllocFast())) {
            // No ctors, raw isa, etc. Go straight to the metal.
            bool dtor = cls->hasCxxDtor();
            id obj = (id)calloc(1, cls->bits.fastInstanceSize());
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            obj->initInstanceIsa(cls, dtor);
            return obj;
        }
        else {
            // Has ctor or raw isa or something. Use the slower path.
            id obj = class_createInstance(cls, 0);
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            return obj;
        }
    }
#endif

    // No shortcuts available.
    if (allocWithZone) return [cls allocWithZone:nil];
    return [cls alloc];
}
//4.class_createInstance 
id  
class_createInstance(Class cls, size_t extraBytes)  
{
    return _class_createInstanceFromZone(cls, extraBytes, nil);
}
//5. _class_createInstanceFromZone
static __attribute__((always_inline))  
id  
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,  
                              bool cxxConstruct = true, 
                              size_t *outAllocatedSize = nil)
{
    if (!cls) return nil;
    assert(cls->isRealized());
    // Read class's info bits all at once for performance
    bool hasCxxCtor = cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();
   //Get the instance size
    size_t size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;
    id obj;
    if (!zone  &&  fast) {
        obj = (id)calloc(1, size);
        if (!obj) return nil;
        obj->initInstanceIsa(cls, hasCxxDtor);
    } 
    else {
        if (zone) {
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;
        // Use raw pointer isa on the assumption that they might be 
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }
    if (cxxConstruct && hasCxxCtor) {
        obj = _objc_constructOrFree(obj, cls);
    }
    return obj;
}
//6.The main method instanceSize
size_t instanceSize(size_t extraBytes) {  
    size_t size = alignedInstanceSize() + extraBytes;
    // CF requires all objects be at least 16 bytes.
   if (size < 16) size = 16;
   return size;
}
  • Via seeing the source code, when we input value extraBytes is less than 16 bytes, it will return 16 bytes.
  • When we create a NSObject instance, the system will distribute 16 bytes, we can use malloc_size to view it's total size and you can use class_getInstanceSize check it's ivars' total size(a simple NSObject object will occupy 8 bytes which it's isa pointer's size).

How can we read and write the memory with Xcode
  • lldb inherit gdb's x command, we can use x command to read and write memory address directly.
  • Read
1.Debug => Debug WorkFlow => View Memory to check the address's content of the assembly.  
2.lldb: memory read + memory address  
3、x + memory address  
4、x/number、style、bytes count + memory address  
example:x/3xg address (3 means 3 lines, x means hexadecimal and g means 8 bytes)  
It's output is a three lines 8 bytes string of hexadecimal.  
  • Write
memory read + memory address  

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2mln51y7cr0go

comments powered by Disqus