分享

iOS中@selector的runtime过程

 叹落花 2015-07-08

大家都知道oc是动态语言,是runtime的,怎么体现他的动态性呢,今天用SEL来分析下,之前分享过钩子其实就可以看出来了,现在具体看看OC是怎么通过SEL找到函数的。

1
2
[self performSelector:@selector(abcd)];
[self abcd];   //这种方法可能都会编译不过去

假如类方法里面我们便没有写adcd方法,这样就会奔溃了,oc是怎么做的呢

  1. 该方法调用后,OC会发出objc_msgSend,将消息传给本来查找本类中的SEL是否存在Method

  2. 假设本类不存在会查找工程里是否有分类提供了该方法

  3. 假设分类中也没有该方法,系统会将消息转发给其父类,父类收到消息后也会执行上面的操作,找方法,没找到再往上转发消息

  4. 假设最终都没有找到,就会执行最后的消息转发(message forwarding)操作

  5. 如果转发出去都没人接收的话,NSObject中的doesNotRecognizeSelector就选择抛出异常了,也就是我们看到的crash


上面的过程有点复杂,大家会觉得这样很慢,第一次的确很慢,所性objc_msgSend会将匹配结果写到映射表中缓存起来,每个类都有这样的一块缓存

整个过程就是上面将的那样,我们讲一下第4项消息转发怎么处理吧:

消息转发还分两个阶段:

1.动态方法解析:意思就是说询问接受者要不要增加个方法来实现该函数

1
2
+ (BOOL)resolveInstanceMethod:(SEL)selecotor   //对象方法
+ (BOOL)resolveClassMethod:(SEL)sel            //类方法

 在方法里可以动态给类增加一个方法Method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
     
    [self performSelector:@selector(abcd)];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSString *selname = NSStringFromSelector(sel);
    if ([selname isEqualToString:@"abcd"]) {
        class_addMethod(self, sel, class_getMethodImplementation([self class], @selector(oklala)), method_getTypeEncoding(class_getInstanceMethod([self class], @selector(oklala))));
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
- (void)oklala {
    NSLog(@"oklala");
}

2.完整的消息转发:看看还有没有别的对象要处理,有转出,没有的话封装成NSInvocation对象处理

1)假如可以转给别的对象处理:

1
- (id)forwardingTargetForSelector:(SEL)aSelector

可以将要转发的对象返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@interface abcdCaller : NSObject
@end
@implementation abcdCaller
- (void)abcd {
    NSLog(@"~~~~~~");
}
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
     
    [self performSelector:@selector(abcd)];
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"!!!!!!!!!!!!!");
    return [[abcdCaller alloc] init];
}
@end

2)假如没有对象可以转发(完整消息转发)

1
- (void)forwardInvocation:(NSInvocation *)anInvocation

最后可以处理消息的机会

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
     
    [self performSelector:@selector(abcd)];
     
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes:"@@:"];
    return sig;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"~~~~%@", [anInvocation description]);
}


---------------------------------------------------凌乱的分割线-------------------------------------------------

假设你的消息转发不处理,那么在第5步捕获异常,也是不会奔溃的

1
2
3
4
5
6
7
8
9
    @try {
        [self performSelector:@selector(abcd)];
    }
    @catch (NSException *exception) {
        NSLog(@"~~~~~~~~");
    }
    @finally {
         
    }


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多