IOS開發(fā)之NSCoding協(xié)議(使用runtime)

近期學(xué)習(xí)IOS的runtime庫(kù)粉私,然后看到之前寫的NSCoding協(xié)議有點(diǎn)復(fù)雜绷旗,如果屬性少還好,如果100多個(gè)屬性际乘,則會(huì)顯得麻煩坡倔。下面使用常規(guī)方式和使用Runtime兩種方式進(jìn)行比較,然后總結(jié)一下中間遇到的坑脖含。

1.常規(guī)方法做歸檔與解檔

//自定義Person類繼承自NSObject

.h文件
@interface Person : NSObject<NSCoding>
@property(nonatomic,strong) NSString * name;//名字
@property(nonatomic,strong) NSString * gender;//性別
@property(nonatomic,strong) NSString * address;//地址
@property(nonatomic) NSUInteger age;//年齡
-(instancetype)initWithName:(NSString*)name gender:(NSString*)gender address:(NSString*)adderss age:(NSUInteger)age;
@end

.m文件
#import "Person.h"
@implementation Person
/*
 使用常規(guī)進(jìn)行解檔與歸檔罪塔。
 */
-(void)encodeWithCoder:(NSCoder *)aCoder{
    [aCoder encodeObject:_name forKey:@"name"];
    [aCoder encodeObject:_gender forKey:@"gender"];
    [aCoder encodeObject:_address forKey:@"address"];
    [aCoder encodeInteger:_age forKey:@"age"];
    
}
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super init]) {
        _name = [aDecoder decodeObjectForKey:@"name"];
        _gender = [aDecoder decodeObjectForKey:@"gender"];
        _address = [aDecoder decodeObjectForKey:@"address"];
        _age = [aDecoder decodeIntegerForKey:@"age"];
    
    }
    return self;
}
-(instancetype)initWithName:(NSString *)name gender:(NSString *)gender address:(NSString *)adderss age:(NSUInteger)age{
    if (self = [super init]) {
        _name = name;
        _gender = gender;
        _address = adderss;
        _age = age;
    }
    return self;

}
-(NSString*)description{
    return [NSString stringWithFormat:@"name:%@  gender:%@  age:%lu  address:%@",self.name,self.gender,(unsigned long)self.age,self.address];

}
@end

上面定義了Person類,下面定義一個(gè)Teacher類繼承自Person類
//自定義Student繼承自Person類

.h文件
#import "Person.h"
@interface Teacher : Person
@property(nonatomic,strong) NSString * course;//課程
-(instancetype)initWithName:(NSString *)name gender:(NSString *)gender address:(NSString *)adderss age:(NSUInteger)age course:(NSString*)course;
@end

.m文件
#import "Teacher.h"

@implementation Teacher
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {
        _course = [aDecoder decodeObjectForKey:@"course"];
    }
    return self;
}
-(void)encodeWithCoder:(NSCoder *)aCoder{
    [super encodeWithCoder:aCoder];
    [aCoder encodeObject:_course forKey:@"course"];
}
-(instancetype)initWithName:(NSString *)name gender:(NSString *)gender address:(NSString *)adderss age:(NSUInteger)age course:(NSString *)course{
    if (self = [super initWithName:name gender:gender address:adderss age:age]) {
        _course = course;
    }
    return self;
}
-(NSString*)description{
    NSString* str = [super description];
    return [str stringByAppendingString:[NSString stringWithFormat:@"  course:%@",self.course]];
}
@end

解檔與歸檔成對(duì)存在养葵,一定要都要實(shí)現(xiàn)征堪。當(dāng)一個(gè)類繼承自自定義的類,一定要調(diào)用父類的歸檔和解檔关拒,調(diào)用[super initWithCoder:aDeoder][super encodeWithCoder:aCoder]完成父類的歸檔與解檔佃蚜。

2.使用Runtime完成歸檔與解檔

只使用Runtime重構(gòu)了Person類庸娱,因?yàn)镾tudent類使用Runtime重構(gòu)沒有特別之處,與Person類相同谐算。

//.h文件熟尉,與上面定義相同
#import <Foundation/Foundation.h>
@interface Person : NSObject<NSCoding>
@property(nonatomic,strong) NSString * name;
@property(nonatomic,strong) NSString * gender;
@property(nonatomic,strong) NSString * address;
@property(nonatomic) NSUInteger age;
-(instancetype)initWithName:(NSString*)name gender:(NSString*)gender address:(NSString*)adderss age:(NSUInteger)age;
@end

.m文件
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
/*
 使用runtime進(jìn)行解檔與歸檔。
 */
-(void)encodeWithCoder:(NSCoder *)aCoder{
    unsigned int count = 0;
    Ivar *ivarLists = class_copyIvarList([Person class], &count);// 注意下面分析
    for (int i = 0; i < count; i++) {
        const char* name = ivar_getName(ivarLists[i]);
        NSString* strName = [NSString stringWithUTF8String:name];
        [aCoder encodeObject:[self valueForKey:strName] forKey:strName];
    }
    free(ivarLists);   //一定不要忘了洲脂,自己釋放臣樱。
}
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivarLists = class_copyIvarList([Person class], &count);
        for (int i = 0; i < count; i++) {
            const char* name = ivar_getName(ivarLists[i]);
            NSString* strName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
           id value = [aDecoder decodeObjectForKey:strName];
            [self setValue:value forKey:strName];
        }
        free(ivarLists);
    }
    return self;
}
-(instancetype)initWithName:(NSString *)name gender:(NSString *)gender address:(NSString *)adderss age:(NSUInteger)age{
    if (self = [super init]) {
        _name = name;
        _gender = gender;
        _address = adderss;
        _age = age;
    }
    return self;
}
-(NSString*)description{
    return [NSString stringWithFormat:@"name:%@  gender:%@  age:%lu  address:%@",self.name,self.gender,(unsigned long)self.age,self.address];

}
@end
在實(shí)現(xiàn)上面注意點(diǎn)的時(shí)候,由于對(duì)Runtime系統(tǒng)中super的理解不足腮考,導(dǎo)致開始寫的代碼時(shí)Ivar *ivarLists = class_copyIvarList([self class], &count);雇毫,這樣在沒有子類繼承的情況下沒有錯(cuò)誤,完全可以使用踩蔚。但是如多有子類繼承這個(gè)類棚放,當(dāng)子類進(jìn)行解檔和歸檔的的時(shí)候就會(huì)出錯(cuò)。下面分析一下問(wèn)題:

1.解釋一些理論知識(shí)

(1)當(dāng)對(duì)一個(gè)類或者對(duì)象調(diào)用方法時(shí)候馅闽,在Runtime運(yùn)行時(shí)系統(tǒng)轉(zhuǎn)化成了發(fā)送消息飘蚯,編譯器在這時(shí)便根據(jù)情況在objc_msgSend(),objc_msgSend_stret()objc_msgSendSuper(),objc_msgSendSuper_stret()四個(gè)函數(shù)選擇一種調(diào)用。如果消息是傳遞給父類福也,就調(diào)用帶super的方法局骤;如果消息返回?cái)?shù)據(jù)時(shí)數(shù)據(jù)結(jié)構(gòu)而不是簡(jiǎn)單值時(shí),調(diào)用名字帶stret的函數(shù)暴凑。
(2)隱藏的關(guān)鍵字self:當(dāng)objc_msgSend()函數(shù)找到方法實(shí)現(xiàn)的時(shí)候峦甩,將消息的所有參數(shù)都傳遞給方法的實(shí)現(xiàn),同時(shí)還有兩個(gè)參數(shù)现喳,其中之一就是接受消息的對(duì)象self凯傲,其所指向的內(nèi)容是當(dāng)前對(duì)象的指針;另一個(gè)是方法選擇器_cmd,其所指向的內(nèi)容是當(dāng)前方法的SEL指針嗦篱。
(3)關(guān)鍵字super:super是super接受到消息時(shí)候冰单,編譯器創(chuàng)建的一個(gè)結(jié)構(gòu)體objc_super:struct objc_super{ id receiver, Class class} ,里面的receiver 其實(shí)就是self的id指針,這個(gè)結(jié)構(gòu)體指定了消息應(yīng)該傳遞給指定的父類灸促。例如我們?nèi)绻{(diào)用[super class]方法诫欠,獲取父類時(shí)候,編譯器實(shí)際是把self的id指針和class的SEL傳遞給了objc_msgSendSuper().相當(dāng)于self調(diào)用父類的方法浴栽。這個(gè)時(shí)候[super class][self class]一樣荒叼。
總結(jié)一句就是使用super的時(shí)候只是提示編譯器,我調(diào)用的是父類的方法吃度,真正的傳入的對(duì)象還是 self甩挫。由于水平有限,很深入的分析達(dá)不到椿每,可能還有哪里說(shuō)的不對(duì)伊者,可以參考下面的講解英遭,我的有些內(nèi)容也是參考這里。http://www.reibang.com/p/1e06bfee99d0
(4)代碼Ivar *ivarLists = class_copyIvarList([Person class], &count);生成實(shí)例變量的數(shù)組亦渗,不包含父類的挖诸。

2.分析原因

通過(guò)上面的基礎(chǔ)的知識(shí)的了解,應(yīng)該對(duì)知道大致的原因了法精。當(dāng)子類調(diào)用調(diào)用[super initWithCoder:aDeoder][super encodeWithCoder:aCoder]時(shí)候多律,相當(dāng)于使用self對(duì)象調(diào)用父類的initWithCoder:encodeWithCoder:方法。
這個(gè)時(shí)候如果使用Ivar *ivarLists = class_copyIvarList([self class], &count);代碼搂蜓,self現(xiàn)在已經(jīng)是子類的對(duì)象的指針狼荞,現(xiàn)在[self class]獲取的是子類Teacehr的類型,獲取的結(jié)果是Teacher類的實(shí)例變量列表帮碰,則父類的實(shí)例變量在子類沒有被歸檔與解檔相味,造成錯(cuò)誤,并不抱錯(cuò)殉挽,知識(shí)歸檔與解檔不完全丰涉,丟掉父類的內(nèi)容。
而下面的代碼一定要用self斯碌,因?yàn)槟闶菫樽宇怲eacher進(jìn)行歸檔和解檔的一死,需要傳遞Teacher對(duì)象的實(shí)例。

3.調(diào)用歸檔與解檔

#import "ViewController.h"
#import "Teacher.h"
#import "Person.h"
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    Teacher* teacher1 = [Teacher factoryWithName:@"liyang" gender:@"male" address:@"shandong" age:24 course:@"math"];
    Teacher* teacher2 = [Teacher factoryWithName:@"li" gender:@"female" address:@"shanghai" age:23 course:@"english"];
    NSMutableData* data = [NSMutableData data];
    NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc]initForWritingWithMutableData:data1];
    [archiver encodeObject:teacher1 forKey:@"teacher"];
    [archiver encodeObject:teacher2 forKey:@"teacher2"];
    [archiver finishEncoding];
    NSString* path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"teacher"];
    [data writeToFile:path atomically:YES];
    
    NSData* data2 = [NSData dataWithContentsOfFile:path];
    NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData:data2];
    Teacher* teacher3 = [unarchiver decodeObjectForKey:@"teacher"];
    Teacher* teacher4 = [unarchiver decodeObjectForKey:@"teacher2"];
    [unarchiver finishDecoding];
    NSLog(@"\nteacher1:%@\nteacher3:%@\nteacher2:%@\nteacher4:%@\n%@",teacher1,teacher3,teacher2,teacher4,path);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

總的來(lái)說(shuō)使用Runtime庫(kù)來(lái)寫代碼傻唾,需要注意很多地方投慈,因?yàn)樘`活太強(qiáng)大,很難駕馭策吠。需要多學(xué)習(xí)里面的機(jī)制和理論才能好好使用逛裤。使用不當(dāng)就會(huì)掉入萬(wàn)劫不復(fù)之地。
使用NSObject類就能方便使用Runtime運(yùn)行時(shí)庫(kù)猴抹。
里面的內(nèi)容參考很多東西網(wǎng)上的知識(shí),自己歸納總結(jié)一下锁荔。
有錯(cuò)誤的地方往指正蟀给。感覺有用給個(gè)好評(píng)。
Runtime庫(kù)講解阳堕,請(qǐng)點(diǎn)擊下面鏈接:
http://www.reibang.com/p/1e06bfee99d0
http://www.reibang.com/p/25a319aee33d

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末跋理,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子恬总,更是在濱河造成了極大的恐慌前普,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件壹堰,死亡現(xiàn)場(chǎng)離奇詭異拭卿,居然都是意外死亡骡湖,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門峻厚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)响蕴,“玉大人,你說(shuō)我怎么就攤上這事惠桃∑忠模” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵辜王,是天一觀的道長(zhǎng)劈狐。 經(jīng)常有香客問(wèn)我,道長(zhǎng)呐馆,這世上最難降的妖魔是什么肥缔? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮摹恰,結(jié)果婚禮上辫继,老公的妹妹穿的比我還像新娘。我一直安慰自己俗慈,他們只是感情好姑宽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著闺阱,像睡著了一般炮车。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上酣溃,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天瘦穆,我揣著相機(jī)與錄音,去河邊找鬼赊豌。 笑死扛或,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的碘饼。 我是一名探鬼主播熙兔,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼艾恼!你這毒婦竟也來(lái)了住涉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤钠绍,失蹤者是張志新(化名)和其女友劉穎舆声,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柳爽,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡媳握,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年碱屁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片毙芜。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡忽媒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出腋粥,到底是詐尸還是另有隱情晦雨,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布隘冲,位于F島的核電站闹瞧,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏展辞。R本人自食惡果不足惜奥邮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望罗珍。 院中可真熱鬧洽腺,春花似錦、人聲如沸覆旱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)扣唱。三九已至藕坯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間噪沙,已是汗流浹背炼彪。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留正歼,地道東北人辐马。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像局义,于是被迫代替她去往敵國(guó)和親齐疙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉旭咽,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,709評(píng)論 0 9
  • 對(duì)于從事 iOS 開發(fā)人員來(lái)說(shuō),所有的人都會(huì)答出【runtime 是運(yùn)行時(shí)】什么情況下用runtime?大部分人能...
    夢(mèng)夜繁星閱讀 3,721評(píng)論 7 64
  • 今天和朋友參加了EF的life clup 赌厅,主題就是啤酒穷绵,我酒量不行,酒精過(guò)敏體質(zhì)特愿,所以不是EF同學(xué)邀約我想我應(yīng)該...
    愛笑的胖妞閱讀 455評(píng)論 0 0
  • 常見的瀏覽器:1俩由、IE瀏覽器2、Mozilla Firefox:擴(kuò)展插件功能強(qiáng)大3癌蚁、Google Chrome:窗...
    不止風(fēng)雨閱讀 383評(píng)論 0 0
  • 分開大概有五個(gè)月了幻梯,還是會(huì)習(xí)慣想起你,吃飯的時(shí)候突然想起你不吃雞蛋努释,抽煙的時(shí)候記得你抽的牌子碘梢,洗頭的時(shí)候記得你用的...
    哦zyn親愛的閱讀 391評(píng)論 0 0