你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 【投稿】利用Runtime 實現自動化歸檔

【投稿】利用Runtime 實現自動化歸檔

編輯:IOS開發基礎

本文是投稿文章,作者:鄭欽洪_(簡書)


1.前言

對於對象的歸檔,之前使用MJ老師的MJExtension框架做字典轉模型的時候,直接使用宏MJCodingImplementation就能實現對象自動實現存檔和解檔的方法,很是好用。但是有時候會遇到不需要用到字典轉模型,不想使用框架時,就需要自己手動一個一個實現,有時候屬性一多了,簡單卻繁瑣的相同代碼會讓人覺得有點不耐煩。剛好看到袁峥老師的文章《讓你快速上手Runtime》,於是自己想動手來實現用runtime,實現自動化歸檔,整個項目的代碼 GitHub。

2.對象歸檔

以下是一個可以跳過的步驟,簡單的為自己回顧了實現對象歸檔的原始方法,創建一個類叫做學生類,其中學生有name,和sex屬性。

/** name */
@property (nonatomic,copy) NSString *name;
/** sex */
@property (nonatomic,copy) NSString *sex;

我們要對student類的對象實現歸檔的時候,需要做以下的步驟

  • 類的頭文件遵守

@interface StudentModel : NSObject
  • 在類的.m文件中,實現以下方法

 // 存檔的時候需要實現
 - (void)encodeWithCoder:(NSCoder *)aCoder{
 [aCoder encodeObject:self.name forKey:@"name"];
 [aCoder encodeObject:self.sex forKey:@"sex"];
 }
 
 // 解檔的時候需要實現
 - (id)initWithCoder:(NSCoder *)aDecoder{
 if (self = [super init]) {
     self.name = [aDecoder decodeObjectForKey:@"name"];
     self.sex  = [aDecoder decodeObjectForKey:@"sex"];
 }
     return self;
 }

在需要存檔和從文件中解析存檔的對象實現

// 將student對象歸檔到file中
[NSKeyedArchiver archiveRootObject:student toFile:file]

// 從file存檔中解析對象到student中
Student *student = [NSKeyedUnarchiver unarchiveObjectWithFile:file];

這樣,只需要這三個步驟我們就簡單的實現了歸檔。看起來好像歸檔的過程並不麻煩,簡簡單單幾句話就能實現了,為何需要用到運行時呢? 在這個例子中,我們的類只有2個屬性:name和sex,於是我們在存檔和解檔的時候分別對這兩個屬性進行了處理,萬一這個類的屬性很多呢?! 請看下面這張圖

677383-f75da480b921ae7c.png

坑爹啊,這後台返回的參數,一個個屬性實現歸檔,都快把我寫哭了!於是想要找到一種偷懶的方式要實現自動化歸檔,於是這時候,Runtime就派上用場了!

3.Runtime的使用

首先,我們需要利用運行時為我們做到的一步是,通過運行時,獲取類中的所有成員屬性,這裡用到了運行時的方法。

OBJC_EXPORT Ivar *class_copyIvarList(Class cls, unsigned int *outCount)

這裡引用了袁峥老師的注釋來加強對這一方法的認識

// Ivar:成員屬性的意思
// 第一個參數:表示獲取哪個類中的成員屬性
// 第二個參數:表示這個類有多少成員屬性,傳入一個Int變量地址,會自動給這個變量賦值
// 返回值Ivar *:指的是一個ivar數組,會把所有成員屬性放在一個數組中,通過返回的數組就能全部獲取到

於是我們需要寫一個方法,來返回一個數組類中的所有屬性名稱

// 返回self的所有對象名稱
+ (NSArray *)propertyOfSelf{
    unsigned int count;
    
// 1. 獲得類中的所有成員變量
Ivar *ivarList = class_copyIvarList(self, &count);

NSMutableArray *properNames =[NSMutableArray array];
for (int i = 0; i < count; i++) {
    Ivar ivar = ivarList[i];
    
    // 2.獲得成員屬性名
    NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
    
    // 3.除去下劃線,從第一個角標開始截取
    NSString *key = [name substringFromIndex:1];
    
    [properNames addObject:key];
}

return [properNames copy];
}

在這裡,我們已經獲取到類中的所有屬性的名稱,接著我們需要在encodeWithCoder對屬性名稱和屬性的值進行歸檔操作,在這裡我們遇到了一個問題,如何把屬性名稱和屬性的值對應起來呢。

在這裡我們需要知道NSStringFromSelector(方法名)返回的是一個SEL變量指向方法名中的方法

我們注意到每個屬性都有兩個共同的方法那就是set方法和get方法,那麼我們只需要通過屬性名字,創建屬性名指向的方法也就是get方法,就能獲取到屬性名對應的值。

// 歸檔
- (void)encodeWithCoder:(NSCoder *)enCoder{
// 取得所有成員變量名
NSArray *properNames = [[self class] propertyOfSelf];

for (NSString *propertyName in properNames) {
    // 創建指向get方法
    SEL getSel = NSSelectorFromString(propertyName);
    // 對每一個屬性實現歸檔
    [enCoder encodeObject:[self performSelector:getSel] forKey:propertyName];
    }
}

接下來,我們需要對類實現解檔方法。這裡我們遇到第二個問題,如何對屬性名的屬性進行賦值呢?這裡我們需要用到屬性的set方法,利用屬性名,拼接出一個set方法的字符串,並創建一個指向屬性set方法的SEL變量,並且利用performSelector實現賦值

// 解檔
- (id)initWithCoder:(NSCoder *)aDecoder{
// 取得所有成員變量名
    NSArray *properNames = [[self class] propertyOfSelf];
    
    for (NSString *propertyName in properNames) {
        // 創建指向屬性的set方法
        // 1.獲取屬性名的第一個字符,變為大寫字母
        NSString *firstCharater = [propertyName substringToIndex:1].uppercaseString;
        // 2.替換掉屬性名的第一個字符為大寫字符,並拼接出set方法的方法名
        NSString *setPropertyName = [NSString stringWithFormat:@"set%@%@:",firstCharater,[propertyName substringFromIndex:1]];
        SEL setSel = NSSelectorFromString(setPropertyName);
        [self performSelector:setSel withObject:[aDecoder decodeObjectForKey:propertyName]];
    }
    return  self;
}

就這樣,我們實現了對一個類實現自動歸檔的類,下次需要創建一個Model類時,只要繼承自我們編寫的這個類,就能實現自動歸檔啦,是不是很輕松呢。其實拿到類的屬性名可以擴展很多內容,例如我們每次打印Model類的時候,都需要一個model裡的屬性都拼接出來,利用我們剛剛寫的代碼,重寫description方法,就能實現在NSLog的時候把對象裡的每個屬性和值都打印出來了

- (NSString *)description{
    NSMutableString *descriptionString = [NSMutableString stringWithFormat:@"\n"];
    // 取得所有成員變量名
    NSArray *properNames = [[self class] propertyOfSelf];
    for (NSString *propertyName in properNames) {
        // 創建指向get方法
            SEL getSel = NSSelectorFromString(propertyName);
            
        NSString *propertyNameString = [NSString stringWithFormat:@"%@ - %@\n",propertyName,[self performSelector:getSel]];
        [descriptionString appendString:propertyNameString];
    }
            return [descriptionString copy];
    }

4.後記

這段時間一直在學Swift和UI動畫,也慢慢有了一些積累,希望在空閒之余可以跟大家分享,也希望能得到各位前輩的指點。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved