1.Category的实现原理?
- Category实际是Category_t的结构体,在运行时,新添加的方法,都被倒序插入到原方法列表的最前面,所以不同的Category,添加了同一个方法,执行的实际上是最后一个.
- Category在刚编译完成的时候,和原来的类是分开的,只有在程序运行起来后,通过Runtime,Category和原来的类才会合并到一起.
2.isa指针的理解,对象的isa指针指向哪里?isa指针有哪两种类型?
- isa 等价于 is kind of
实例对象的isa指针指向类对象
类对象的isa指针指向元类对象
元类对象的isa指针指向元类的基类NSObject - isa 的两种类型
纯指针,指向内存地址 (指针型isa: 64位的0或者1的整体内容代表所指向的Class的地址,也就是可以通过isa的内容来获得类对象的地址
)
NON_POINTER_ISA,除了内存地址,还存有一些其他信息 (非指针型isa: isa的值得部分代表Class的地址,之所以这样是因为我们在寻址过程中,只有三四十位数就可以保证我们寻找到所有Class地址了,多出来的位可以用来存储其他相关内容,来达到节省内存的目的
)
3.Objective_C如何实现多重继承?
OC的类没有多继承,只支持单继承,如果要实现多继承的话,可以使用以下方式间接实现:
- 通过组合实现
A和B组合,作为C类的组件 - 通过协议实现
C类实现A和B类的协议方法
*消息转发实现
forwardIncocation:方法 (这个方法作用是用来实现消息转发的,我们可以创建一个 delegate 转发器,作为多个 delegate 的容器,也是对外唯一的 delegate。当消息发送给这个转发器的时候会触发对应的 forwardInvocation 方法。我们在这里进行消息转发
)
- (void)forwardInvocation:(NSInvocation *)anInvocation{
SEL aSelector = [anInvocation selector];
if([self.firstDelegate respondsToSelector:aSelector]){
[anInvocation invokeWithTarget:self.firstDelegate];
}
if([self.secondDelegate respondsToSelector:aSelector]){
[anInvocation invokeWithTarget:self.secondDelegate];
}
}
4.Runtime 如何实现weak属性?
weak此特质表明改属性定义了一种非拥有关系 (nonowning relationship
).这种属性设置新值时,设置方法即不持有新值 (新指向的对象
) ,不持有旧值 (原来指向的对象
)
Runtime 对注册的类,会进行内存布局,从一个粗粒度的概念上讲,这时候会有一个hash表,这是一个全局表,表中是用weak指向的对象内存地址作为key,用所有指向对象的weak指针表作为value.当此对象的引用计数为0的时候回dealloc,假如该对象的内存地址是a,那么多就会以a为key,在这weak表中搜索,找到所有以a为key的weak对象,从而设置为nil.
- 1.初始化时:runtime会调用objc_initWeak 函数,初始化一个新的weak指针指向对象地址
- 2.添加引用时:objc_initWeak函数会调用objc_storeWeak函数.objc_storeWeak的作用是更新指针指向 (
指针可能原来指向其他对象,这时候需要将该weak的指针与旧对象解除绑定,会调用到weak_unregister_no_lock
),如果指针指向的新对象非空,则创建对应的弱引用表,讲weak指针与新对象进行绑定,会调用weak_register_no_lock.在这个过程中,为了防止多线程中竞争冲突,会有一些加锁的操作. - 3.释放时:调用clearDeallocating函数,clearDeallocating函数首先根据对象地址获取所有的weak指针地址数组,然后遍历这个数组把其中的数据设置成nil,最后把这个entry从weak表中删除,最后清理对象的记录.
5.OC的消息机制
- 1.OC中的方法调用其实都是转成了objc_megSend函数调用,给receiver(方法调用者)发送一条消息(selector方法名)
- 2.objc_msgSend底层有3大阶段,消息发送(当前类,父类中查找),动态方法解析,消息转发.
6.runtime具体应用
- 1.利用关联对象(AssociatedObject)给分类添加属性
- 2.遍历类的成员变量(修改textfield的占位文字颜色,字典转模型,自动归解档)
- 3.交换方法(交换系统的方法)
- 4.利用消息转发机制解决方法找不到的异常问题
- 5.KVC字典转模型
7.runtime如何通过selector找到对应IMP地址?
每一个类对象中都有一个对象方法列表(对象方法缓存)
- 1.类方法类表中存放在类对象中的isa指针指向的元类对象中(类方法缓存)
- 2.方法列表中没个方法结构体中记录着方法的名称,方法实现,以及参数类型,其实selector本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现.
- 3.当我们发送一个消息给NSObject对象时,这条消息会在对象的类对象方法列表里查找.
- 4.当我们发送一个消息给一个类时,这条消息会在类的Meta Class对象列表中查找.
8.Objective-C中调用方法的过程
Objective-C是动态语言,每个方法在运行时会被动态转为消息发送,即 : objc_msgSend(receiver,selector),整个过程如下:
- 1.objc在想一个对象发送消息是,runtime库会根据对象的isa指针找到改对象实际所属的类.
- 2.然后在改类中的方法列表以及其父类方法列表中找寻方法运行.
- 3.如果,在最顶层的父类(一般也就是NSObject)中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecongnized selector sent to XXX.
- 4.但是在这之前,objc的运行时会给三次拯救程序崩溃的机会,这三次拯救程序崩溃如下:
第一次: 动态添加一个新方法并执行的机会,当系统第一次找不到某个方法的时候, 会自动调用这个方法, 用来给程序添加一个新方法并执行的机会.
+ (bool)resolveInstanceMethod:(sel)sel{ }
第二次: 当系统调用上一个方法后未能实现添加新的方法, 则系统会再来调用下面的这个方法, 这个方法是系统提供的一个将 SEL 转给其他对象的机会.
- (id)forwardingTargetForselector:(sel)aselctor{ }
第三次: 当 forwardingTargetForselector 返回的 nil 或者 self 时, 会进入到这个方法, 这个方法是拯救程序的最后一步.
这个方法用来返回一个方法签名, 在由后面的 forwardInvocation: 去执行
- (NSMethodSigature *)methodSignatrueForseletor:(sel)aselector{ }
9.load和initialize的区别
两者都会自动调用父类的,不需要super操作,且仅会调用一次(不包括外部显示调用).
- 1.load和initialize方法都会在实例化对象之前调用,以main函数为分水岭,前者在main函数之前调用,后者在之后调用.这两个方法会被自动调用,不能手动调用它们.
- 2.load和initialize方法都不用显示的调用父类的方法而自动调用,即子类没有initialize方法也会调用父类的方法,而load方法则不会调用父类.
- 3.load方法通常用来进行Method Swizzle,initialize方法一般用于初始化全局变量或者静态变量.
- 4.load和initialize方法内部使用了锁,因此它们是线程安全的.实现时要尽可能保持简单,避免阻塞线程,不要再使用锁.
10.如何理解Objective-C是动态语言.
- 1.主要是讲数据类型的确定时间由编译时,推迟到了运行时.这个问题其实涉及到两个概念,运行时和多态.
- 2.简单来说,运行时机制使我们直到运行时才去决定一个对象的类别,以及其调用该类别对象指定的方法.
- 3.多态:不同对象以自己的方式响应相同信息的能力叫做多态.(意思就是假设生物类(life)都拥有一个相同的方法 -eat,那么人类属于生物,狗也属于生物,都继承了life后,实现了各自的eat方法.但是调用时,我们只需要调用各自的eat方法.也就是不同对象以自己的方式响应相同的消息.因此也可以说,运行时机制是多态的基础).