iOS中類找不到方法時(shí)消息處理機(jī)制

如果我們調(diào)用了一個(gè)類沒(méi)有的方法,就會(huì)進(jìn)入消息處理機(jī)制,有下面幾個(gè)階段

  1. “動(dòng)態(tài)方法解析”:
    + (BOOL)resolveClassMethod:(SEL)sel;
    + (BOOL)resolveInstanceMethod:(SEL)sel;
    征詢接收者所屬的類,是否需要?jiǎng)討B(tài)添加類方法或?qū)嵗椒ǎ瑏?lái)處理這個(gè)未找到的方法。
    1). 首先判斷是否實(shí)現(xiàn)了 resolveInstanceMethod,如果沒(méi)有實(shí)現(xiàn),進(jìn)入下一步處理;
    2). 如果實(shí)現(xiàn)了,調(diào)用 resolveInstanceMethod,獲取返回值;
    3). 如果返回值為 YES,表示 resolveInstanceMethod 聲稱它已經(jīng)提供了 selector 的實(shí)現(xiàn),因此再次查找 method list,如果找到對(duì)應(yīng)的 IMP,則返回該實(shí)現(xiàn),否則提示警告信息,進(jìn)入下一步處理;
    4). 如果返回值為 NO,進(jìn)入下一步處理;

  2. “重定向”:
    - (id)forwardingTargetForSelector:(SEL)aSelector;
    如果沒(méi)有動(dòng)態(tài)添加方法,則會(huì)進(jìn)入此階段,此時(shí)詢問(wèn)是否要將這條消息轉(zhuǎn)發(fā)給其他的對(duì)象,來(lái)處理這個(gè)方法。如果返回nil,即表示不轉(zhuǎn)發(fā)給其他對(duì)象,此時(shí)會(huì)進(jìn)入第3階段

  3. “消息轉(zhuǎn)發(fā)”:
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
    - (void)forwardInvocation:(NSInvocation *)anInvocation;
    此時(shí)系統(tǒng)會(huì)根據(jù)SEL詢問(wèn)方法簽名,即調(diào)用methodSignatureForSelector:方法獲取方法簽名,如果這個(gè)方法返回nil,那么就會(huì)看到我們最常見(jiàn)的一種crash -[Class xxx]: unrecognized selector sent to instance ...
    如果這個(gè)方法返回了正確的方法簽名,系統(tǒng)就會(huì)根據(jù)消息的SEL、target、參數(shù)、方法簽名,把消息封裝成一個(gè)NSInvocation對(duì)象,我們可以拿到這個(gè)NSInvocation對(duì)象,指定對(duì)應(yīng)的target調(diào)用方法。
    這里可以不調(diào)用Invocation,并不會(huì)觸發(fā)crash

1.動(dòng)態(tài)解析

下面我們通過(guò)一個(gè)demo來(lái)理解下動(dòng)態(tài)解析,新建一個(gè)Person的類

//Person.h
@interface Person : NSObject
//在.m文件中添加了@dynamic,所以不會(huì)自動(dòng)添加setter和getter方法
@property(assign,nonatomic)NSInteger weight;
@end

//Person.m
@implementation Person
@dynamic weight;

void setPropertyDynamic(id self,SEL _cmd){    
    NSLog(@"This is a dynamic method added for Person instance");
}

+ (BOOL)resolveInstanceMethod:(SEL)sel{    
    if (sel == @selector(setWeight:)){
        class_addMethod([self class], sel,(IMP)setPropertyDynamic, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
@end

// main函數(shù)中調(diào)用
Person *p = [[Person alloc]init] ;
p.weight = 100;

上面的例子,會(huì)打印出log:This is a dynamic method added for Person instance

2.重定向

再來(lái)通過(guò)demo理解重定向,Person類和上面一樣,再建一個(gè)People類

//Person.m

// Person類重寫(xiě)重定向方法,將weight的getter方法重定向到People類,其他方法不處理
- (id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(weight)) {
        People *people = [People new];
        return people;
    }
    id target = [super forwardingTargetForSelector:aSelector];
    return target;
}

//People.h
@interface People : NSObject
@end

//People.m
@implementation People
- (NSInteger)weight {
    return 666;
}
@end

// main函數(shù)中調(diào)用
Person *person = [[Person alloc] init] ;
NSInteger weightValue = person.weight;
NSLog(@"%ld",weightValue); //666

上面的例子,會(huì)打印出log:666

3.消息轉(zhuǎn)發(fā)

再來(lái)通過(guò)demo理解消息轉(zhuǎn)發(fā),Person類、People類和上面一樣

//Person.h
//在.m文件中添加了@dynamic,所以下面的屬性不會(huì)自動(dòng)添加setter和getter方法
@property(copy,nonatomic)NSString *identifier;


//Person.m
@dynamic identifier;

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    if (aSelector == @selector(setIdentifier:) || aSelector == @selector(identifier) ) {
        NSMethodSignature *sign = [People instanceMethodSignatureForSelector:aSelector];
        return sign;
    }
    return nil;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
    People *people = [[People alloc]init];
    if ([people respondsToSelector:[anInvocation selector]]) {
        [anInvocation invokeWithTarget:people];
    }else{
        [super forwardInvocation:anInvocation];
    }
}


//People.h
@property(copy,nonatomic)NSString *identifier;


// main函數(shù)中調(diào)用
Person *person = [[Person alloc] init];
person.identifier = @"yzk whlpkk";
NSLog(@"%@",person.identifier); //null

上面的例子,會(huì)打印出log:(null),之所以會(huì)這樣,是因?yàn)槲覀兿⑥D(zhuǎn)發(fā)的時(shí)候,每次都實(shí)例化了一個(gè)新的people,所有我們setter的people和getter的people并不是同一個(gè)實(shí)例。

再來(lái)說(shuō)一下消息轉(zhuǎn)發(fā)和重定向的區(qū)別。

  1. 重定向只能重定向到一個(gè)對(duì)象,但是消息轉(zhuǎn)發(fā),可以同時(shí)對(duì)多個(gè)對(duì)象轉(zhuǎn)發(fā),只需要[anInvocation invokeWithTarget:]多個(gè)target即可。
  2. 重定向的target必須要有一個(gè),如果是nil,則target就是當(dāng)前實(shí)例。消息轉(zhuǎn)發(fā)
    可以不轉(zhuǎn)發(fā),即不調(diào)用[anInvocation invokeWithTarget:],不會(huì)crash,但是消息轉(zhuǎn)發(fā)的methodSignatureForSelector:方法簽名不能返回nil,否則會(huì)crash。

最后附上demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容