Objective-C方法的调用流程详解

在Objective-C中,方法会在运行时转换成一个消息函数的的调用,objc_msgSend。其基本形式是objc_msgSend(receiver, selector, arg1, arg2, ...)。第一个参数是消息接受者,第二个参数是一个SEL类型的数据,其余的为消息的参数。

  等等,如果您对基本方法基本的概念还不太清晰的话,请参阅一下另一篇文章。如果有了基本的概念性基础后,我们来进行流程的解析。

定义一个男人类,里面没有多余的代码。

@interface Man : NSObject
@end
@implementation Man
@end

 

 1. 如果selector是一些内存管理相关的消息(如retain,release,retainCount等),则直接返回接收者。

2. 如果没有接受者则返回为空(nil,0,或者{0})

3. 接下来接受者会在对象结构体中缓存的方法链表中寻找selector,如果寻找到就进行方法的调用。如果没有找到,则继续在所有的方法链表中寻找,如果寻找到了,则进行方法的调用,并且把这个selector存在缓存链表中。假如在对象的结构体中没有寻找到这个selector,则会一级一级的在父类的方法链表中寻找,直到NSObject停止。

这些流程和大部分的语言相似,但是Objective-C并不止如此,它还会进行更多的检测和操作,从而进一步挽救大家“脆弱”的程序。

4. 挽救第一步:利用resolveInstanceMethod:进行自救---动态方法解析

假设这个男人还算勤劳,自己会做饭,所以定义一个做饭的方法。在Man.h中加入如下方法

- (NSString *)makeSomeFood:(NSString *)food;          //做饭
但是在Man.h中不去实现它,因为他不知道今天到底有没有肉,如果有肉就做肉吃,没有就做大白菜吃。在Man.m中加入如下代码

static id makeVegetable (id self, SEL _cmd, id value)
{
    NSString *intentedFood = (NSString *)value;
    return [NSString stringWithFormat:@"本来打算做%@吃,现在只有做大白菜吃了",intentedFood];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSString *selStr = NSStringFromSelector(sel);
    if ([selStr isEqualToString:@"makeSomeFood:"]) {
        class_addMethod([Man class], sel, (IMP)makeVegetable, "@:@");
    }
    return [super resolveInstanceMethod:sel];
}
 

 
resolveInstanceMethod:方法中先判断方法名称,如果是makeSomeFood:方法,则在运行时动态的添加一个makeVegetable方法去进行方法处理并放回结果。 
class_addMethod的第一个参数是类名,第二个参数是方法名称,第三个是函数实现地址,第四个参数是类型编码参数。 

5. 挽救第二步:利用forwardingTargetForSelector:进行求助---备用接受者

假设这个男人想生一个孩子,他必然得求助他老婆啦,当然前提有愿意替他生孩子的老婆。

定义Wife类

@interface Wife : NSObject
@end
@implementation Wife
- (void)giveBirthToBaby
{
    NSLog(@"生孩子啦");
}
@end

 
//Man.h中添加方法<pre name="code" class="objc">@property (nonatomic, retain) Wife *mWife;
- (void)giveBirthToBaby; //生孩子
 
 
//Man.m中实现<pre name="code" class="objc">- (id)forwardingTargetForSelector:(SEL)aSelector{ 
    if ([_mWife respondsToSelector:aSelector]) { 
        return _mWife; 
    } 
        return [super forwardingTargetForSelector:aSelector];
}
 
 
//初始化的时候<pre name="code" class="objc">Man *people = [[Man alloc] init];
people.mWife = Wife.new;
[people giveBirthToBaby]; 
 

当调用giveBirthToBaby:时,会执行第四步去检查自己能不能自力更生解决问题,当没法解决的时候会让_mWife去实现相应的方法,从表面上看时Man去实现了这个方法。

 这里需要注意一点的是:由于是让新对象去执行这个方法,一个必要的流程也是如果这个新对象也没有实现方法,就会去父类中找实现方法,如果找到了就调用并存储到缓存链表中,没有找到则没法实现会抛出异常,继续接下来的拯救方法。

6. 挽救第三步:利用forwardInVocation:进行外包---完整消息转发假设这个男人现在想要制造一个大飞机,虽然是土豪,但是最多也只能是外包给朋友去解决了。

定义能解决问题的土豪朋友类:

@interface RichFriend : NSObject
@end
@implementation RichFriend
- (void)makeSomeAirplane:(NSString *)airPlaine
{
    NSLog(@"%@",[NSString stringWithFormat:@"制造%@成功",airPlaine]);
}
@end
 
//Man.h中添加方法
- (void)makeSomeAirplane:(NSString *)airPlaine;       //造飞机

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSMethodSignature *sig = [super methodSignatureForSelector:aSelector];
    if (!sig) {
        if ([RichFriend instancesRespondToSelector:aSelector]) {
            sig = [RichFriend instanceMethodSignatureForSelector:aSelector];
        }
    }
    return sig;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
    if ([RichFriend instancesRespondToSelector:anInvocation.selector])
    {
        [anInvocation invokeWithTarget:RichFriend.new];
    } else {
        [super forwardInvocation:anInvocation];
    }
}
 

 
//初始化
Man *people = [[Man alloc] init];
[people makeSomeAirplane:@"A380"];
如果第四和第五部都没法解决问题,那么只能是最后一步进行NSInVocation进行转发,NSInvocation是一个包含receiver,selector,parameter 等的一个封装类,把所有相关的信息进行打包处理。首先需要通过methodSignatureForSelector:方法获得selector的方法签名建立NSInvocation对象,这个对象可以通过invokeWithTarget:方法驱动新的接受者去进行消息的处理。

如果这一步也没有进行消息的处理,NSObject 的forwardInVocation:就会调用doesNotRecognizeSelector:方法,程序就崩溃啦。

最后上图:



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值