美文网首页
关于Runtime

关于Runtime

作者: 未_漆小七 | 来源:发表于2018-01-16 16:46 被阅读0次

    一个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 的使用场景

    相关文章

      网友评论

          本文标题:关于Runtime

          本文链接:https://www.haomeiwen.com/subject/scuowxtx.html