一、objc 对象的 isa 的指针指向什么?有什么作用?

  • isa 等价于 is kind of:
    • 实例对象 isa 指向类对象;
    • 类对象指 isa 向元类对象;
    • 元类对象的 isa 指向元类的基类;
  • isa 有两种类型:
    • 纯指针,指向内存地址;
    • NON_POINTER_ISA,除了内存地址,还存有一些其它信息。
  • isa 指向它的类对象,从而可以找到对象上的方法,对象、类、元类之间的关系,如下所示:

  • 说明如下:
    • Root class(class) 其实就是 NSObject,NSObject 是没有超类的,因此 Root class(class) 的 superclass 指向 nil;
    • 每个 Class 都有一个 isa 指针指向唯一的 Meta class;
    • Root class(meta) 的 superclass 指向 Root class(class),也就是 NSObject,形成一个回路;
    • 每个 Meta class 的 isa 指针都指向 Root class(meta)。
  • 在 Runtime 源码查看 isa_t 是共用体,简化结构如下:
union isa_ t {Class cls;uintptr_ t bits;# if _arm64__                                      // arm64架构# define ISA_MASK        0x80000000ffffffff8ULL   // 用来取出33位内存地址使用(&)操作# define ISA_MAGIC_MASK  0x0000003f000000001ULL# define ISA_MAGIC_VALUE 0x0000001a000000001ULLstruct {uintptr_t nonpointer         :1;  // 0:代表普通指针,1:表示优化过的,可以存储更多信息uintptr_t has_assoc          :1;  // 是否设置过关联对象,如果没设置过,释放会更快uintptr t has_cxx_dtor       :1;  // 是否有C++的析构函数uintptr t shiftcls           :33; // MACH_VL_MAX_ADDRESS ox10000000000 内存地址值uintptr_t magic              :6;  // 用于在调试时分解对象是否未完成初始化uintptr_t weakly_referenced  :1;  // 是否有被弱引用指向过uintptr_t deallocating       :1;  // 是否正在释放uintptr_t has_sidetable_rc   :1;  // 引用计数器是否过大无法存储在ISA中,如果为1,那么引用计数会存储在一个叫ideTable的类的uintptr_t extra_rc           :19; // 里面存储的值是引用计数器减define RC_ONE  (1ULL<<45)define RC_HALF (1ULL<<18)
};elif _x86_64_     // arm86架构, 模拟器是arm86define ISA_MASK       0x00007ffffffffff8ULLdefine ISA_MAGIC_MASK  0x001f800000008001ULLdefine ISA_MAGIC_VALUE 0x001d800000008001ULLstruct {uintptr_t nonpointer        : 1;uintptr_t has_assoc.        : 1;uintptr_t has_cxx_dtor      : 1;uintptr_t shiftcls          : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000uintptr_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)};
elseerror unknown architecture for packed isa
endif

二、一个 NSObject 对象占用多少内存空间?

  • 受限于内存分配的机制,一个 NSObject 对象都会分配 16byte 的内存空间。但是实际上在 64 位下,只使用了 8byte;在 32 位下,只使用了 4byte。
  • 一个 NSObject 实例对象成员变量所占的大小,实际上是 8 字节。
#import <Objc/Runtime>
Class_getInstanceSize([NSObject class])
  • 本质是:
size_t class_getInstanceSize(Class cls)
{if (!cls) return 0;return cls->alignedInstanceSize();
}
  • 获取 ObjC 指针所指向的内存的大小,实际上是 16 字节:
#import <malloc/malloc.h>
malloc_size((__bridge const void *)objc);
  • 对象在分配内存空间时,会进行内存对齐,所以在 iOS 中,分配内存空间都是 16 字节的倍数。

三、实例对象的数据结构

  • 在文件 objc-private.h 中,如下所示:
struct objc_object {private:isa_t isa;public:// ISA() assumes this is NOT a tagged pointer objectClass ISA(bool authenticated = false);// rawISA() assumes this is NOT a tagged pointer object or a non pointer ISAClass rawISA();// getIsa() allows this to be a tagged pointer objectClass getIsa();uintptr_t isaBits() const;// 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 objectsvoid initIsa(Class cls /*nonpointer=false*/);void initClassIsa(Class cls /*nonpointer=maybe*/);void initProtocolIsa(Class cls /*nonpointer=maybe*/);void initInstanceIsa(Class cls, bool hasCxxDtor);// changeIsa() should be used to change the isa of existing objects.// If this is a new object, use initIsa() for performance.Class changeIsa(Class newCls);bool hasNonpointerIsa();bool isTaggedPointer();bool isTaggedPointerOrNil();bool isBasicTaggedPointer();bool isExtTaggedPointer();bool isClass();// object may have associated objects?bool hasAssociatedObjects();void setHasAssociatedObjects();// object may be weakly referenced?bool isWeaklyReferenced();void setWeaklyReferenced_nolock();// object may have -.cxx_destruct implementation?bool hasCxxDtor();// Optimized calls to retain/release methodsid retain();void release();id autorelease();// Implementations of retain/release methodsid rootRetain();bool rootRelease();id rootAutorelease();bool rootTryRetain();bool rootReleaseShouldDealloc();uintptr_t rootRetainCount();// Implementation of dealloc methodsbool rootIsDeallocating();void clearDeallocating();void rootDealloc();private:void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);// Slow paths for inline controlid rootAutorelease2();uintptr_t overrelease_error();#if SUPPORT_NONPOINTER_ISA// Controls what parts of root{Retain,Release} to emit/inline// - Full means the full (slow) implementation// - Fast means the fastpaths only// - FastOrMsgSend means the fastpaths but checking whether we should call//   -retain/-release or Swift, for the usage of objc_{retain,release}enum class RRVariant {Full,Fast,FastOrMsgSend,};// Unified retain count manipulation for nonpointer isainline id rootRetain(bool tryRetain, RRVariant variant);inline bool rootRelease(bool performDealloc, RRVariant variant);id rootRetain_overflow(bool tryRetain);uintptr_t rootRelease_underflow(bool performDealloc);void clearDeallocating_slow();// Side table retain count overflow for nonpointer isastruct SidetableBorrow { size_t borrowed, remaining; };void sidetable_lock();void sidetable_unlock();void sidetable_moveExtraRC_nolock(size_t extra_rc, bool isDeallocating, bool weaklyReferenced);bool sidetable_addExtraRC_nolock(size_t delta_rc);SidetableBorrow sidetable_subExtraRC_nolock(size_t delta_rc);size_t sidetable_getExtraRC_nolock();void sidetable_clearExtraRC_nolock();
#endif// Side-table-only retain countbool sidetable_isDeallocating();void sidetable_clearDeallocating();bool sidetable_isWeaklyReferenced();void sidetable_setWeaklyReferenced_nolock();id sidetable_retain(bool locked = false);id sidetable_retain_slow(SideTable& table);uintptr_t sidetable_release(bool locked = false, bool performDealloc = true);uintptr_t sidetable_release_slow(SideTable& table, bool performDealloc = true);bool sidetable_tryRetain();uintptr_t sidetable_retainCount();
#if DEBUGbool sidetable_present();
#endif
};
  • 本质上 objc_object 的私有属性只有一个 isa 指针,指向类对象的内存地址。

四、类对象的数据结构

  • 类对象就是 objc_class,如下所示:
struct objc_class : objc_object {Class superclass;const char *name;uint32_t version;uint32_t info;uint32_t instance_size;struct old_ivar_list *ivars;struct old_method_list **methodLists;Cache cache;struct old_protocol_list *protocols;// CLS_EXT onlyconst uint8_t *ivar_layout;struct old_class_ext *ext;void setInfo(uint32_t set) {OSAtomicOr32Barrier(set, (volatile uint32_t *)&info);}void clearInfo(uint32_t clear) {OSAtomicXor32Barrier(clear, (volatile uint32_t *)&info);}// set and clear must not overlapvoid changeInfo(uint32_t set, uint32_t clear) {ASSERT((set & clear) == 0);uint32_t oldf, newf;do {oldf = this->info;newf = (oldf | set) & ~clear;} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&info));}bool hasCxxCtor() {// set_superclass propagates the flag from the superclass.return info & CLS_HAS_CXX_STRUCTORS;}bool hasCxxDtor() {return hasCxxCtor();  // one bit for both ctor and dtor}// Return YES if the class's ivars are managed by ARC, // or the class is MRC but has ARC-style weak ivars.bool hasAutomaticIvars() {return info & (CLS_IS_ARC | CLS_HAS_WEAK_WITHOUT_ARC);}// Return YES if the class's ivars are managed by ARC.bool isARC() {return info & CLS_IS_ARC;}bool hasCustomRR() {return true;}bool hasCustomAWZ() {return true;}bool forbidsAssociatedObjects() {// Old runtime doesn't support forbidding associated objects.return false;}bool instancesHaveAssociatedObjects() {return info & CLS_INSTANCES_HAVE_ASSOCIATED_OBJECTS;}void setInstancesHaveAssociatedObjects() {setInfo(CLS_INSTANCES_HAVE_ASSOCIATED_OBJECTS);}bool shouldGrowCache() {return info & CLS_GROW_CACHE;}void setShouldGrowCache(bool grow) {if (grow) setInfo(CLS_GROW_CACHE);else clearInfo(CLS_GROW_CACHE);}// +initialize bits are stored on the metaclass onlybool isInitializing() {return getMeta()->info & CLS_INITIALIZING;}// +initialize bits are stored on the metaclass onlyvoid setInitializing() {getMeta()->setInfo(CLS_INITIALIZING);}// +initialize bits are stored on the metaclass onlybool isInitialized() {return getMeta()->info & CLS_INITIALIZED;}// +initialize bits are stored on the metaclass onlyvoid setInitialized() {getMeta()->changeInfo(CLS_INITIALIZED, CLS_INITIALIZING);}bool isLoadable() {// A class registered for +load is ready for +load to be called// if it is connected.return isConnected();}IMP getLoadMethod();bool isFuture();bool isConnected();const char *mangledName() { return name; }const char *demangledName() { return name; }const char *nameForLogging() { return name; }bool isRootClass() {return superclass == nil;}bool isRootMetaclass() {return ISA() == (Class)this;}bool isMetaClass() {return info & CLS_META;}// NOT identical to this->ISA() when this is a metaclassClass getMeta() {if (isMetaClass()) return (Class)this;else return this->ISA();}// May be unaligned depending on class's ivars.uint32_t unalignedInstanceStart() {// This is not simply superclass->instance_size.// superclass->instance_size is padded to its sizeof() boundary, // which may envelop one of this class's ivars. // That in turn would break ARC-style ivar layouts.// Instead, we use the address of this class's first ivar when possible.if (!superclass) return 0;if (!ivars || ivars->ivar_count == 0) return superclass->instance_size;return ivars->ivar_list[0].ivar_offset;}// Class's instance start rounded up to a pointer-size boundary.// This is used for ARC layout bitmaps.uint32_t alignedInstanceStart() {return word_align(unalignedInstanceStart());}// May be unaligned depending on class's ivars.uint32_t unalignedInstanceSize() {return instance_size;}// Class's ivar size rounded up to a pointer-size boundary.uint32_t alignedInstanceSize() {return word_align(unalignedInstanceSize());}size_t instanceSize(size_t extraBytes) {size_t size = alignedInstanceSize() + extraBytes;// CF requires all objects be at least 16 bytes.if (size < 16) size = 16;return size;}
};
  • 继承自 objc_object 结构体,因此包含 isa 指针:
    • isa:指向元类;
    • superClass:指向父类;
    • Cache:方法的缓存列表;
    • data:顾名思义,就是数据,是一个被封装好的 class_rw_t。

五、class_rw_t 的理解

  • rw 代表可读可写,ObjC 类中的属性、方法还有遵循的协议等信息都保存在 class_rw_t 中:
// 可读可写
struct class_rw_t {// Be warned that Symbol ication knows the layout of this structureuint32_t flags;uint32_t version; ,const class_ro_t *ro;        // 指向只读的结构体,存故类初始信息/*这三个都是二维数组,是可读可写的,包含了类的初始内容,分类的内容methods 中,存储 methods_list_t --> methods_t二维数组,methods_list_t --> methods_t这三个二维数组中的数据有一部分是从 class_ro_t 中合并过来*/method_array_t methods;      // 方法列表(类对象存放对象方法,元类对象存放实例方法)property_array_t properties; // 属性列表protocol_array_t protocols;  // 协议列表Class firstSubclass;Class nextSiblingClass;// ...

六、class_ro_t 的理解

  • 存储了当前类在编译期就已经确定的属性、方法以及遵循的协议:
struct class_ro_t {uint32_t flags;uint32_t instanceStart;uint32_t instanceSize;
#ifdef __LP64__uint32_t reserved;
#endifunion {const uint8_t * ivarLayout;Class nonMetaclass;};explicit_atomic<const char *> name;// With ptrauth, this is signed if it points to a small list, but// may be unsigned if it points to a big list.void *baseMethodList;protocol_list_t * baseProtocols;const ivar_list_t * ivars;const uint8_t * weakIvarLayout;property_list_t *baseProperties;// ...

七、objc 中向一个 nil 对象发送消息将会发生什么?

  • 如果向一个 nil 对象发送消息,首先在寻找对象的 isa 指针时就是 0 地址返回,所以不会出现任何错误,也不会崩溃。
  • 如果一个方法返回值是一个对象,那么发送给 nil 的消息将返回 0(nil);
  • 如果方法返回值为指针类型,其指针大小为小于或者等于 sizeof(void*) ,float,double,long double 或者 long long 的整型标量,发送给 nil 的消息将返回 0;
  • 如果方法返回值为结构体,发送给 nil 的消息将返回 0,结构体中各个字段的值将都是 0;
  • 如果方法的返回值不是上述提到的几种情况,那么发送给 nil 的消息的返回值将是未定义的。

八、objc 在向一个对象发送消息时,发生了什么?

  • objc 在向一个对象发送消息时,runtime 会根据对象的 isa 指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果一直到根类还没找到,转向拦截调用,执行消息转发机制,一旦找到 ,就去执行它的实现 IMP。

iOS经典面试题之深入解析objc对象的内存空间、数据结构以及isa指针的理解相关推荐

  1. iOS经典面试题之深入解析类Class的iskindOfClass与isMemberOfClass的底层原理

    iskindOfClass 与 isMemberOfClass 的底层原理与区别 一.示例展示 分析以下代码: BOOL re1 = [(id)[NSObject class] isKindOfCla ...

  2. 李洪强iOS经典面试题156 - Runtime详解(面试必备)

    李洪强iOS经典面试题156 - Runtime详解(面试必备)   一.runtime简介 RunTime简称运行时.OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制. 对于C ...

  3. 李洪强iOS经典面试题144-数据存储

    李洪强iOS经典面试题144-数据存储   数据存储 sqlite中插入特殊字符的方法和接收到处理方法. 除'其他的都是在特殊字符前面加"/",而 ' -> '' .方法:k ...

  4. 李洪强iOS经典面试题153- 补充

    李洪强iOS经典面试题153- 补充   补充 有空就来解决几个问题,已经懒癌晚期没救了... UML 统一建模语言(UML,UnifiedModelingLanguage)是面向对象软件的标准化建模 ...

  5. 李洪强iOS经典面试题36-简单介绍 ARC 以及 ARC 实现的原理

    李洪强iOS经典面试题36-简单介绍 ARC 以及 ARC 实现的原理 问题 简单介绍 ARC 以及 ARC 实现的原理. 考查点 ARC 是苹果在 WWDC 2011 提出来的技术,因此很多新入行的 ...

  6. 李洪强iOS经典面试题37-解释垃圾回收的原理

    李洪强iOS经典面试题37-解释垃圾回收的原理 问题 我们知道,Android 手机通常使用 Java 来开发,而 Java 是使用垃圾回收这种内存管理方式. 那么,ARC 和垃圾回收对比,有什么优点 ...

  7. iOS经典面试题总结--内存管理

    内存管理 1.什么是ARC? ARC是automatic reference counting自动引用计数,在程序编译时自动加入retain/release.在对象被创建时retain count+1 ...

  8. iOS经典面试题大全

    1.INTERVIEW 共勉 作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:638302184,不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题.面试经验,讨 ...

  9. 整理一篇iOS经典面试题大全

    ###1.INTERVIEW 共勉 ###2.iOS developers 方向 ###3.INTERVIEW QUESTION 1.深copy和浅copy 浅拷贝: 1.对内存地址的复制,让目标对象 ...

最新文章

  1. //输入学生人数,挨个输入姓名,身高,年龄,求平均年龄,然后按身高降序排列输出...
  2. opencv4.0在linux下编译,Ubuntu 18.04源码编译安装OpenCV 4.0步骤
  3. flutter TabBar 底部导航栏
  4. golang生成随机定长的验证吗
  5. 如何计算边缘计算的成本
  6. 夺命雷公狗---PDO NO:13 PDO的预处理查询4
  7. 问题三十六:ray tracing中的Inverse Mapping(1)——球面Inverse Mapping
  8. URLDecoder.decode()转义处理
  9. MATLAB之牛顿插值法
  10. swfupload 无法加载_flash上传插件——swfupload 【原创】
  11. word里双横线怎么打_word怎么加双下划线
  12. c语言大计基题库,2016年大学计算机基础试题题库及答案
  13. 数据报表体系搭建流程
  14. Apple Final Cut Pro X 10.4.6 中文特别版 Mac 最强大视频后期制作软件
  15. 基本过滤工具之配置前缀列表
  16. 如何将caj格式文件转换成word文档
  17. snmp v3 参数_snmp v3的安全配置 snmp认证与加密配置(53)
  18. 聚合支付码 一码支付的大时代到来
  19. 软件公司需要哪些资质?
  20. Python之Excel图片处理(将excel chart另存为图片)

热门文章

  1. 航空叶片检测之相关名词解释
  2. 春招95后很狂阿里P7晒出工资单:狠补了两眼泪汪汪,真香...
  3. 请结合在播影视剧,对比一下腾讯、优酷和爱奇艺三家在付费内容推广策略上的差异。
  4. 强制删除需要卸载密码的软件
  5. 产品经理的需求分析四个层次
  6. jquery监听pda 按键_js 点击input焦点不弹出键盘 PDA扫描枪
  7. 摆脱流量依赖,“心智营销”是玄学吗?
  8. 计算机配置打开命令行,电脑cmd命令可以打开电脑属性,知道怎么设置
  9. SpingBoot yml语法及测试总结yml文件常用的五种方式
  10. CDN和CDN加速有什么关联