一个objc对象如何进行内存布局?(考虑有父类的情况)
NSString *str = @“”;
* 所有父类的成员变量和自己的成员变量都会存放在该对象str所对应的存储空间中.
* 每一个对象内部都有一个isa指针,指向他的类对象NSString,类对象中存放着本对象的
* 对象方法列表(对象能够接收的消息列表,保存在它所对应的类对象中)
* 成员变量的列表,
* 属性列表,
* 它内部也有一个isa指针指向元对象(meta class),(既类对象所属的类型,用来表述NSString 是一个什么样的对象 元对象内部存放的是类方法列表)
* 类对象内部还有一个superclass的指针,指向他的父类对象。
* NSObject,它的superclass指针指向nil
* 类对象既然称为对象,那它也是一个实例。类对象中也有一个isa指针指向它的元类(meta class),即类对象是元类的实例。元类内部存放的是类方法列表,根元类的isa指针指向自己,superclass指针指向NSObject类。
一个objc对象的isa的指针指向什么?有什么作用?
指向他的类对象,从而可以找到对象上的方法
objc中的类方法和实例方法有什么本质区别和联系?
类方法:
1. 类方法是属于类对象的
2. 类方法只能通过类对象调用
3. 类方法中的self是类对象
4. 类方法可以调用其他的类方法
5. 类方法中不能访问成员变量
6. 类方法中不能直接调用对象方法
实例方法:
1. 实例方法是属于实例对象的
2. 实例方法只能通过实例对象调用
3. 实例方法中的self是实例对象
4. 实例方法中可以访问成员变量
5. 实例方法中直接调用实例方法
6. 实例方法中也可以调用类方法(通过类名)
runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)?
每一个类对象中都一个方法列表,方法列表中记录着方法的名称,方法实现,以及参数类型,其实selector本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现.
objc中向一个nil对象发送消息将会发生什么?
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果向一个nil对象发送消息,首先在寻找对象的isa指针时就是0地址返回了,所以不会出现任何错误。
objc中向一个对象发送消息[obj foo]和objc_msgSend()函数之间有什么关系?
objc是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)。
[obj foo];在objc动态编译时,会被转意为:objc_msgSend(obj, @selector(foo));。
什么时候会报unrecognized selector的异常?
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果,在最顶层的父类中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX 。但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会:
1. Method resolutionobjc运行时会调用+resolveInstanceMethod:或者 +resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数,那运行时系统就会重新启动一次消息发送的过程,否则 ,运行时就会移到下一步,消息转发(Message Forwarding)。
2. Fast forwarding如果目标对象实现了-forwardingTargetForSelector:,Runtime 这时就会调用这个方法,给你把这个消息转发给其他对象的机会。既能不能把这条消息转发给其他接受者处理, 只要这个方法返回的不是nil和self,整个消息发送的过程就会被重启,当然发送的对象会变成你返回的那个对象。否则,就会继续Normal Fowarding。 这里叫Fast,只是为了区别下一步的转发机制。因为这一步不会创建任何新的对象,但下一步转发会创建一个NSInvocation对象,所以相对更快点。
3. Normal forwarding这一步是Runtime最后一次给你挽救的机会。首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。如果-methodSignatureForSelector:返回nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象。
下面的代码输出什么?
@implementation Son : Father
- (id)init
{
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
其实 super 是一个 Magic Keyword, 它本质是一个编译器标示符,和 self 是指向的同一个消息接受者!他们两个的不同点在于:super 会告诉编译器,调用 class 这个方法时,要去父类的方法,而不是本类里的。
上面的例子不管调用[self class]还是[super class],接受消息的对象都是当前 Son *xxx 这个对象。
当使用 self 调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用 super 时,则从父类的方法列表中开始找。然后调用父类的这个方法。
_objc_msgForward函数是做什么的,直接调用它将会发生什么?
_objc_msgForward是 IMP 类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发。
结合《NSObject官方文档》,排除掉 NSObject 做的事,剩下的就是_objc_msgForward消息转发做的几件事:
1. 调用resolveInstanceMethod:方法 (或 resolveClassMethod:)。允许用户在此时为该 Class 动态添加实现。如果有实现了,则调用并返回YES,那么重新开始objc_msgSend流程。这一次对象会响应这个选择器,一般是因为它已经调用过class_addMethod。如果仍没实现,继续下面的动作。
2. 调用forwardingTargetForSelector:方法,尝试找到一个能响应该消息的对象。如果获取到,则直接把消息转发给它,返回非 nil 对象。否则返回 nil ,继续下面的动作。注意,这里不要返回 self ,否则会形成死循环。
3. 调用methodSignatureForSelector:方法,尝试获得一个方法签名。如果获取不到,则直接调用doesNotRecognizeSelector抛出异常。如果能获取,则返回非nil:创建一个 NSlnvocation 并传给forwardInvocation:。
4. 调用forwardInvocation:方法,将第3步获取到的方法签名包装成 Invocation 传入,如何处理就在这里面了,并返回非ni。
5. 调用doesNotRecognizeSelector: ,默认的实现是抛出异常。如果第3步没能获得一个方法签名,执行该步骤。
一旦调用_objc_msgForward,将跳过查找 IMP 的过程,直接触发“消息转发”,
如果调用了_objc_msgForward,即使这个对象确实已经实现了这个方法,你也会告诉objc_msgSend:“我没有在这个对象里找到这个方法的实现”
有哪些场景需要直接调用_objc_msgForward?最常见的场景是:你想获取某方法所对应的NSInvocation对象。举例说明:
JSPatch (Github 链接)就是直接调用_objc_msgForward来实现其核心功能的:
JSPatch 以小巧的体积做到了让JS调用/替换任意OC方法,让iOS APP具备热更新的能力。
能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
* 不能向编译后得到的类中增加实例变量;
* 能向运行时创建的类中添加实例变量;
解释下:
* 因为编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表 和 instance_size 实例变量的内存大小已经确定,同时runtime 会调用 class_setIvarLayout 或 class_setWeakIvarLayout 来处理 strong weak 引用。所以不能向存在的类中添加实例变量;
* 运行时创建的类是可以添加实例变量,调用 class_addIvar 函数。但是得在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。
http://www.jianshu.com/p/e071206103a4 runtime 的使用场景
网友评论