對runtime的總結:讓你會用Runtime

Mahalia25R 8年前發布 | 30K 次閱讀 Objective-C開發 Objective-C

導語

Runtime,簡稱運行時,就是系統在運行的時候的一些機制,其中最主要的是消息機制,是一套底層的純C語言的API,我們平時所編寫的OC代碼,在程序的運行過程中都轉成了runtime的代碼,平時調方法都是轉成了objc_msgSend函數。大家應該都聽過或者用過一個叼庫IQKeyboard,它只需導入進工程,不需要寫一行代碼就能實現功能,其實就是用的runtime機制實現的,俗稱黑魔法。

runtime的作用

Objective-C 的 Runtime 鑄就了它動態語言的特性,這些深層次的知識雖然平時寫代碼用的少一些,但是卻是每個 Objc 程序員需要了解的。

  • 在程序運行過程中,動態的創建一個類(KVO實現原理)以及動態的為每個類添加或修改屬性、方法
  • 遍歷一個類中所有的成員變量以及方法(NSCoding的自動解檔和歸檔、MJExtension的實現)
  • 交換兩個方法的實現(Swizzle黑魔法)
  • ...

KVO實現原理

當你第一次觀察某個對象時,runtime會創建一個新的類NSKVONOtifying_class,該類繼承自原先的class。在這個新的派生類中,它重寫了所有被觀察屬性的setter方法,然后將對象的isa指針指向新創建的NSKVONOtifying_class(這個指針告訴Objective-C的runtime某個對象到底是哪種類型的對象)。所以該對象神奇地變成了新的子類的實例。當對象的屬性發生改變時,會觸發setter方法,但這個方法已經被重寫了,并且在方法的內部添加了發送通知機制,實現了自動觸發通知機制,這就是KVO實現的原理。

交換兩個方法的實現

先看下面例子:

創建一個Student類,實現兩個類方法:studyJava和studyC

使用runtime交換兩個方法的實現:

#import<objc/runtime.h>//別忘加運行時頭文件
    //獲取class中的類方法
    Method studyJava = class_getClassMethod([Student class], @selector(studyJava));
    Method studyC = class_getClassMethod([Student class], @selector(studyC));
    //交換方法的實現
    method_exchangeImplementations(studyJava, studyC);

[Student studyC];
[Student studyJava];</code></pre> 

運行后會發現:讓他先學C再學Java就是不聽,結果是先學Java。

看到這你會覺得并沒有什么卵用,是的,這樣寫意義不大,但我們換個角度想,如果交換的是系統的方法呢?假如我們要統一監聽控制器何時被銷毀,用runtime可以讓我們只寫一次代碼,看例子:

首先新建一個UIViewController的分類,然后在里面寫上代碼:

#import "UIViewController+Extension.h"
#import <objc/runtime.h>

@implementation UIViewController (Extension)
//將交換方法寫在load方法中,使得在該類被加載如內存中就調用該方法以交換方法
+ (void)load {
    //class_getInstanceMethod :獲取實例方法
    Method dealloc = class_getInstanceMethod(self, NSSelectorFromString(@"dealloc"));
    Method jyh_dealloc = class_getInstanceMethod(self, @selector(jyh_dealloc));
    method_exchangeImplementations(dealloc, jyh_dealloc);
}

//自己實現的dealloc方法,監聽銷毀
- (void)jyh_dealloc {
    NSLog(@"%@ is dealloc", self);
    [self jyh_dealloc];//調用原來的dealloc方法
}

@end

這樣便實現了所用控制器銷毀的監聽。以上的例子只是為了說明runtime的作用,它可以有很多擴展,比如:將[UIImage imageNamed:]換成自己的方法,在方法中加入對圖片名稱的操作,就可以實現換膚或適配等功能;可以替換viewDidLoad方法實現用戶進入頁面的統計,不用每個控制器都復制粘貼同樣的代碼(或許你會想我們可以使用 OOP 的特性之一,繼承的方式來解決這個問題。創建一個基類,在這個基類中添加統計方法,其他類都繼承自這個基類。然而,這種方式修改還是很大,而且定制性很差。以后有新人加入之后,都要囑咐其繼承自這個基類,所以這種方式并不可取。)IQKeyboard庫不需要寫一行代碼就能實現功能,就是通過這一原理實現的。

遍歷類中的所有成員變量

有時候我們要對一些信息進行歸檔,如用戶信息類UserInfo,這將需要重寫initWithCoder和encodeWithCoder方法,并對每個屬性進行encode和decode操作。那么問題來了:當屬性只有幾個的時候可以輕松寫完,如果有幾十個屬性呢?我們可以 用runtime提供的函數遍歷Model自身所有屬性,并對屬性進行encode和decode操作。

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if(self = [super init]) {
        unsigned int outCount;
        //獲得所傳入類的成員變量
        Ivar *ivars = class_copyIvarList([self class], &outCount);
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            [self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
        }
        free(ivars);//別忘記釋放
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    unsigned int outCount;
    Ivar *ivars = class_copyIvarList([self class], &outCount);
    for (int i = 0; i < outCount; i++) {
        Ivar ivar = ivars[i];
        NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
        [aCoder encodeObject:[self valueForKey:key] forKey:key];
    }
    free(ivars);
}

同理,字典轉模型用的也是這種方法。

訪問蘋果私有屬性

利用runtime獲取屬性名

objc_property_t *properties = class_copyPropertyList([UITextField class], &count);

這樣就可以獲取到UITextField中的所有屬性,包括在頭文件中蘋果不想讓你看到的。

這有什么用呢?假設我們有一個需求,想改變輸入框中占位文字的顏色,用runtime的這個作用可以讓我們很方便的實現:

先將輸入框所包含的屬性打印出來:

+ (void)getProperties
{
    unsigned int count = 0;

    objc_property_t *properties = class_copyPropertyList([UITextField class], &count);

    for (int i = 0; i<count; i++) {
        // 取出屬性
        objc_property_t property = properties[i];

        // 打印屬性名字
        JYHLog(@"%s   <---->   %s", property_getName(property), property_getAttributes(property));
    }

    free(properties);
}

我們可以看到這樣一個屬性:

查看私有屬性

顧名思義,這就是占位的Label,我們只要利用KVC就可以將其改成我們想要的樣子:

[self setValue:[UIColor whiteColor] forKeyPath:@"_placeholderLabel.textColor"];

以上只是舉一些例子來窺探runtime的一些作用,可以有很多擴展,還需我們繼續去學習。

如果文章對你有用的話請點下喜歡或關注,Thank You!

 

來自:http://www.jianshu.com/p/2e63a4d9b7ca

 

 本文由用戶 Mahalia25R 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!