深入剖析OC Runtime(三) Message Forward Demo

深入剖析OC Runtime(三) Message Forward Demo
原文地址

objc_msgSend

objc_msgSend的格式如下:

void objc_msgSend(id self, SEL cmd, parameter...)

OC中所有的調(diào)用方法,屬性賦值,都會轉(zhuǎn)化為上面的C函數(shù)發(fā)送消息。找不到方法時,會走消息轉(zhuǎn)發(fā)機制。

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

先上圖,
消息轉(zhuǎn)發(fā)全流程

消息轉(zhuǎn)發(fā)分為兩大階段。

  • 第一階段先問接收者,所屬的類,看能否動態(tài)添加方法,以處理這個未知的selector,這叫動態(tài)方法解析。
  • 第二階段涉及完整的消息轉(zhuǎn)發(fā)機制。
    如果運行期系統(tǒng)已經(jīng)把第一階段執(zhí)行完了,那接收者自己就無法再以動態(tài)新增方法的手段來響應(yīng)包含該selector的消息了。此時運行期系統(tǒng)會請求接收者以其他手段來處理與消息相關(guān)的方法調(diào)用。這又分為兩步:a.請接發(fā)者看看有沒有其他對象能處理這條消息,若有,則runtime會把消息轉(zhuǎn)給那個對象,轉(zhuǎn)發(fā)過程結(jié)束。若沒備用的接收者,會啟動完整的消息轉(zhuǎn)發(fā)機制,runtime會把與消息有關(guān)的全部細節(jié)都封裝在NSInvocation對象中,再給接收者最后一次機會。

示例

由開發(fā)者來添加屬性的定義,并聲明為@dynamic,此類會自動處理屬性值的存放。
header中:

#import <Foundation/Foundation.h>

@interface SSAutoDictionary : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic, strong) NSNumber *number;
@end

上面定義了幾種數(shù)據(jù)類型。
在實現(xiàn)文件中:

#import "SSAutoDictionary.h"
#import <objc/runtime.h>

@interface SSAutoDictionary()
@property (nonatomic, strong) NSMutableDictionary *backStore;
@end

@implementation SSAutoDictionary

@dynamic name, date, number;

- (id)init {
    if (self = [super init]) {
        _backStore = [NSMutableDictionary new];
    }
    return self;
}

假如只這樣寫,從外部訪問屬性的set和get方法時,都會找不到方法,所以引入了resolveInstanceMethod:

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSString *selectorString = NSStringFromSelector(sel);
    if ([selectorString hasPrefix:@"set"]) {
        class_addMethod(self, sel, (IMP)autoDictionarySetter, "v@:@");
    } else {
        class_addMethod(self, sel, (IMP)autoDictionaryGetter, "@@:");
    }
    return YES;
}

用前綴斷定是否為set,兩種情況下,都向類中新增一個處理該selector的子方法,這兩個方法以函數(shù)指針形式出現(xiàn)autoDictionarySetter和autoDictionaryGetter。Getter方法:

id autoDictionaryGetter(id self, SEL _cmd) {
    SSAutoDictionary *mSelf = (SSAutoDictionary *)self;
    NSString *key = NSStringFromSelector(_cmd);
    return [mSelf.backStore objectForKey:key];
}

setter方法:

void autoDictionarySetter(id self, SEL _cmd, id value) {
    SSAutoDictionary *mSelf = (SSAutoDictionary *)self;
    NSString *selectorString = NSStringFromSelector(_cmd);
    NSMutableString *key = [selectorString mutableCopy];
    [key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)];
    [key deleteCharactersInRange:NSMakeRange(0, 3)];
    NSString *lowercaseFirstChar = [[key substringToIndex:1] lowercaseString];
    [key replaceCharactersInRange:NSMakeRange(0, 1) withString:lowercaseFirstChar];
    if (value) {
        [mSelf.backStore setObject:value forKey:key];
    } else {
        [mSelf.backStore removeObjectForKey:key];
    }
}

使用的時候:

    SSAutoDictionary *dict = [SSAutoDictionary new];
    dict.date = [NSDate new];
    NSLog(@"dict.date = %@", dict.date);

輸出:

RuntimeDemo[61043:9339954] dict.date = 2017-07-25 13:19:22 +0000

源碼地址

Demo:https://github.com/dulingkang/Runtime

原文地址

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

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

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