runtime的作用及實例

什么是runtime

runtime是底層的純C語言的API,它包含的很多底層的語法。
我們平時編寫的OC代碼,在程序運行過程中,最終都是轉(zhuǎn)化成了runtime的C語言代碼,
runtime也稱為運行時,它是OC的幕后工作者。

runtime的作用

runtime主要就是做一些底層的操作,如:
   1. 動態(tài)的添加對象的成員變量和方法
   2.動態(tài)交換兩個方法的實現(xiàn)(可以替換系統(tǒng)的方法)
   3.獲得某個類的所有成員方法、所有成員變量
   4. 實現(xiàn)分類也可以添加屬性
   5.實現(xiàn)NSCoding的自動歸檔和解檔
   6.實現(xiàn)字典轉(zhuǎn)模型的自動轉(zhuǎn)換

替換系統(tǒng)方法,可以通過攔截系統(tǒng)的方法探究底層,比如block 的實現(xiàn)原理

常用方法

1.獲取類中的方法

Method class_getClassMethod(Method cls , SEL name)

如:

Method m = class_getClassMethod([Person class],@selector(setName:));

2.獲取對象中的方法

Method class_getInstanceMethod(Method cls, SEL name)

如:

Person *person = [[Person alloc] init];
Method m = get_InstanceMethod([person class],@selector(setName:));

3.交換兩個方法的實現(xiàn)

 void method_exchangeImplementations(Method m1,Method m2)

     Person *p =[[Person alloc] init];
    [p study];
    [p run];
    //交換實現(xiàn)
    //instance method :實例方法,
    //class_getInstanceMethod得到實例的方法(即對象方法)
    //兩個參數(shù) 1:類名 2.方法名
    //class_getClassMethod :得到實例化的方法
    Method m1 = class_getInstanceMethod([Person class], @selector(study));
    Method m2 = class_getInstanceMethod([Person class], @selector(run));
    method_exchangeImplementations(m2, m1);
    [p study];
    [p run];

具體操作


051B23EE-AFC6-4C95-9297-1E58708D5B96.png

4.獲取成員變量

Ivar  *ivars = class_getCopyIvarList(Ivar ivar);

實現(xiàn)分類中添加屬性

為所有的NSObject對象添加屬性
1.首先創(chuàng)建一個NSObject分類NSObject+Extension
2.在.h中使用@property添加屬性

此時使用@property添加數(shù)屬性,并非真正的屬性,如果此時調(diào)用查看屬性,將會崩潰,
因為分類并未實現(xiàn)添加添加屬性的功能,想要添加屬性,需要使用runtime,動態(tài)的添加

3.在.m文件中實現(xiàn)getter和setter方法

如果想要添加多個屬性,就需要在每個對象中抽出一塊空間用于存放屬性,
使用objc_setAssociatedObject方法進行關(guān)聯(lián)

#import "NSObject+Extension.h"
#import <objc/runtime.h>
@implementation NSObject (Extension)
//用于存放屬性的變量,多個屬性,需要創(chuàng)建不同的變量
char BookKey;
-(void)setBooks:(NSArray *)books{
objc_setAssociatedObject(self, &BookKey, books, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSArray *)books{
return objc_getAssociatedObject(self, &BookKey);
}
@end

遵守協(xié)議NSCoding,實現(xiàn)屬性的自動歸檔與解檔

需求分析:
當(dāng)想要對象自動進行歸檔解檔的時候,如果屬性非常的多,一個一個天添加[encoder encodeObject:@(xxx) forKey:@"_xxx"];將會非常的繁瑣。
既然能夠獲取所有的屬性,我們就可以通過循環(huán)遍歷屬性的方式進行統(tǒng)一的歸檔和解檔

- (id)initWithCoder:(NSCoder *)decoder
{
if (self = [super init]) {
    // 用來存儲成員變量的數(shù)量
    unsigned int outCount = 0;
    
    // 獲得Dog類的所有成員變量
    Ivar *ivars = class_copyIvarList([self class], &outCount);
    
    // 遍歷所有的成員變量
    for (int i = 0; i<outCount; i++) {
        // 取出i位置對應(yīng)的成員變量
        Ivar ivar = ivars[i];
        
        NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
        // 獲得key對應(yīng)的值
        id value = [decoder decodeObjectForKey:key];
        
        // 設(shè)置到成員變量上
        [self setValue:value forKeyPath:key];
    }
    
    free(ivars);
}
return self;
}

/**
 * 將對象寫入文件時會調(diào)用這個方法(開發(fā)者需要在這個方法中說明需要存儲哪些屬性)
 */
- (void)encodeWithCoder:(NSCoder *)encoder
{
// 用來存儲成員變量的數(shù)量
unsigned int outCount = 0;

// 獲得Dog類的所有成員變量
Ivar *ivars = class_copyIvarList([self class], &outCount);

// 遍歷所有的成員變量
for (int i = 0; i<outCount; i++) {
    // 取出i位置對應(yīng)的成員變量
    Ivar ivar = ivars[i];
    
    NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
    // 通過key獲得對應(yīng)成員變量的值
    id value = [self valueForKeyPath:key];
    
    [encoder encodeObject:value forKey:key];
}

free(ivars);
}

注意:

ARC的內(nèi)存管理機制 只適合OC語法,對于C語言的內(nèi)存還是需要手動的釋放,當(dāng)使用runtime的時候,
如果包含了copy、create、retain、new等詞語,那么在最后就需要釋放內(nèi)存

使用free(對象)進行釋放如:free(ivars);

利用runtime實現(xiàn)字典轉(zhuǎn)模型

描述:

KVC的字典轉(zhuǎn)模型具有一個缺陷,就是屬性的數(shù)量與名稱都必須保持一致,如果字典中的屬性多,
而模型中沒有使用KVC賦值的時候就會崩潰,需要實現(xiàn)另一個方法 
setValue:forUndefinedKey:方法,并如果對象中包含了另一個對象作為屬性,
也將不能自動將其轉(zhuǎn)化為模型

而使用runtime實現(xiàn)的字典轉(zhuǎn)模型,可以實現(xiàn)將所有的對象都轉(zhuǎn)化為對應(yīng)的模型,
并且不會出現(xiàn)屬性找不到,而奔潰的現(xiàn)象

NSObject+Extension.h
#import <Foundation/Foundation.h>
@interface NSObject (Extension)
-(void)setDiction:(NSDictionary *)dict;
+(instancetype)objectWithDiction:(NSDictionary *)dict;
@end

NSObject+Extension.m

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

@implementation NSObject (Extension)

-(void)setDiction:(NSDictionary *)dict{
//獲取類
Class c = self.class;
//循環(huán)遍歷 類 (本類 和所有的父類)
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)];
        //去掉key中的 _
        key = [key substringFromIndex:1];
        //通過key 獲取屬性的值
        id value = dict[key];
        //如果key是一個空值 退出本輪的循環(huán)
        //原因:如果字典中沒有這個key,那么value將會是一個空值,kvc 不能賦值空值
        if (value == nil) {
            continue;
        }
        //如果類中包含另一個類為對象,也要將該對象進行字典轉(zhuǎn)模型
        //獲取對象的屬性的類名
        NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        // 對象名會以@“名字”的形式 出現(xiàn),但是同時字符串也是以這種形式表示,因此可以先判斷type中是否包含 @ 符號
        NSRange range = [type rangeOfString:@"@"];
        //如果range.location 不等于NSNotFound說明 找到了@
        if (range.location != NSNotFound) {
            //截取type中的名字 去除@“ ”
            type = [type substringWithRange:NSMakeRange(2, type.length -3)];
            if (![type hasPrefix:@"NS"]) {
                //將type轉(zhuǎn)化為類名
                Class class = NSClassFromString(type);
                value = [class objectWithDiction:value];
            }
            
        }
        
        //賦值
        [self setValue:value forKey:key];
        
    }
    //ARC 只適用于OC語法,C語言中的內(nèi)存 需要手動釋放
    free(ivars);
    c = [c superclass];
    NSLog(@"1");
  }
}
+(instancetype)objectWithDiction:(NSDictionary *)dict{
NSObject *obj = [[self alloc] init];
[obj setDiction:dict];
return  obj;
}  
@end
最后編輯于
?著作權(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)容