36选7玩法 >iOS开发

Runtime源码阅读分享之对象的本质,了解isa

2018-04-26 09:39 编辑: yyuuzhu 分类:iOS开发 来源:shenglanya

时时彩玩法:引言

36选7玩法 www.mnaki.com 我们都知道,Runtime 是 Objective-C 这门动态语言的核心,只有理解了它,我们才能够更好的理解 Objective-C 到底是如何工作的,在编程时,也会更加得心应手。由于时间和精力有限,此次我主要想从以下几方面来进行 Runtime 源码的阅读,日后将会逐步完善。由于总体篇幅较长,所以我将会每一部分拆分成一篇文章来具体分析。

目录

一、对象的本质,了解 isa

二、对象的生命周期

三、对象的引用计数

四、对象的扩展方法

五、Runtime 的应用

对象的本质,了解 isa

首先来看一看对象和类的定义

///?Represents?an?instance?of?a?class.?struct?objc_object?{?private:?isa_t?isa;?public:?//?ISA()?assumes?this?is?NOT?a?tagged?pointer?object?Class?ISA();
};?//?其内部有一个指向?Class?的指针,而?Class?是什么呢?//?An?opaque?type?that?represents?an?Objective-C?class.?typedef?struct?objc_class?*Class;?//?再来看一下??objc_class?的定义,需要注意的是,objc_class?也是继承自?objc_object,?由于?objc_object?中已经定义了一个?isa?指针,由于结构体中所有的成员都是?public?的,所以?objc_class?也就拥有了?isa?并且也拥有访问?isa?的权限。?struct?objc_class?:?objc_object?{?//?Class?ISA;???//?此时,类中的?isa?指针指向的是?metaClass?元类?Class?superclass;?//?父类?cache_t?cache;?//?formerly?cache?pointer?and?vtable?类的方法缓存,因为?Runtime?时会把第一次遇到的方法缓存到方法缓存中,此后将直接从缓存中读取方法,极大提高了效率?class_data_bits_t?bits;?//?class_rw_t?*?plus?custom?rr/alloc?flags?//??class_data_bits_t??class_rw_t?+?一个?rr/alloc?位?};

所以到这里,我们也就理解到了,实际上,类也是一个对象

  • 我们需要知道的是,在 Objective-C 中,对象的方法并没有存储于对象的结构体中(如果每一个对象都保存了自己能执行的方法,那么对内存的占用有极大的影响)。在调用实例方法时,而是通过 isa 指针来寻找相应的类,通过 class_data_bits_t 来寻找类中的方法。具体是如何寻找的,我们看

    //?首先?class_data_bits_t?中有一个?bits?位?struct?class_data_bits_t?{?//?Values?are?the?FAST_?flags?above.?uintptr_t?bits;?public:?//?这里返回的数据是?class_rw_t*?指针类型的数据,在这个方法中我们可以看出,将?bits?与?FAST_DATA_MASK?进行位运算,只取其中的?[3,?47]?位转换成?class_rw_t?*?返回。?class_rw_t*?data()?{?return?(class_rw_t?*)(bits?&?FAST_DATA_MASK);
    ????}
    }?//?data()?返回的是一个?class_rw_t*?指针,?class_rw_t?又是什么??//?类中的属性、方法还有遵循的协议等信息都保存在?class_rw_t?中:?struct?class_rw_t?{?//?Be?warned?that?Symbolication?knows?the?layout?of?this?structure.?uint32_t?flags;?uint32_t?version;?const?class_ro_t?*ro;?method_array_t?methods;?property_array_t?properties;?protocol_array_t?protocols;
    
    ????Class?firstSubclass;
    ????Class?nextSiblingClass;?char?*demangledName;?#if?SUPPORT_INDEXED_ISA?uint32_t?index;?#endif?};?//?由此我们可以看出,?class_rw_t?中包含了一些关于类的信息,比如?flag,?版本号,?方法数组,?属性数组等。而其中又有一个指向?class_ro_t?的指针,?class_ro_t?又是什么??//?原来,class_ro_t?中存储了当前类在编译期就已经确定的属性、方法以及遵循的协议。?struct?class_ro_t?{?uint32_t?flags;?uint32_t?instanceStart;?uint32_t?instanceSize;?#ifdef?__LP64__?uint32_t?reserved;?#endif?const?uint8_t?*?ivarLayout;?const?char?*?name;?method_list_t?*?baseMethodList;?protocol_list_t?*?baseProtocols;?const?ivar_list_t?*?ivars;?const?uint8_t?*?weakIvarLayout;?property_list_t?*baseProperties;?method_list_t?*baseMethods()?const?{?return?baseMethodList;
    ????}
    };
  • 所以由此,我们知道 class_ro_t 保存的是在编译期时就已经确定的方法,所以当在编译期时, class_data_bits_t 将直接指向 class_ro_t ,而后在 Runtime 时,将会调用 class_data_bits_t 的 data() 直接将结果从 class_rw_t 转化成 class_ro_t 指针, 然后再初始化一个 class_rw_t 指针,此时它中的数据都为空,然后再设置它的 ro 变量和 flag, 最后再为其设置正确的 data

    /***********************************************************************
    *?realizeClass
    *?Performs?first-time?initialization?on?class?cls,?
    *?including?allocating?its?read-write?data.
    *?Returns?the?real?class?structure?for?the?class.?
    *?Locking:?runtimeLock?must?be?write-locked?by?the?caller
    **********************************************************************/?static?Class?realizeClass(Class?cls)?{
    ????runtimeLock.assertWriting();?const?class_ro_t?*ro;?class_rw_t?*rw;
    ????Class?supercls;
    ????Class?metacls;?bool?isMeta;?if?(!cls)?return?nil;?if?(cls->isRealized())?return?cls;
    ????assert(cls?==?remapClass(cls));?//?fixme?verify?class?is?not?in?an?un-dlopened?part?of?the?shared?cache??//?强制转化为?ro?ro?=?(const?class_ro_t?*)cls->data();?if?(ro->flags?&?RO_FUTURE)?{?//?This?was?a?future?class.?rw?data?is?already?allocated.?rw?=?cls->data();
    ????????ro?=?cls->data()->ro;
    ????????cls->changeInfo(RW_REALIZED|RW_REALIZING,?RW_FUTURE);
    ????}?else?{?//?Normal?class.?Allocate?writeable?class?data.?//?这时候首先给?rw?分配内存空间并且初始化为?0?rw?=?(class_rw_t?*)calloc(sizeof(class_rw_t),?1);?//?使?rw?指向?ro?rw->ro?=?ro;?//?设置?rw?的?flag?为?正在初始化和已经初始化?/**
    ??????//?class?is?realized?-?must?never?be?set?by?compiler
    ??????????#define?RO_REALIZED???????????(1data?is?class_rw_t,?not?class_ro_t
    ??????????#define?RW_REALIZED???????????(1setData(rw);
    ????}?//?判断是否为?metaClass??RO_META?(1?<flags?&?RO_META;
    
    ????rw->version?=?isMeta???7?:?0;?//?old?runtime?went?up?to?6?//?Choose?an?index?for?this?class.?//?Sets?cls->instancesRequireRawIsa?if?indexes?no?more?indexes?are?available?cls->chooseClassArrayIndex();?if?(PrintConnecting)?{
    ??????_objc_inform("CLASS:?realizing?class?'%s'%s?%p?%p?#%u",?
    ???????????????????cls->nameForLogging(),?isMeta???"?(meta)"?:?"",?
    ???????????????????(void*)cls,?ro,?cls->classArrayIndex());
    ??}?//?Realize?superclass?and?metaclass,?if?they?aren't?already.?//?This?needs?to?be?done?after?RW_REALIZED?is?set?above,?for?root?classes.?//?This?needs?to?be?done?after?class?index?is?chosen,?for?root?metaclasses.?supercls?=?realizeClass(remapClass(cls->superclass));
    ??metacls?=?realizeClass(remapClass(cls->ISA()));?#if?SUPPORT_NONPOINTER_ISA?//?Disable?non-pointer?isa?for?some?classes?and/or?platforms.?//?Set?instancesRequireRawIsa.?bool?instancesRequireRawIsa?=?cls->instancesRequireRawIsa();?bool?rawIsaIsInherited?=?false;?static?bool?hackedDispatch?=?false;?if?(DisableNonpointerIsa)?{?//?Non-pointer?isa?disabled?by?environment?or?app?SDK?version?instancesRequireRawIsa?=?true;
    ??}?else?if?(!hackedDispatch??&&??!(ro->flags?&?RO_META)??&&?0?==?strcmp(ro->name,?"OS_object"))?
    ??{?//?hack?for?libdispatch?et?al?-?isa?also?acts?as?vtable?pointer?hackedDispatch?=?true;
    ??????instancesRequireRawIsa?=?true;
    ??}?else?if?(supercls??&&??supercls->superclass??&&??
    ???????????supercls->instancesRequireRawIsa())?
    ??{?//?This?is?also?propagated?by?addSubclass()??//?but?nonpointer?isa?setup?needs?it?earlier.?//?Special?case:?instancesRequireRawIsa?does?not?propagate??//?from?root?class?to?root?metaclass?instancesRequireRawIsa?=?true;
    ??????rawIsaIsInherited?=?true;
    ??}?if?(instancesRequireRawIsa)?{
    ??????cls->setInstancesRequireRawIsa(rawIsaIsInherited);
    ??}?//?SUPPORT_NONPOINTER_ISA?#endif?//?Update?superclass?and?metaclass?in?case?of?remapping?cls->superclass?=?supercls;
    ??cls->initClassIsa(metacls);?//?Reconcile?instance?variable?offsets?/?layout.?//?This?may?reallocate?class_ro_t,?updating?our?ro?variable.?if?(supercls??&&??!isMeta)?reconcileInstanceVariables(cls,?supercls,?ro);?//?Set?fastInstanceSize?if?it?wasn't?set?already.?cls->setInstanceSize(ro->instanceSize);?//?Copy?some?flags?from?ro?to?rw?if?(ro->flags?&?RO_HAS_CXX_STRUCTORS)?{
    ??????cls->setHasCxxDtor();?if?(!?(ro->flags?&?RO_HAS_CXX_DTOR_ONLY))?{
    ??????????cls->setHasCxxCtor();
    ??????}
    ??}?//?Connect?this?class?to?its?superclass's?subclass?lists?if?(supercls)?{
    ??????addSubclass(supercls,?cls);
    ??}?else?{
    ??????addRootClass(cls);
    ??}?//?Attach?categories?//?在这个方法中将?rw?的方法列表,属性列表,协议列表赋值?methodizeClass(cls);?return?cls;
    };

此时,经过 Runtime 的作用之后,现在内存中的关系是,类中的 data 指针指向 class_data_bits_t, class_data_bits_t 结构体中的 data() 方法获取到的是 class_rw_t 指针, class_rw_t 结构体中的 ro 指针指向 class_ro_t。图如下:

图片来自一位大神的博客,侵权删
  • 但是问题来了,类的方法是如何被查找和调用的呢?由于我们已经知道了,在 ObjC 中,实际上类也是一个特殊的对象,查找类的方法实际上就和查找实例方法采用同样的机制,但是如何才能让他们采用同样的机制呢?这时,元类的作用就显现了出来。

    • metaClass 保证了类中也有一个指向 Class 类型的指针,保证了类和对象的一致性,保证了类查找方法的机制与对象查找方法的机制保持同步。

      • 当实例方法调用时,通过对象的isa在类中获取方法的实现

      • 当类方法调用时,通过类的isa在元类中获取方法的实现

现在,我们的重点终于到了,?isa 到底是什么?

  • 我们在 Runtime 的源码中可以看到,在不同的处理器上,这个共同体所分配的内存位数是不同的。

    union?isa_t?{?isa_t()?{?}?isa_t(uintptr_t?value)?:?bits(value)?{?}
    
    ????Class?cls;?uintptr_t?bits;?#if?SUPPORT_PACKED_ISA?//?extra_rc?must?be?the?MSB-most?field?(so?it?matches?carry/overflow?flags)?//?nonpointer?must?be?the?LSB?(fixme?or?get?rid?of?it)?//?shiftcls?must?occupy?the?same?bits?that?a?real?class?pointer?would?//?bits?+?RC_ONE?is?equivalent?to?extra_rc?+?1?//?RC_HALF?is?the?high?bit?of?extra_rc?(i.e.?half?of?its?range)?//?future?expansion:?//?uintptr_t?fast_rr?:?1;?????//?no?r/r?overrides?//?uintptr_t?lock?:?2;????????//?lock?for?atomic?property,?@synch?//?uintptr_t?extraBytes?:?1;??//?allocated?with?extra?bytes?#?if?__arm64__?#?define?ISA_MASK????????0x0000000ffffffff8ULL?#?define?ISA_MAGIC_MASK??0x000003f000000001ULL?#?define?ISA_MAGIC_VALUE?0x000001a000000001ULL?struct?{?uintptr_t?nonpointer????????:?1;?uintptr_t?has_assoc?????????:?1;?uintptr_t?has_cxx_dtor??????:?1;?uintptr_t?shiftcls??????????:?33;?//?MACH_VM_MAX_ADDRESS?0x1000000000?uintptr_t?magic?????????????:?6;?uintptr_t?weakly_referenced?:?1;?uintptr_t?deallocating??????:?1;?uintptr_t?has_sidetable_rc??:?1;?uintptr_t?extra_rc??????????:?19;?#?define?RC_ONE???(1ULL<<45)?#?define?RC_HALF??(1ULL<<18)?};?#?elif?__x86_64__?#?define?ISA_MASK????????0x00007ffffffffff8ULL?#?define?ISA_MAGIC_MASK??0x001f800000000001ULL?#?define?ISA_MAGIC_VALUE?0x001d800000000001ULL?struct?{?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;?#?define?RC_ONE???(1ULL<<56)?#?define?RC_HALF??(1ULL<<7)?};
    };

    以?x86_64_ 为例,

    图片来自一位大神的博客,侵权删
  • 更深一步,从 isa 的初始化来看 isa 的结构

    inline?void?objc_object::initInstanceIsa(Class?cls,?bool?hasCxxDtor)
    {
    ????initIsa(cls,?true,?hasCxxDtor);
    }?inline?void?objc_object::initIsa(Class?cls,?bool?indexed,?bool?hasCxxDtor)?
    {?if?(!indexed)?{
    ????????isa.cls?=?cls;
    ????}?else?{
    ????????isa.bits?=?ISA_MAGIC_VALUE;
    ????????isa.has_cxx_dtor?=?hasCxxDtor;
    ????????isa.shiftcls?=?(uintptr_t)cls?>>?3;
    ????}
    }?//?由于对象的?isa?初始化时传入?indexed?为?true?,所以,可简化为?inline?void?objc_object::initIsa(Class?cls,?bool?indexed,?bool?hasCxxDtor)?
    {?//?其中?ISA_MAGIC_VALUE?为?0x000001a000000001ULL?isa.bits?=?ISA_MAGIC_VALUE;
    ????????isa.has_cxx_dtor?=?hasCxxDtor;
    ????????isa.shiftcls?=?(uintptr_t)cls?>>?3;
    }
    • 此时,当执行完 isa.bits = ISA_MAGIC_VALUE; 后 isa 的结构为 ,可以看到 ISA_MAGIC_VALUE 将 magic 和 indexed 都初始化了

      image
    • 接着 isa.has_cxx_dtor = hasCxxDtor; 这一位会设置 has_cxx_dtor 的值,如果是 1, 则表示当前对象是否有析构器,如果没有,就会快速释放

    • 最后, isa.shiftcls = (uintptr_t)cls >> 3; 将当前对象对应的类指针赋值给 shiftcls 这些位,之所以向右移三位,移三位的主要原因是用于将 Class 指针中无用的后三位清楚减小内存的消耗,因为类的指针要按照字节(8 bits)对齐内存,其指针后三位都是没有意义的 0。赋值之后如下

![image](http://upload-images.jianshu.io/upload_images/3262069-f3a02c34c16975e6?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)?
至此,也就证明了我们之前对于初始化?`isa`?时对?`initIsa`?方法的分析是正确的。它设置了?`indexed`、`magic`?以及?`shiftcls`。

获取 isa

  • 由于我们现在使用了结构体 isa_t 来替代 Class 类型的指针, 所以我们也就需要一个指针能够返回 isa 所指的类,所以我们此时需要一个 ISA() 方法。

    inline?Class?
    objc_object::ISA()?
    {
    ????assert(!isTaggedPointer());?#if?SUPPORT_INDEXED_ISA?if?(isa.nonpointer)?{?uintptr_t?slot?=?isa.indexcls;?return?classForIndex((unsigned)slot);
    ????}?return?(Class)isa.bits;?#else?return?(Class)(isa.bits?&?ISA_MASK);?#endif?}?//?简化后如下?inline?Class?
    objc_object::ISA()?
    {
    ????assert(!isTaggedPointer());?//?由此可以看到,实际上?ISA()?返回的是?isa.bits?与?0x0000000ffffffff8ULL?进行的按位与操作,确实可以返回当前的类?return?(Class)(isa.bits?&?ISA_MASK);
    }

总结

  • 至此,此次源码分析的第一部分也就此结束,如果您发现了什么问题和不足欢迎与我探讨和指教。

参考资料

作者:shenglanya
链接:https://www.jianshu.com/p/2879f56e7df6

搜索CocoaChina微信公众号:CocoaChina
微信扫一扫
订阅每日移动开发及APP推广热点资讯
公众号:
CocoaChina
我要投稿   收藏文章
上一篇:2018 - iOS 面试题汇总
下一篇:追踪Objective-C方法中的Block参数对象
我来说两句
发表评论
您还没有登录!请登录注册
所有评论(0

综合评论

相关帖子

sina weixin mail 回到顶部
幸运飞艇5码公式 | 幸运飞艇开奖直播app | 北京pk | 幸运农场官网 | 重庆幸运农场预测结果 | 重庆幸运农场开奖历史 | 北京赛车pk10高手心得 | 幸运飞艇定位公式 |