iOS進階之runtime作用

圖片來源于網絡

前言

Runtime基本是用C和匯編寫的,可見蘋果為了動態(tài)系統(tǒng)的高效而作出的努力。你可以在這里下到蘋果維護的開源代碼。蘋果和GNU各自維護一個開源的runtime版本,這兩個版本之間都在努力的保持一致。Objective-C 從三種不同的層級上與 Runtime 系統(tǒng)進行交互,分別是通過 Objective-C 源代碼通過 Foundation 框架的NSObject類定義的方法通過對 runtime 函數(shù)的直接調用。大部分情況下你就只管寫你的Objc代碼就行,runtime 系統(tǒng)自動在幕后辛勤勞作著。

1、概念

RunTime簡稱運行時,就是系統(tǒng)在運行的時候的一些機制,其中最主要的是消息機制。

??、ios進階之傳遞消息
??
2、作用

1.動態(tài)交換兩個方法的實現(xiàn)
2.為類別添加屬性(我們知道類別是不能擴展屬性的,只能擴展方法,但可以運行時實現(xiàn),通過為類增加屬性)
3.獲取某個類的所有成員變量和成員方法
4.實現(xiàn)NSCoding的自動歸檔和自動解檔
5.動態(tài)添加對象的成本變量和成員方法
作用:當硬件內存過小的時候,如果我們將每個方法都直接加到內存當中去,但是幾百年不用一次,這樣就造成了浪費,所有采取動態(tài)添加
6.實現(xiàn)字典和模型的自動轉換

3、詳解:

一、動態(tài)交換方法

1.在自定義類DWExchangeTwoMethod.m中

- (instancetype)init {
    if (self = [super init]) {
        Method workMethod = class_getInstanceMethod([DWExchangeTwoMethod class], @selector(work));
        Method playMethod = class_getClassMethod([DWExchangeTwoMethod class], @selector(play));
        method_exchangeImplementations(workMethod, playMethod);
    }
    return self;
}

- (void)play {
    NSLog(@"玩...");
}

+ (void)work {
    NSLog(@"工作...");
}

拋磚引玉:可以交換方法,防止數(shù)組越界導致的崩潰

2、然后在其他類調用

    DWExchangeTwoMethod *exchangeMethod = [DWExchangeTwoMethod new];
    [exchangeMethod play];

由上可以看出,調用的是play方法
但打印結果為 【玩...】,證明已經動態(tài)交換成功

二、為分類添加屬性

一般情況下,分類不可以添加屬性,但用runtime卻可以實現(xiàn)

- (NSString *)height {
    return objc_getAssociatedObject(self, @"height");
}

- (void)setHeight:(NSString *)height {
    objc_setAssociatedObject(self, @"height", height, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

注:
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
id object :表示關聯(lián)者,是一個對象,變量名理所當然也是object
const void *key :獲取被關聯(lián)者的索引key
id value :被關聯(lián)者,這里是一個block
objc_AssociationPolicy policy : 關聯(lián)時采用的協(xié)議,有assign,retain,copy等協(xié)議,一般使用OBJC_ASSOCIATION_RETAIN_NONATOMIC

objc_getAssociatedObject(id object, const void *key)
參數(shù)解析同上

三、獲取實例變量列表
    unsigned int outCount = 0;
    Ivar *ivars = class_copyIvarList([DWExchangeTwoMethod class], &outCount);
    for (int i = 0; i < outCount; i++) {
        Ivar ivar = ivars[i];
        const char* name = ivar_getName(ivar);
        const char* type = ivar_getTypeEncoding(ivar);        
        NSLog(@"變量名%s :%s", name, type);
    }
//    此時要注意, 盡管在ARC模式下, 取出變量后要依然手動釋放內存, 利用free()方法即可:
    free(ivars);

注:
class_copyPropertyList與class_copyIvarList
class_copyPropertyList返回的僅僅是對象類的屬性(@property申明的屬性),而class_copyIvarList返回類的所有屬性和變量(包括在@interface大括號中聲明的變量)

四、實現(xiàn)NSCoding的自動歸檔和自動解檔;

(不用對每個屬性edcode和decode了,如果幾十個屬性一個個的encode和decode真的很麻煩啊,使用運行時可以遍歷出每個對象的屬性,數(shù)組的方式遍歷eccode,decode)

#import "DWPerson.h"

#define kname           @"name"
#define kidCard         @"idCard"
#define kage            @"age"
#define kothers         @"others"

@implementation DWPerson

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:_name forKey:kname];
    [aCoder encodeObject:_idCard forKey:kidCard];
    [aCoder encodeInt:_age forKey:kage];
    [aCoder encodeObject:_others forKey:kothers];
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        _name = [aDecoder decodeObjectForKey:kname];
        _age = [aDecoder decodeIntForKey:kage];
        _idCard = [aDecoder decodeObjectForKey:kidCard];
        _others = [aDecoder decodeObjectForKey:kothers];
    }
    return self;
}

@end

上面每個類都要寫一次,所以我們添加個分類會更加簡化

#import "NSObject+ArchiveExtension.h"
#import <objc/runtime.h>

@implementation NSObject (ArchiveExtension)

// 先對當前類進行編碼,然后對父類進行編碼,如果父類是NSObject就結束編碼
- (void)encode:(NSCoder *)aCoder {
    // 一層層父類往上查找,對父類的屬性執(zhí)行歸解檔方法
    Class c = self.class;
    while (c && c != [NSObject class]) {
        unsigned int outCount = 0;
        Ivar *ivars = class_copyIvarList(c, &outCount);
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            id value = [self valueForKey:key];
            [aCoder encodeObject:value forKey:key];
        }
        
        free(ivars);
        c = [c superclass];
    }
}

- (void)decode:(NSCoder *)aDecoder {
    Class c = self.class;
    while (c && c != [NSObject class]) {
        unsigned int outCount = 0;
        Ivar *ivars  = class_copyIvarList(c, &outCount);
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            //注意與歸檔的順序不同
            id value = [aDecoder decodeObjectForKey:key];
            [self setValue:value forKey:key];
        }
        free(ivars);
        c = [c superclass];
    }
}

@end

這時,可以在DWPerson.m中取代之前的代碼

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [self encode:aCoder];
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        [self decode:aDecoder];
    }
    return self;
}

當然還可以更加簡潔,把NSCoding的定義成宏

#ifndef Archive_h
#define Archive_h

#import "NSObject+ArchiveExtension.h"


#define ArchiveImplemention \
\
- (instancetype)initWithCoder:(NSCoder *)aDecoder {\
    if (self = [super init]) {\
        [self decode:aDecoder];\
    }\
    return self;\
}\
\
- (void)encodeWithCoder:(NSCoder *)aCoder {\
    [self encode:aCoder];\
}  //最后這句不用斜線
#endif /* Archive_h */

注:
如果有空行,需要加斜線補充

使用:

//宏定義
#import "Archive.h"

@implementation DWPerson

ArchiveImplemention

@end

五、動態(tài)添加方法

在viewController使用

- (IBAction)clickBtn:(id)sender {
    
    [self performSelector:@selector(play)];
}

void play(id self, SEL _cmd) {
    NSLog(@"動態(tài)添加成功");
    UIViewController *vc = [UIViewController new];
    vc.view.backgroundColor = [UIColor redColor];
    [self presentViewController:vc animated:YES completion:nil];
}

+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
    if (aSEL ==  NSSelectorFromString(@"play")) {
        class_addMethod(self, aSEL, (IMP) play, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:aSEL];
}

六、實現(xiàn)字典和模型的自動轉換

獲取屬性的列表的方法是字典轉模型的比較核心的方法

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 對于從事 iOS 開發(fā)人員來說,所有的人都會答出【runtime 是運行時】什么情況下用runtime?大部分人能...
    夢夜繁星閱讀 3,734評論 7 64
  • 轉至元數(shù)據(jù)結尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,789評論 0 9
  • 注:以下圖片素材皆來自于網絡。 上周日,《歡樂喜劇人》第二季拉下了帷幕,小岳岳更是在萬眾矚目與期盼下,帶著一幫德云...
    娛樂圈老王閱讀 420評論 0 0
  • 曾經夢幻過的種種山景特色,溪水湍流、樹木綠蔭,瀑布銀練,也許還有更令人嘆為觀止的景色,就這樣一下子夢斷了。我是出奇...
    鄧文偉閱讀 1,028評論 0 0
  • 腦海里浮現(xiàn)出一個畫面,在鄉(xiāng)下的柏油路上,在一輛有些破舊的班車里,一個小女孩坐在靠著窗的位置,望著窗外的風景,發(fā)呆。...
    好魚閱讀 297評論 1 2