美文网首页
iOS-OC底层03:isa是怎么关联类的

iOS-OC底层03:isa是怎么关联类的

作者: MonKey_Money | 来源:发表于2020-09-10 23:09 被阅读0次

    前沿

    经过看apple的开源源码,了解到共同体(isa_t)和位域(ISA_BITFIELD)在底层起到至关重要的作用。 首先先对用到的位域和共同体简单回顾。

    共同体

    共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。

    union Data
    {
       int I;
       float f;
       char  str[20];
    };
     
    int main( )
    {
       union Data data;        
     
       data.i = 10;
       data.f = 220.5;
       strcpy( data.str, "C Programming");
     
       printf( "data.i : %d\n", data.i);
       printf( "data.f : %f\n", data.f);
       printf( "data.str : %s\n", data.str);
     
       return 0;
    }
    输入结果
    data.i : 1917853763
    data.f : 4122360580327794860452759994368.000000
    data.str : C Programming
    

    在这里,我们可以看到共用体的 i 和 f 成员的值有损坏,因为最后赋给变量的值占用了内存位置,这也是 str 成员能够完好输出的原因。
    我们做一下调整

    int main( )
    {
       union Data data;        
     
       data.i = 10;
       printf( "data.i : %d\n", data.i);
       
       data.f = 220.5;
       printf( "data.f : %f\n", data.f);
       
       strcpy( data.str, "C Programming");
       printf( "data.str : %s\n", data.str);
     
       return 0;
    }
    输入结果
    data.i : 10
    data.f : 220.500000
    data.str : C Programming
    

    在这里,所有的成员都能完好输出,因为同一时间只用到一个成员。

    位域

    struct
    {
      unsigned int widthValidated : 1;
      unsigned int heightValidated : 1;
    } status;
    

    上面的结构中,status 变量将占用 4 个字节的内存空间,但是只有 2 位被用来存储值。如果您用了 32 个变量,每一个变量宽度为 1 位,那么 status 结构将使用 4 个字节,但只要您再多用一个变量,如果使用了 33 个变量,那么它将分配内存的下一段来存储第 33 个变量,这个时候就开始使用 8 个字节。

    关联类

    如想了解alloc的前期,请参考
    obj->initInstanceIsa(cls, hasCxxDtor);===》initIsa(cls, true, hasCxxDtor);

    我们先了解一下对象的成员isa类型

    union isa_t {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;
        uintptr_t bits;
    #if defined(ISA_BITFIELD)
        struct {
            ISA_BITFIELD;  // defined in isa.h
        };
    #endif
    };
    
    #   define ISA_BITFIELD                         \
         uintptr_t nonpointer        : 1;                                         \
          uintptr_t has_assoc         : 1;                                         \
          uintptr_t has_cxx_dtor      : 1;                                         \
          uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
          uintptr_t magic             : 6;                                         \
          uintptr_t weakly_referenced : 1;                                         \
          uintptr_t deallocating      : 1;                                         \
          uintptr_t has_sidetable_rc  : 1;                                         \
          uintptr_t extra_rc          : 8
    

    1.nonpointer:表示是否对 isa 指针开启指针优化 0:纯isa指针,1:不止是类对象地址,isa 中包含了类信息、对象的引用计数等
    2.has_assoc:关联对象标志位,0没有,1存在
    3.has_cxx_dtor:该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑, 如果没有,则可以更快的释放对象
    4.shiftcls:存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位用来存储类指针。
    5.magic:用于调试器判断当前对象是真的对象还是没有初始化的空间
    6.weakly_referenced:志对象是否被指向或者曾经指向一个 ARC 的弱变量,
    没有弱引用的对象可以更快释放。和dealloc方法有关系
    7.deallocating:标志对象是否正在释放内存
    8.has_sidetable_rc:当对象引用技术大于 10 时,则需要借用该变量存储进位
    9.extra_rc:当表示该对象的引用计数值,实际上是引用计数值减 1, 例如,如果对象的引用计数为 10,那么 extra_rc 为 9。如果引用计数大于 10, 则需要使用到下面的 has_sidetable_rc。
    我们来分析一下initIsa具体实现

    inline void 
    objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
    { 
        ASSERT(!isTaggedPointer()); 
        
        if (!nonpointer) {
            isa = isa_t((uintptr_t)cls);
        } else {
            ASSERT(!DisableNonpointerIsa);
            ASSERT(!cls->instancesRequireRawIsa());
    
            isa_t newisa(0);
    #if SUPPORT_INDEXED_ISA
            ASSERT(cls->classArrayIndex() > 0);
            newisa.bits = ISA_INDEX_MAGIC_VALUE;
            // isa.magic is part of ISA_MAGIC_VALUE
            // isa.nonpointer is part of ISA_MAGIC_VALUE
            newisa.has_cxx_dtor = hasCxxDtor;
            newisa.indexcls = (uintptr_t)cls->classArrayIndex();
    #else
            newisa.bits = ISA_MAGIC_VALUE; //给共同体设置默认值
            // isa.magic is part of ISA_MAGIC_VALUE
            // isa.nonpointer is part of ISA_MAGIC_VALUE
            newisa.has_cxx_dtor = hasCxxDtor;
    //设置类,因为cls的类型也是为isa_t,去掉前面三位,设置值时,后面的会没地方存储会自动抹掉。
            newisa.shiftcls = (uintptr_t)cls >> 3; 
    #endif
            // This write must be performed in a single store in some cases
            // (for example when realizing a class because other threads
            // may simultaneously try to use the class).
            // fixme use atomics here to guarantee single-store and to
            // guarantee memory order w.r.t. the class index table
            // ...but not too atomic because we don't want to hurt instantiation
            isa = newisa;
        }
    }
    

    验证


    image.png

    我们操作的思想如下图


    123.png

    lldb命令

    p:打印
    po:打印描述,如果在类中重新description,那po的打印结果就是description中的返回字符串
    memory read p:p为某个对象,含义就是打印p对象的内存情况
    x p:也是打印p对象的内存情况
    x/4gx p:也是打印p对象的内存情况后面的4gx为打印格式,一段8字节4段打印p对象的内存
    x/16gw objc2:打印objc2的内存情况,打印格式 是4字节一组打印16组数据

    计算大小

    sizeof(p),因为p为对象的指针,指针为8字节,所以返回是8
    sizeof(int),我们知道int是4个字节,所以返回是4字节
    如果我们想知道某个对象的占有的内存是多少怎么办呢?下面有两个方法
    class_getInstanceSize([LGPerson class]) 和malloc_size((__bridge const void *)(person)),
    class_getInstanceSize为实力的内存大小,8字节对齐
    malloc_size为系统分配的内存大小,因为需要16字节对齐,所以分配的内存要大于等于所占用的内存

    Clang

    clang -rewrite-objc main.m -o main.cpp 把目标文件编译成c++文件
    UIKit报错问题
    clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot / Applications/Xcode.app/Contents/Developer/Platforms/ iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m
    xcode安装的时候顺带安装了xcrun命令,xcrun命令在clang的基础上进行了 一些封装,要更好用一些
    xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp (模拟器)
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main- arm64.cpp (手机)

    分析对象

    @interface LGPerson : NSObject
    @property (nonatomic, copy) NSString *name;
    @end
    
    @implementation LGPerson
    @end
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
            NSLog(@"Hello, World!");
        }
        return 0;
    }
    
    

    通过
    clang -rewrite-objc main.m -o main.cpp 后的主要代码

    struct LGPerson_IMPL {
        struct NSObject_IMPL NSObject_IVARS;
        NSString *_name;
    };
    // @property (nonatomic, copy) NSString *name;
    /* @end */
    // @implementation LGPerson
    static NSString * _I_LGPerson_name(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)); }
    extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
    static void _I_LGPerson_setName_(LGPerson * self, SEL _cmd, NSString *name) {
        objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _name), (id)name, 0, 1);
    }
    // @end
    

    我们可以看出NSObject_IVARS就是我们研究的isa,_I_LGPerson_name和I_LGPerson_setName就是get和set方法
    我们可以看出set的方法调用的本质就是objc_setProperty

    setMethod.png

    补充内容dealloc的过程

    _objc_rootDealloc->obj->rootDealloc();->rootDealloc()

    objc_object::rootDealloc()
    {
        if (isTaggedPointer()) return;  // fixme necessary?
    
    //如果没有开启指针优化,没有弱引用,没有关联,没有cxx,没有映射表直接释放
        if (fastpath(isa.nonpointer  &&  
                     !isa.weakly_referenced  &&  
                     !isa.has_assoc  &&  
                     !isa.has_cxx_dtor  &&  
                     !isa.has_sidetable_rc))
        {
            assert(!sidetable_present());
            free(this);
        } 
        else {
            object_dispose((id)this);
        }
    }
    id 
    object_dispose(id obj)
    {
        if (!obj) return nil;
    
        objc_destructInstance(obj);    
        free(obj);
    
        return nil;
    }
    void *objc_destructInstance(id obj) 
    {
        if (obj) {
            // Read all of the flags at once for performance.
            bool cxx = obj->hasCxxDtor();
            bool assoc = obj->hasAssociatedObjects();
    
            // This order is important.
            if (cxx) object_cxxDestruct(obj);
            if (assoc) _object_remove_assocations(obj);
            obj->clearDeallocating();
        }
    
        return obj;
    }
    

    相关文章

      网友评论

          本文标题:iOS-OC底层03:isa是怎么关联类的

          本文链接:https://www.haomeiwen.com/subject/gzrpektx.html