美文网首页
OC底层-类和对象

OC底层-类和对象

作者: 阿丶伦 | 来源:发表于2021-05-20 10:04 被阅读0次

    类和对象

    OC中的类

    OC中.类基于C/C++的结构体.

    image-20210511155149831.png image-20210511155751921.png

    通过查看NSObject的类定义,可以看到内部有一个Class isa的成员变量. 从Apple开放的objc源码来看,可以发现,Class类型是一个结构体指针

    继续往下找.objc_class的内部实现,可以发现objc_class是继承于objc_object的结构体.而objc_object内部.只存放了isa变量.

    image-20210511155853395.png
    image-20210511160003551.png

    isa

    这里需要知道.64位系统前.isa是个普通指针.存放着类对象和元类对象地址值

    64位系统中,isa是一个union(共用体).这里涉及到共用内存以及位域的概念.
    对于共用体,个人理解不深...就不乱分析误导别人了.相关的内容,依据我个人的理解.我在对应成员后都加上注释了.直接获取isa的地址.是拿不到正确地址的.需要与 ISA_MASK做位运算&才能得到真正的isa地址.

    union isa_t 
    {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;
        uintptr_t bits;
    # if __arm64__
        struct {
            uintptr_t nonpointer        : 1;        /*nonpointer 值为0代表普通指针.
                                                                                        存储Class, Meta-Class对象的内存地址
                                                                                        1代表优化过的union*/
            uintptr_t has_assoc         : 1;    // 是否有关联对象,影响释放效率
            uintptr_t has_cxx_dtor      : 1;    // 是否有析构函数,影响释放想绿
            uintptr_t shiftcls          : 33;   /* 存放地址值.存放着Class,Meta-Class的地址
                 &ISA_MASK就是取的这个成员 根据与运算的规则. 类对象地址最后三位都是0*/
            uintptr_t magic             : 6;    // 分辨对象是否完场初始化
            uintptr_t weakly_referenced : 1;    // 是否有弱引用
            uintptr_t deallocating      : 1;    // 是否正在释放
            uintptr_t has_sidetable_rc  : 1;    // 引用计数是否过大,会指向引用表
            uintptr_t extra_rc          : 19;   // 引用计数
        };
     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;
    }
    

    class_rw_t

    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;
    
    private:
        using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t, class_rw_ext_t, PTRAUTH_STR("class_ro_t"), PTRAUTH_STR("class_rw_ext_t")>;
    
        const ro_or_rw_ext_t get_ro_or_rwe() const {
            return ro_or_rw_ext_t{ro_or_rw_ext};
        }
    
        void set_ro_or_rwe(const class_ro_t *ro) {
            ro_or_rw_ext_t{ro, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_relaxed);
        }
    
        void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
            // the release barrier is so that the class_rw_ext_t::ro initialization
            // is visible to lockless readers
            rwe->ro = ro;
            ro_or_rw_ext_t{rwe, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_release);
        }
    
        class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);
    
    public:
        void setFlags(uint32_t set)
        {
            __c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED);
        }
    
        void clearFlags(uint32_t clear) 
        {
            __c11_atomic_fetch_and((_Atomic(uint32_t) *)&flags, ~clear, __ATOMIC_RELAXED);
        }
    
        // set and clear must not overlap
        void changeFlags(uint32_t set, uint32_t clear) 
        {
            ASSERT((set & clear) == 0);
    
            uint32_t oldf, newf;
            do {
                oldf = flags;
                newf = (oldf | set) & ~clear;
            } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
        }
    
        class_rw_ext_t *ext() const {
            return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
        }
    
        class_rw_ext_t *extAllocIfNeeded() {
            auto v = get_ro_or_rwe();
            if (fastpath(v.is<class_rw_ext_t *>())) {
                return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
            } else {
                return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
            }
        }
    
        class_rw_ext_t *deepCopy(const class_ro_t *ro) {
            return extAlloc(ro, true);
        }
    
        const class_ro_t *ro() const {
            auto v = get_ro_or_rwe();
            if (slowpath(v.is<class_rw_ext_t *>())) {
                return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
            }
            return v.get<const class_ro_t *>(&ro_or_rw_ext);
        }
    
        void set_ro(const class_ro_t *ro) {
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro;
            } else {
                set_ro_or_rwe(ro);
            }
        }
    
        const method_array_t methods() const {
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
            } else {
                return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
            }
        }
    
        const property_array_t properties() const {
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
            } else {
                return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
            }
        }
    
        const protocol_array_t protocols() const {
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
            } else {
                return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
            }
        }
    };
    
    struct class_ro_t {
        void *baseMethodList;
        protocol_list_t * baseProtocols;
        const ivar_list_t * ivars;
        const uint8_t * weakIvarLayout;
        property_list_t *baseProperties;
     }
    
    • class_ro_t (里面存放的都是一维数组.只读.包含类初始内容)
      • method_list_t * baseMethodList;
      • protocol_list_t * baseProtocols;
      • const ivar_list_t * ivars;
      • property_list_t *baseProperties;
    • method_array_t methods;
    • property_array_t properties;
    • protocol_array_t protocols;

    class_rw_t中.存放了类的 成员变量, 协议, 属性以及方法. 其中.class_ro_t中存放的是类的初始内容.这些内容不可被更改.而 methods, properties, protocols都是二维数组,可读可写.包含类的初始,以及分类的内容.

    之所以设置成二维数组,是为了实现动态添加相应的功能.

    本类的方法一开始都放在class_ro_t中,当运行程序时,分类中的数据与class_ro_t 的数据会合并放到class_rw_t中.

    关于分类的详细.由于篇幅过长.后续会在其他的文章进行分析.

    关于method_t.应该会在消息发送机制(objc_msgsend)里面进行补充

    总结

    OC中的类.其实是一个结构体指针.指向的结构体类型为 objc_class.其内部存放了isa. 以及类成员变量, 协议, 属性以及方法.

    OC中的对象

    实例对象(Instace)

    实例对象,顾名思义就是类的实例对象.当一个类的实例对象在堆中alloc时.内存中存放的是实例对象的isa以及成员变量.至于方法.是通过isa找到对应类对象.再寻找对应的方法进行调用

    类对象(Class)

    OC的类对象,其实我们上面已经讲过了.类对象存放了isa.成员变量列表.属性列表.方法列表.协议列表.但是值得注意的一点是,类对象中存放的方法为实例方法

    元类对象(Metaclass)

    元类对象.是类对象的元类对象.元类对象的结构与类对象基本一致.但是存放的是类的类方法

    为什么说类对象存放的是实例方法.元类对象存放的是类方法.我们通过把OC代码编译成c++可以得到答案.

    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc xxx.m -o xxx.cpp
    

    从编译得到的C++代码中可以看到. OC调用方法,其实是调用的 objc_msgSend().

    image-20210512144339503.png
    image-20210512145030282.png

    看看objc_msgSend的部分代码,不难看出.调用方法时.会先判断传入的是否为nil,如果不是.
    找到传入对象的isa.然后找到对应的类中寻找方法.

    isa指向

    image-20210512142915984.png
    对象的isa ===> 类

    类的isa ===> 元类(metaClass)

    元类(metaClass)的isa ===>父类 ===> RootMetaClass(根元类)

    RootMetaClass(根元类)的isa ===> RootMetaClass(根元类)

    类对象的superClass ===> 父类 ===> rootClass(根类) ===> nil

    元类的superClass ==> 父元类 ===> metaRootClass (根元类) ===> rootClass(根类) ===> nil

    方法调用顺序(不涉及消息转发)

    • 调用实例对象方法时

      • 通过isa找到对象的类,然后查找类中的方法列表,

      • 如果找不到,通过superClass找到父类

      • 最终到根类.如果都找不到.程序崩溃(unrecognize selector)

    • 调用类方法时

      • 通过isa找到元类,查找类方法列表

      • 层层向上.到根元类.

      • 最终到根类.根元类isa指向根类.都找不到.崩溃

    思考题:上述代码会不会崩溃

    @interface Test : NSObject
    + (void)test;
    @end
    
    @implementation Test
    @end
    
    @interface NSObject (Test)
    - (void)test;
    @end
    
    @implementation NSObject (Test)
    - (void)test {
        NSLog(@"%s",__func__);
    }
    @end
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            [Test test];
        }
        return 0;
    }
    

    相关文章

      网友评论

          本文标题:OC底层-类和对象

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