美文网首页
iOS高性能OC四:Runtime Object

iOS高性能OC四:Runtime Object

作者: Trigger_o | 来源:发表于2020-08-17 18:27 被阅读0次

    1.类和对象
    首先进到runtime.h中

    typedef struct objc_class *Class;
    
    /// Represents an instance of a class.
    struct objc_object {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    };
    
    /// A pointer to an instance of a class.
    typedef struct objc_object *id;
    
    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;
    

    可以看到,#if !OBJC2以及很多OBJC2_UNAVAILABLE,说明这些在OC2.0中以及废弃了,真正的实现要看opensource或者下载源码

    下载源码进入project headers里面的objc-runtime-new.h中,找到objc_object

    struct objc_class;
    struct objc_object;
    
    typedef struct objc_class *Class;
    typedef struct objc_object *id;
    
    
    union isa_t 
    {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;
        uintptr_t bits;
    
    #if SUPPORT_NONPOINTER_ISA
    /*
    ...
    */
    }
    
    struct objc_object {
    private:
        isa_t isa;
    
    public:
    
        // ISA() assumes this is NOT a tagged pointer object
        Class ISA();
    
        // getIsa() allows this to be a tagged pointer object
        Class getIsa();
    
        // initIsa() should be used to init the isa of new objects only.
        // If this object already has an isa, use changeIsa() for correctness.
        // initInstanceIsa(): objects with no custom RR/AWZ
        // initClassIsa(): class objects
        // initProtocolIsa(): protocol objects
        // initIsa(): other objects
        void initIsa(Class cls /*indexed=false*/);
        void initClassIsa(Class cls /*indexed=maybe*/);
        void initProtocolIsa(Class cls /*indexed=maybe*/);
        void initInstanceIsa(Class cls, bool hasCxxDtor);
    
    /*
    ...
    */
    }
    }
    
    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() { 
            return bits.data();
        }
    /*
    ...
    */
    }
    

    可以看到,Class是结构体objc_class,id是结构体objc_object,而objc_class继承自objc_object,
    也就是说Class本身就是一个对象,对象都会有一个isa_t isa.

    • 元类
      对象的方法并不是存储于对象的结构体中的,因为每一个对象都存一份这个开销是很大的.当实例方法被调用时,它会通过自己的 isa 来查找自己的类,然后在类的 class_data_bits_t 结构体中查找方法的实现,每一个对象有一个指向自己的父类的指针 super_class 用来查找父类的方法.
      由于类Class本身也是对象,因此也有一个isa,类的isa指向元类metaClass,类就是其元类的对象,实例方法调用时,通过对象的 isa 在类中获取方法的实现;类方法调用时,通过类的 isa 在元类中获取方法的实现.


      对象,类与元类

      图中,实线是父类superclass,虚线是isa,对象的isa指向类对象,类对象的isa指向元类,元类的isa指向基类NSObject.NSObject的元类的isa指向自己,并且NSObject的元类的父类指向自己的类对象,所以当NSObject类调用自己的实例方法时,是可以成功的.同理,任何一个类都可以通过类方法调用NSObject里的实例方法.
      每个类只有一个类对象.

    • block的内存结构
      在main.m写一个block

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            void (^block)(void) = ^ {
                NSLog(@"Hello World!");
            };
            block();
        }
        return 0;
    }
    

    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
    查看main.cpp,找到main函数

    struct __block_impl {
        void *isa;
        int Flags;
        int Reserved;
        void *FuncPtr;
    };
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
          NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_0xn052bn6dgb9z7pfk8bbg740000gn_T_main_88f00d_mi_0);
    }
    
    static struct __main_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    
    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
            void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
            ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
        }
        return 0;
    }
    

    block调用了一个结构体,__main_block_impl_0,传入两个参数, __main_block_func_0即函数,__main_block_desc_0_DATA是结构体的地址.
    可以看到__main_block_impl_0也有isa,可以理解为block也是对象,是id类型,在这里isa是_NSConcreteStackBlock,栈类型,__main_block_impl_0的isa有三种类型,
    &_NSConcreteStackBlock,&_NSConcreteMallocBlock和&_NSConcreteGlobalBlock;栈堆和全局.
    不过在clang出的文件中看到的isa都是_NSConcreteStackBlock,真正的分布需要在运行时通过lldb查看.
    在OC方法中定义block,默认会被copy到堆中,如果是内存修饰copy的property定义block,自然也是在堆中,当block作为函数参数时,如果不进行类型转换,是会默认分配在栈中的,其内存在后续的执行中很可能会被覆盖;定义全局位置的block则是全局类型,分配在全局内存,因为是全局位置因此不会捕获局部变量.
    block声明的时候保存了__main_block_impl_0地址,__main_block_impl_0保存了函数体以及block的类型和blcok的结构体大小,当block回调的时候就是调用了函数__main_block_func_0.


    blcok的结构

    相关文章

      网友评论

          本文标题:iOS高性能OC四:Runtime Object

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