Runtime 見聞?wù)?/h2>

Runtime

基本是用C和匯編寫的
Runtime 涉及三個(gè)點(diǎn),面向?qū)ο?,消息分發(fā),消息轉(zhuǎn)發(fā)

面向?qū)ο?/h4>

Objective-C 的對象是基于 Runtime 創(chuàng)建的結(jié)構(gòu)體

image

消息分發(fā)

    // 創(chuàng)建person對象
    Person *p = [[Person alloc] init];
    
    // 調(diào)用對象方法
    [p eat];
    
    // 本質(zhì):讓對象發(fā)送消息
    objc_msgSend(p, @selector(eat));

    // 調(diào)用類方法的方式:兩種
    // 第一種通過類名調(diào)用
    [Person eat];
    // 第二種通過類對象調(diào)用
    [[Person class] eat];
    
    // 用類名調(diào)用類方法,底層會自動把類名轉(zhuǎn)換成類對象調(diào)用
    // 本質(zhì):讓類對象發(fā)送消息
    objc_msgSend([Person class], @selector(eat));

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

完整的消息轉(zhuǎn)發(fā)流程圖:


image
  • 1、動態(tài)方法解析
  • +(BOOL)resolveInstanceMethod:(SEL)sel;

當(dāng)接受到未能識別的SEL時(shí),運(yùn)行時(shí)系統(tǒng)會調(diào)用該函數(shù)用以給對象一次機(jī)會來添加相應(yīng)的方法實(shí)現(xiàn),如果用戶在該函數(shù)中動態(tài)添加了相應(yīng)方法的實(shí)現(xiàn),則跳轉(zhuǎn)到方法的實(shí)現(xiàn)部分,并將該實(shí)現(xiàn)存入緩存中,以供下次調(diào)用。

  • 2、備援接收者
  • -(id)forwardingTargetForSelector:(SEL)aSelector;

如果運(yùn)行時(shí)在消息轉(zhuǎn)發(fā)的第一步中未找到所調(diào)用方法的實(shí)現(xiàn),那么當(dāng)前接收者還有第二次機(jī)會進(jìn)行未知SEL的處理。這時(shí)運(yùn)行期系統(tǒng)會調(diào)用上述方法,并將未知SEL作為參數(shù)傳入,該方法可以返回一個(gè)能處理該選擇子的對象,運(yùn)行時(shí)系統(tǒng)會根據(jù)返回的對象進(jìn)行查找,若找到則跳轉(zhuǎn)到相應(yīng)方法的實(shí)現(xiàn),則消息轉(zhuǎn)發(fā)結(jié)束。

  • 3、完整的消息轉(zhuǎn)發(fā)
  • -(void)forwardInvocation:(NSInvocation *)anInvocation;

當(dāng)運(yùn)行時(shí)系統(tǒng)檢測到第二步中用戶未返回能處理相應(yīng)選擇子的對象時(shí),那么來到這一步就要啟動完整的消息轉(zhuǎn)發(fā)機(jī)制了。該方法可以改變消息調(diào)用目標(biāo),運(yùn)行時(shí)系統(tǒng)根據(jù)所改變的調(diào)用目標(biāo),向調(diào)用目標(biāo)方法列表中查詢對應(yīng)方法的實(shí)現(xiàn)并實(shí)現(xiàn)跳轉(zhuǎn),這種方式和第二步的操作非常相似。當(dāng)然你也可以修改方法的選擇子,亦或者向所調(diào)用方法中追加一個(gè)參數(shù)等來跳轉(zhuǎn)到相關(guān)方法的實(shí)現(xiàn)。

最后,如果消息轉(zhuǎn)發(fā)的第三步還未能處理該未知選擇子的話,那么最終會調(diào)用NSObject類的如下方法用以異常的拋出,表明該選擇子最終未能處理。

  • -(void)doesNotRecognizeSelector:(SEL)aSelector;

常用方式

替換系統(tǒng)方法

獲得某個(gè)類的類方法
Method class_getClassMethod(Class cls , SEL name)
獲得某個(gè)類的實(shí)例對象方法
Method class_getInstanceMethod(Class cls , SEL name)
交換兩個(gè)方法的實(shí)現(xiàn)
void method_exchangeImplementations(Method m1 , Method m2)

攔截系統(tǒng)方法

1.建一個(gè)分類(UIImage+Category)
2.分類中實(shí)現(xiàn)一個(gè)自定義方法,方法中寫要在系統(tǒng)方法中加入的語句

+ (UIImage *)xh_imageNamed:(NSString *)name {
double version = [[UIDevice currentDevice].systemVersion doubleValue];
if (version >= 7.0) {
    // 如果系統(tǒng)版本是7.0以上,使用另外一套文件名結(jié)尾是‘_os7’的扁平化圖片
    name = [name stringByAppendingString:@"_os7"];
}
return [UIImage xh_imageNamed:name];
}

3.分類中重寫UIImage的load方法,實(shí)現(xiàn)方法的交換

+ (void)load {
    // 獲取兩個(gè)類的類方法
    Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
    Method m2 = class_getClassMethod([UIImage class], @selector(xh_imageNamed:));
    // 開始交換方法實(shí)現(xiàn)
    method_exchangeImplementations(m1, m2);
}

實(shí)現(xiàn)分類增加屬性(關(guān)聯(lián)對象)

.h
@property(nonatomic,copy)NSString *name;
.m
static NSString * const nameKey = @"nameKey";
- (void)setName:(NSString *)name {
    // 將某個(gè)值跟某個(gè)對象關(guān)聯(lián)起來,將某個(gè)值存儲到某個(gè)對象中
    objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
 
- (NSString *)name {
    return objc_getAssociatedObject(self, &nameKey);
}

自動歸檔解檔

自定義對象不能用于writeToFile保存,需要用于歸檔來進(jìn)行保存

NSObject+Extension

- (void)encodeWithCoder:(NSCoder *)encoder
{
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([Movie class], &count);

    for (int i = 0; i<count; i++) {
        // 取出i位置對應(yīng)的成員變量
        Ivar ivar = ivars[i];
        // 查看成員變量
        const char *name = ivar_getName(ivar);
        // 歸檔
        NSString *key = [NSString stringWithUTF8String:name];
        id value = [self valueForKey:key];
        [encoder encodeObject:value forKey:key];
    }
    free(ivars);
}

- (id)initWithCoder:(NSCoder *)decoder
{
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList([Movie class], &count);
        for (int i = 0; i<count; i++) {
        // 取出i位置對應(yīng)的成員變量
        Ivar ivar = ivars[i];
        // 查看成員變量
        const char *name = ivar_getName(ivar);
       // 歸檔
       NSString *key = [NSString stringWithUTF8String:name];
      id value = [decoder decodeObjectForKey:key];
       // 設(shè)置到成員變量身上
        [self setValue:value forKey:key];

        }
        free(ivars);
    } 
    return self;
}
@end

字典、Json、Model轉(zhuǎn)換

  //字典數(shù)據(jù)轉(zhuǎn)Model
  
User *user = [[User alloc] init];
    
//獲取當(dāng)前model所有屬性
unsigned int propertiesCount;
objc_property_t *properties = class_copyPropertyList([user class], &propertiesCount);

for (NSUInteger i = 0; i < propertiesCount; i++){
     //獲取property Name
    objc_property_t property = properties[i];
    const char *propertyName = property_getName(property);
    id value = [json valueForKey:@(propertyName)];
    //使用KVC形式進(jìn)行對象賦值
    [user performSelector:@selector(setValue:forKey:) withObject:value withObject:@(propertyName)];
}

上述代碼僅僅可以做到NSDictionary轉(zhuǎn)Model,并且Model不能包含自定義對象
完整代碼可能需要單獨(dú)來介紹---->Runtime 字典、Json、Model(含內(nèi)嵌對象)轉(zhuǎn)換

動態(tài)增加方法

  • 開發(fā)使用場景:如果一個(gè)類方法非常多,加載類到內(nèi)存的時(shí)候也比較耗費(fèi)資源,需要給每個(gè)方法生成映射表,可以使用動態(tài)給某個(gè)類,添加方法解
  • 經(jīng)典面試題:怎么調(diào)用類的私有方法?有沒有使用performSelector,其實(shí)主要想問你有沒有動態(tài)添加過方法。
@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person *p = [[Person alloc] init];
    //還可以調(diào)用私有方法
    [p performSelector:@selector(run:) withObject:@10];
}
@end
#import "Person.h"
#import <objc/message.h>

@implementation Person

// 沒有返回值,也沒有參數(shù) -->  void,(id,SEL)
void aaa(id self, SEL _cmd, NSNumber *meter) {
    NSLog(@"跑了%@", meter); 
}

// 任何方法默認(rèn)都有兩個(gè)隱式參數(shù),self,_cmd(_cmd代表方法編號,打印結(jié)果為當(dāng)前執(zhí)行的方法名)
// 什么時(shí)候調(diào)用:只要一個(gè)對象調(diào)用了一個(gè)未實(shí)現(xiàn)的方法就會調(diào)用這個(gè)方法,進(jìn)行處理
// 作用:動態(tài)添加方法,處理未實(shí)現(xiàn)
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == NSSelectorFromString(@"run:")) {
        //aaa不會生成方法列表
        class_addMethod(self, sel, (IMP)aaa, "v@:@");
       
        return YES;
    }
    
    return [super resolveInstanceMethod:sel];
}

@end

動態(tài)變量控制

在程序中,Person的私有屬性age是10,使用runtime變成了20。

Person *p = [[Person alloc] init];
Ivar *ivar = class_copyIvarList([p class], &count);
const char *varName = ivar_getName(var);
object_setIvar(p, var, @"20");

實(shí)現(xiàn)萬能調(diào)整(Router)

- (void)push:(NSString *)classFromString{

    Class actionClass = NSClassFromString(classFromString);
    id target = [[actionClass alloc] init];

    if(!target){
        NSLog(@"沒有當(dāng)前類");
    }

    if (![(NSObject *) target isKindOfClass:[UIViewController class]]) {
        NSLog(@"當(dāng)前不是UIViewController");
    }

    UINavigationController *navigationCtr = [self getActiveNavigationController];
    if ([navigationCtr isKindOfClass:[UINavigationController class]]) {
        [navigationCtr pushViewController:(UIViewController *)target animated:YES];
    }
}

- (UINavigationController *)getActiveNavigationController
{
    if ([[UIApplication sharedApplication] delegate].window.isKeyWindow)
    {
        UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
        if ([rootViewController isKindOfClass:[UITabBarController class]])
        {
            UINavigationController *navigationController = ((UITabBarController *) rootViewController).selectedViewController;
            return navigationController;
        } else if ([rootViewController isKindOfClass:[UINavigationController class]])
            return (UINavigationController *) rootViewController;
        else
            return nil;

    } else
        return nil;
}

by 有涯sui無涯

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

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,069評論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,892評論 33 466
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡介 Runt...
    樂樂的簡書閱讀 2,249評論 0 9
  • 主要參考鏈接: http://yulingtianxia.com/blog/2014/11/05/objectiv...
    Kevin_Junbaozi閱讀 3,455評論 0 10
  • 煙火璀璨,人生亦奪目。 當(dāng)你漫步在梧桐樹下細(xì)細(xì)地觀賞散落的秋葉,我還在教學(xué)樓上伏窗而望,等待著下課鈴聲的敲響。我們...
    無聊的水母閱讀 647評論 3 6

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