美文网首页
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