iOS面试题-Runtime

语言: CN / TW / HK

objc实例对象的isa指针指向什么?有什么作用?

实例对象的isa指向他的类对象,实例对象可以从他的类对象上找到实例对象所包含的实例方法、属性、代理等信息。

类对象的isa指向他的元类对象,类对象可以从元类对象中找到类对象所包含的类方法等信息。

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

在XCode中创建一个NSObject对象,按住Command查看NSObject的定义的时候,就会发现,一个NSObject对象的大小就是一个isa指针的大小。一个isa的指针应该是8字节,但是实际使用malloc_size()去验证的时候却都是16字节。这是因为,对象在分配内存空间的时候,会进行内存对齐,在iOS中,分配的内存空间都是16字节的整数倍,所以这里是分配的16字节。

说一下对 class_rw_t 的理解?

rw 代表可读可写。

ObjC 类中的属性、方法还有遵循的协议等信息都保存在 class_rw_t 中

说一下对 class_ro_t 的理解?

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

说一下对 isa 指针的理解

isa 可以简单的用一句话来代替,<是一种>

1 实例对象 isa 指向类对象

2 类对象指 isa 向元类对象

3 元类对象的 isa 指向元类的基类

isa 有两种类型

1 纯指针,指向内存地址

2 NON_POINTER_ISA,除了内存地址,还存有一些其他信息

从上面的代码可以看出,isa_t对象除了包含内存地址还包含了一些扩展信息

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

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

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

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

无论在MRC下还是ARC下均不需要,被关联的对象在生命周期内要比对象本身释放的晚很多,它们会在被NSObject的dealloc调用的object_dispose()方法中释放。

实例对象的数据结构?

NSObject在Runtime中只有isa一个私有属性,指向类对象的内存地址,剩下的全是public方法

什么是method swizzling

OC中的函数调用机制叫消息发送机制,是向一个对象的方法发送消息,去调用这个方法,而查找这个方法的唯一依据就是selector的名字,根据selector找到对应的方法实现(IMP),对其调用。而Runtime提供了一种可能,就是修改selector和IMP的对应关系,从而实现方法调用的交换。

这种改变对应关系的API有三个:

1 method_exchangeImplementations 直接交换两个方法的实现

2 class_replaceMethod 替换方法的实现

3 method_setImplementation 直接设置某个方法的IMP

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

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

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

1.因为编译后的类已经注册在runtime中,类结构体中的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经确定,同时runtime会调用 class_setVarLayout或class_setWeakIvarLayout来处理strong和weak引用.所以不能向存在的类中添加实例变量。

2.运行时创建的类是可以添加实例变量,调用 class_addIvar 函数. 但是的在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上.

类对象的数据结构?

类对象就是 objc_class。

它的结构相对丰富一些。继承自 objc_object 结构体,所以包含 isa 指针 isa :指向元类

superClass: 指向父类

Cache: 方法的缓存列表

data: 顾名思义,就是数据。是一个被封装好的 class_rw_t 。

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

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

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

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

SideTable 这个结构体,我给他起名引用计数和弱引用依赖表,因为它主要用于管理对象的引用计数和 weak 表。在 NSObject.mm 中声明其数据结构:

struct SideTable {
// 保证原子操作的自旋锁
    spinlock_t slock;
    // 引用计数的 hash 表
    RefcountMap refcnts;
    // weak 引用全局 hash 表
    weak_table_t weak_table;
}

对于 slock 和 refcnts 两个成员不用多说,第一个是为了防止竞争选择的自旋锁,第二个是协助对象的 isa 指针的 extra_rc 共同引用计数的变量(对于对象结果,在今后的文中提到)。这里主要看 weak 全局 hash 表的结构与作用。

当weak引用指向的对象被释放时,又是如何去处理weak指针的呢?

当释放对象时,其基本流程如下:

1.调用 objc_release

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

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

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

5.调用objc_destructinstance函数

6.最后调用objc_clear_deallocating

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

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

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

3.将 weak 表中该记录删除

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

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

如果向一个 nil 对象发送消息,首先在寻找对象的 isa 指针时就是 0 地址返回了,所以不会出现任何错误。 也不会崩溃。

isKindOfClass 与 isMemberOfClass

1、isKindOfClass可用于判断对象是否是一个类的成员,或者是该派生类的成员

2、isMemberOfClass可用于判断对象是否是当前类

一句话就是, isKindOfClass 判断是否是自己或者父类, isMemberOfClass 判断是否是自己

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

  1. 程序启动后,通过编译之后,Runtime 会进行初始化,调用 _objc_init。
  2. 然后会map_images.
  3. 接下来调用map_images_nolock.
  4. 再然后就是read_images,这个方法会读取所有的类的相关信息。
  5. 最后是调用reMethodizeClass:,这个方法是重新方法化的意思。
  6. 在reMethodizeClass: 方法内部会调用 attachCategories: ,这个方法会传入 Class ,会将方法列表,协议列表等与原有的类合并。最后加入到 class_rw_t 结构体

Category 的实现原理?

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

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

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

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

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

Category在刚刚编译完的时候,和原来的类是分开的,只有在程序运行起来后,通过 Runtime ,Category和原来的类才会合并到一起。

mememove,memcpy:这俩方法是位移、复制,简单理解就是原有的方法移动到最后,同时新开辟的空间,把前面的位置留给分类,然后分类中的方法,按照倒序依次插入,可以得出的结论就就是,越晚参与编译 的分类,里面的方法才是生效的那个。

[self class] 与 [super class]

下面的参考文档最后的23题面试题,简而言之就是:

如果是在实例对象里面调用,这两个结果都是son。

如果是直接类方法调用,就是son和father

参考 Runtime面试题.pdf