Category解析

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

指向他的类对象,从而可以找到对象上的方法

详解:下图很好的描述了对象,类,元类之间的关系:

[图片上传失败...(image-f3fe67-1661948616530)]

<figcaption>图中实线是 super_class 指针,虚线是 isa 指针。</figcaption>

1.Root class (class)其实就是 NSObject,NSObject 是没有超类的,所以 Root class(class)的 superclass

指向 nil。

2.每个 Class 都有一个 isa 指针指向唯一的 Meta class

3.Root class(meta)的 superclass 指向 Root class(class),也就是 NSObject,形成一个回路。

4.每个 Meta class 的 isa 指针都指向 Root class (meta)。

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

受限于内存分配的机制,一个 NSObject 对象都会分配 16byte 的内存空间。

但是实际上在 64 位 下,只使用了 8byte;

在 32 位下,只使用了 4byte

一个 NSObject 实例对象成员变量所占的大小,实际上是 8 字节

[图片上传失败...(image-9f6aec-1661948616530)]

本质是

[图片上传失败...(image-708660-1661948616530)]

获取Obj-C指针所指向的内存的大小,实际上是

[图片上传失败...(image-a7dcb3-1661948616529)]

对象在分配内存空间时,会进行内存对齐,所以在iOS中,分配内存空间都是16字节的倍数。

可以通过以下网址:http://openSource.apple.com/tarballs来查看源代码。

三、说一下对class_rw_t的理解?

rw代表可读可写。ObjC类中的属性、方法还有遵循的协议等信息都保存在class_rw_t中:

[图片上传失败...(image-70997f-1661948616529)]

四、说一下对class_ro_t的理解?

存储了当前类在编译期就已经确定的属性、方法以及遵循的协议

[图片上传失败...(image-939002-1661948616529)]

五、、说一下对isa指针的理解,对象的isa指针指向哪里?isa指针有哪两种类型?

isa等价于 is kind of

  • 实例对象isa指向类对象
  • 类对象指isa向元类对象
  • 元类对象的isa指向元类的基类

isa有两种类型

  • 纯指针,指向内存地址
  • NON_POINTER_ISA,除了内存地址,还存有一些其他信息

isa源码分析

在Runtime源码查看sa_t是共用体。简化结构如下:

[图片上传失败...(image-8790ca-1661948616529)]

六、说一下untime的方法缓存?存储的形式、数据结构以及查找的过程?

cache_t增量扩展的哈希表结构。哈希表内部存储的bucket_t。

bucket_t中存储的是SEL和IMP的键值对。

  • 如果是有序方法列表,采用二分查找
  • 如果是无序方法列表,直接遍历查找

cache_t结构体

[图片上传失败...(image-8fa82e-1661948616529)]

七、使用 runtime Associate 方法关联的对象,需要在主对象 dealloc 的时候释放么?

无论在 MRC 下还是 ARC 下均不需要,被关联的对象在生命周期内要比对象本身释放的晚很多,它们会在

被 NSObject -dealloc 调用的 object_dispose()方法中释放。

[图片上传失败...(image-2b761-1661948616529)]

[图片上传失败...(image-418550-1661948616529)]

十、什么时候会报 unrecognized selector 的异常?

objc 在向一个对象发送消息时,runtime 库会根据对象的 isa 指针找到该对象实际所属的类,然后在该类中

的方法列表以及其父类方法列表中寻找方法运行,如果,在最顶层的父类中依然找不到相应的方法时,会

进入消息转发阶段,如果消息三次转发流程仍未实现,则程序在运行时会挂掉并抛出异常 unrecognized

selector sent to XXX 。

十一、如何给 Category 添加属性?关联对象以什么形式进行存储?

查看的是 关联对象 的知识点。

详细的说一下 关联对象。

关联对象 以哈希表的格式,存储在一个全局的单例中。

[图片上传失败...(image-8d07a4-1661948616529)]

十二、能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?

为什么?

不能向编译后得到的类中增加实例变量;

能向运行时创建的类中添加实例变量;

1.因为编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表和 instance_size

实例变量的内存大小已经确定,同时 runtime 会调用 class_setvarlayout 或 class_setWeaklvarLayout 来

处理 strong weak 引用.所以不能向存在的类中添加实例变量。

2.运行时创建的类是可以添加实例变量,调用 class_addIvar 函数. 但是的在调用 objc_allocateClassPair

之后,objc_registerClassPair 之前,原因同上.

[图片上传失败...(image-d0c96d-1661948616529)]

十四、runtime 如何通过 selector 找到对应的 IMP 地址?

每一个类对象中都一个方法列表,方法列表中记录着方法的名称,方法实现,以及参数类型,其实 selector 本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现.、

十五、runtime 如何实现 weak 变量的自动置 nil?知道 SideTable 吗?

runtime 对注册的类会进行布局,对于 weak 修饰的对象会放入一个 hash 表中。 用 weak 指向的对象内 存地址作为 key,当此对象的引用计数为 0 的时候会 dealloc,假如 weak 指向的对象内存地址是 a,那么就 会以 a 为键, 在这个 weak 表中搜索,找到所有以 a 为键的 weak 对象,从而设置为 nil。

更细一点的回答:

1.初始化时:runtime 会调用 objc_initWeak 函数,初始化一个新的 weak 指针指向对象的地址。

2.添加引用时:objc_initWeak 函数会调用 objc_storeWeak() 函数, objc_storeWeak()的作用是更新指针指向,创建对应的弱引用表。

3.释放时,调用 clearDeallocating 函数。clearDeallocating 函数首先根据对象地址获取所有 weak 指针地址的数组,然后遍历这个数组把其中的数据设为 nil,最后把这个 entry 从 weak 表中删除,最后清理对象的记录。

SideTable 结构体是负责管理类的引用计数表和 weak 表,

详解:参考自《Objective-C 高级编程》一书

[图片上传失败...(image-80e7a0-1661948616529)]

[图片上传失败...(image-232c49-1661948616529)]

3. 释放时,调用 clearDeallocating 函数。clearDeallocating 函数首先根据对象地址获取所有 weak 指针地址的数组,然后遍历这个数组把其中的数据设为 nil,最后把这个 entry 从 weak 表中删除,最后清理对象的记录。

当 weak 引用指向的对象被释放时,又是如何去处理 weak 指针的呢?当释放对象时,其基本流程如下:

1.调用 objc_release

2.因为对象的引用计数为 0,所以执行 dealloc

3.在 dealloc 中,调用了_objc_rootDealloc 函数

4.在_objc_rootDealloc 中,调用了 object_dispose 函数

5.调用 objc_destructInstance

6.最后调用 objc_clear_deallocating

对象被释放时调用的 objc_clear_deallocating 函数:

1.从 weak 表中获取废弃对象的地址为键值的记录

2.将包含在记录中的所有附有 weak 修饰符变量的地址,赋值为 nil3.将 weak 表中该记录删除

4.从引用计数表中删除废弃对象的地址为键值的记录

总结:

其实 Weak 表是一个 hash(哈希)表,Key 是 weak 所指对象的地址,Value 是 weak 指针的地址(这个地址的值是所指对象指针的地址)数组。

十六、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 。

[图片上传失败...(image-94ab67-1661948616529)]

详解:

在 isKindOfClass 中有一个循环,先判断 class 是否等于 meta class,不等就继续循环判断是否等于 meta class 的 super class,不等再继续取 super class,如此循环下去。

[NSObject class]执行完之后调用 isKindOfClass,第一次判断先判断 NSObject 和 NSObject 的 meta class 是否相等,之前讲到 meta class 的时候放了一张很详细的图,从图上我们也可以看出,NSObject 的 meta class 与本身不等。接着第二次循环判断 NSObject 与 meta class 的 superclass 是否相等。还是从那张图 上面我们可以看到:Root class(meta) 的 superclass 就是 Root

class(class),也就是 NSObject 本身。所以第二次循环相等,于是第一行 res1 输出应该为 YES。

同理,[Sark class]执行完之后调用 isKindOfClass,第一次 for 循环,Sark 的 Meta Class 与[Sark class] 不等,第二次 for 循环,Sark Meta Class 的 super class 指向的是 NSObject Meta Class, 和 Sark Class不相等。第三次 for 循环,NSObject Meta Class 的 super class 指向的是 NSObject Class,和 Sark Class 不相等。第四次循环,NSObject Class 的 super class 指向 nil, 和 Sark Class 不相等。第四次循环之 后,退出循环,所以第三行的 res3 输出为 NO。

isMemberOfClass 的源码实现是拿到自己的 isa 指针和自己比较,是否相等。

第二行 isa 指向 NSObject 的 Meta Class,所以和 NSObject Class 不相等。第四行,isa 指向 Sark 的Meta Class,和 Sark Class 也不等,所以第二行 res2 和第四行 res4 都输出 NO。

十九、Category 在编译过后,是在什么时机与原有的类合并到一起的?

1. 程序启动后,通过编译之后,Runtime 会进行初始化,调用 _objc_init。

2. 然后会 map_images。

3. 接下来调用 map_images_nolock。

4. 再然后就是 read_images,这个方法会读取所有的类的相关信息。

5. 最后是调用 reMethodizeClass:,这个方法是重新方法化的意思。

6. 在 reMethodizeClass: 方法内部会调用 attachCategories: ,这个方法会传入 Class 和 Category ,会将方法列表,协议列表等与原有的类合并。最后加入到 class_rw_t 结构体中。

二十、Category 有哪些用途?

 给系统类添加方法、属性(需要关联对象)。

对某个类大量的方法,可以实现按照不同的名称归类。

二十一、Category 的实现原理?

被添加在了 class_rw_t 的对应结构里。

Category 实际上是 Category_t 的结构体,在运行时,新添加的方法,都被以倒序插入到原有方法列 表的最前面,所以不同的 Category,添加了同一个方法,执行的实际上是最后一个。

拿方法列表举例,实际上是一个二维的数组。

Category 如果翻看源码的话就会知道实际上是一个 _catrgory_t 的结构体。

--

例如我们在程序中写了一个 Nsobject+Tools 的分类,那么被编译为 C++ 之后,实际上是:

[图片上传失败...(image-eb2544-1661948616529)]

二十二、_objc_msgForward 函数是做什么的,直接调用它将会发生什么?

_objc_msgForward 是 IMP 类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候, _objc_msgForward 会尝试做消息转发。

详解:_objc_msgForward 在进行消息转发的过程中会涉及以下这几个方法:

1. List itemresolveInstanceMethod:方法 (或 resolveClassMethod:)。

2. List itemforwardingTargetForSelector:方法

3. List itemmethodSignatureForSelector:方法

4. List itemforwardInvocation:方法

5. List itemdoesNotRecognizeSelector: 方法

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351

推荐阅读更多精彩内容