iOS面试总结

Runtime之类对象、元类对象、消息传递

2019-06-19  本文已影响0人  Jimmy_L_Wang

类对象与元类对象的关系

meta_class.jpg

消息传递

OBJC_EXPORT void
objc_msgSend(void /* id self, SEL op, ... */ )
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
msg_send01.png
OBJC_EXPORT void
objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

/// Specifies the superclass of an instance. 
struct objc_super {
    /// Specifies an instance of a class.
    __unsafe_unretained _Nonnull id receiver;  //接收者

    /// Specifies the particular superclass of the instance to message. 
#if !defined(__cplusplus)  &&  !__OBJC2__
    /* For compatibility with old objc-runtime.h header */
    __unsafe_unretained _Nonnull Class class;
#else
    __unsafe_unretained _Nonnull Class super_class;
#endif
    /* super_class is the first class to search */
};
msg_send02.png

消息传递机制

msg_send03.png

缓存查找

给定值是SEL,目标值是对应bucket_t中的IMP

缓存查找.png

当前类中查找

父类逐级查找

父类逐级查找.png

消息转发

消息转发.png
+ (BOOL) resolveInstanceMethod:(SEL)aSEL //动态地为实例方法的给定选择器提供实现
{
    if (aSEL == @selector(resolveThisMethodDynamically))
    {
          class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
          return YES;
    }
    return [super resolveInstanceMethod:aSel];
}

在调用Objective-C转发机制之前调用此方法。如果respondsToSelector:instancesRespondToSelector:·被调用,则动态方法解析器有机会首先为给定的选择器提供IMP

//返回首先应将无法识别的消息定向到的对象。
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    NSLog(@"forwardingTargetForSelector:");
    return nil;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    if (aSelector == @selector(test)) {
        NSLog(@"methodSignatureForSelector:");
        // v 代表返回值是void类型的  @代表第一个参数类型是id,即self
        // : 代表第二个参数是SEL类型的  即@selector(test)
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    else{
        return [super methodSignatureForSelector:aSelector];
    }
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSLog(@"forwardInvocation:");
}

Method-Swizzling

method_swizzling.png
#import "MethodChange.h"
#import <objc/runtime.h>

@implementation MethodChange

+ (void)load
{
    //获取before方法
    Method before = class_getInstanceMethod(self, @selector(methodChangeBefore));
    //获取after方法
    Method after = class_getInstanceMethod(self, @selector(methodChangeAfter));
    //交换两个方法的实现
    method_exchangeImplementations(before, after);
}

- (void)methodChangeBefore
{
    NSLog(@"methodChangeBefore");
}

- (void)methodChangeAfter
{
    [self methodChangeAfter];
    NSLog(@"methodChangeAfter");
}

@end
    MethodChange *change = [MethodChange new];
    [change methodChangeBefore];

2019-06-05 22:24:11.363371+0800 RuntimeTest[14603:26518469] methodChangeBefore
2019-06-05 22:24:11.363471+0800 RuntimeTest[14603:26518469] methodChangeAfter

动态添加方法

performSelector:

编译时没有这个方法,运行时确需要调用这个方法

void testImp (void)
{
    NSLog(@"test invoke");
}

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    // 如果是test方法 打印日志
    
    if (sel == @selector(test)) {
        NSLog(@"resolveInstanceMethod:");

        // 动态添加test方法的实现
        class_addMethod(self, @selector(test), testImp, "v@:");

        return YES;
//        return NO;
    }
    else{
        // 返回父类的默认调用
        return [super resolveInstanceMethod:sel];
    }
}

动态方法解析

@dynamic

对属性标记为@dynamic,不需要编译器自动为我们添加getter和setter的具体实现,而是在运行时我们具体的调用了getter和setter的时候,再去为他添加具体的实现。

相当于getter和setter是在运行时添加,而不是在编译时声明好具体的实现。

编译型语言和动态运行时语言的区别

[obj foo]objc_msgSend()函数之间有什么关系?

[obj foo]在编译期处理之后就变成了objc_msgSend(obj,@selector(foo))函数,接下来就开始了objc_msgSend函数传递过程。

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

首先查找当前实例所对应类对象的缓存是否有Selector对应缓存的IMP的实现。 ,如果有就把缓存中的函数返回给调用方;

如果缓存中查找不到, 在根据当前类的方法列表去查找Selector对应的IMP的实现;

如果当前类方法列表页没有查找到,在根据当前类的superClass指针,逐级查找父类的方法列表去去查找Selector对应的IMP的实现。

能否向编译后的类中增加实例变量?

不能,class_ro_t中ro代表readOnly,所以没有办法为期添加实例变量。

可以向动态添加的类中添加实例变量,只要在调用注册类对的方法之前,去完成实例变量的添加,就是可以实现的。

上一篇下一篇

猜你喜欢

热点阅读