什么是runtime?
- 從字面上理解為 運(yùn)行時(shí)做鹰,簡(jiǎn)單來說 runtime是一個(gè)實(shí)現(xiàn)OC語言的C庫。
- 我們編寫的OC代碼會(huì)在程序運(yùn)行過程中會(huì)轉(zhuǎn)換成runtime的c語言代碼鼎姐,當(dāng)然我們也可以直接運(yùn)用runtime提供的APi進(jìn)行魔法操作钾麸。
- OC是運(yùn)行時(shí)動(dòng)態(tài)語言在運(yùn)行時(shí)將對(duì)象類型確定更振、方法調(diào)用、代碼和資源的裝載等喂走。
Runtime 概念 及術(shù)語
1. Object(objc_object) 實(shí)例
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
- 這里我們看到 objc_object 結(jié)構(gòu)體 里面只包含一個(gè) class類型 的isa 指針殃饿。
這里也就說明 一個(gè)Object (實(shí)例)唯一保存的就是他所屬Class(類)的地址,當(dāng)我們對(duì)一個(gè) 實(shí)例進(jìn)行方法調(diào)用時(shí)候芋肠。
例如 [object message] 乎芳,會(huì)通過objc_object結(jié)構(gòu)體的 isa指針 去找到到對(duì)應(yīng)的 objec_class 結(jié)構(gòu)體。
2. Class(objc_class) 類
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
struct objc_class {
Class _Nonnull isa; // objc_class 結(jié)構(gòu)體的實(shí)例指針
#if !__OBJC2__
Class _Nullable super_class; // 指向父類的指針
const char * _Nonnull name; // 類的名字
long version; // 類的版本信息帖池,默認(rèn)為 0
long info; // 類的信息奈惑,供運(yùn)行期使用的一些位標(biāo)識(shí)
long instance_size; // 該類的實(shí)例變量大小;
struct objc_ivar_list * _Nullable ivars; // 該類的實(shí)例變量列表
struct objc_method_list * _Nullable * _Nullable methodLists; // 方法定義的列表
struct objc_cache * _Nonnull cache; // 方法緩存
struct objc_protocol_list * _Nullable protocols; // 遵守的協(xié)議列表
#endif
這里我們看到 objc_object 結(jié)構(gòu)體里面定義了很多變量 通過命名不難發(fā)現(xiàn) 結(jié)構(gòu)體里面保存了 指向父類的 指針、類的名字睡汹、版本肴甸、實(shí)例大小、實(shí)例變量 list 方法 list 緩存 協(xié)議列表 等囚巴。一個(gè)類包含的信息不就正式這些嗎原在?
objc_class結(jié)構(gòu)體 的第一個(gè)成員變量是 isa指針,isa指針 保存的是所屬類的結(jié)構(gòu)體的實(shí)例指針也就是對(duì)象彤叉。
所以Class(類)的本質(zhì)就是一個(gè)對(duì)象庶柿, 稱之為 類對(duì)象 。類對(duì)象就是一個(gè)結(jié)構(gòu)體 struct objc_class ,這個(gè)結(jié)構(gòu)體存放的數(shù)據(jù) 稱之為 元數(shù)據(jù) (metadata)
3. Meta Class(元類)
從上面可以看出秽浇,對(duì)象(objc_object)結(jié)構(gòu)體的 isa指針 指向的是對(duì)應(yīng) 類對(duì)象(objc_class)結(jié)構(gòu)體 浮庐,那么類對(duì)象 (objc_class)的isa指向什么?答案指向 元類
元類 是類對(duì)象(objc_class) 的類 聽起來繞口 下面看它的作用就會(huì)豁然開朗
在OC中柬焕,每當(dāng)我們創(chuàng)建一個(gè)類审残。在編譯時(shí)就會(huì)創(chuàng)建一個(gè)元類,而這個(gè)元類的對(duì)象 就是我們創(chuàng)建的這個(gè)類斑举。(我們創(chuàng)建的類本質(zhì)也是一個(gè)對(duì)象objc_class)
那么為什么要有元類搅轿?我們看類對(duì)象( objc_class結(jié)構(gòu)體) ivars 用來存放屬性變量,objc_method_list 用來存放 實(shí)例方法 (-方法)懂昂,那一些靜態(tài)變量 和 類(+)方法 哪里去了介时? 沒錯(cuò),他們就是存放在 元類的methodLists和ivars里面凌彬。
4. Method(objc_method)
/// An opaque type that represents a method in a class definition.
/// 代表類定義中一個(gè)方法的不透明類型
typedef struct objc_method *Method;
struct objc_method {
SEL _Nonnull method_name; /// 方法名
char * _Nullable method_types; /// 方法類型
IMP _Nonnull method_imp; ///方法實(shí)現(xiàn)
};
- SEL _method_name (方法名)釋義
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
SEL: 是一個(gè)指向 objc_selecter 結(jié)構(gòu)體的指針,但是在runtime相關(guān)頭文件中 并沒有找到明確的定義循衰,經(jīng)過測(cè)試打印 得出 猜測(cè) 結(jié)論 SEL 只是一個(gè)保存方法名的字符串铲敛。
- char *_Nullable method_types(方法類型)釋義
方法類型 method_types 是個(gè)字符串,用來存儲(chǔ)方法的參數(shù)類型和返回值類型会钝。
- IMP method_imp(方法實(shí)現(xiàn))釋義
/// A pointer to the function of a method implementation.
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);
#endif
IMP 的實(shí)質(zhì)是一個(gè)函數(shù)指針伐蒋,所指向的就是方法的實(shí)現(xiàn)工三。IMP用來找到函數(shù)地址,然后執(zhí)行函數(shù)先鱼。
objc_class (類對(duì)象)結(jié)構(gòu)體中的 methodLists(方法列表)中存放的元素就是Method(方法)
由此看出Method將 SEL(方法名)和IMP(函數(shù)指針)相關(guān)聯(lián)俭正。當(dāng)對(duì)一個(gè)對(duì)象 發(fā)送消息的時(shí)候 會(huì)通過SEL(方法名)去找到IMP(函數(shù)指針)進(jìn)行執(zhí)行。
消息機(jī)制的基本原理
根據(jù)上面分析焙畔,大家腦海中應(yīng)該會(huì)有一個(gè)方法調(diào)用掸读,也就是消息發(fā)送的全過程。下面總結(jié)一下
///實(shí)例方法調(diào)用
Person * p = [[Person alloc]init];
[p eat];
///類方法調(diào)用
[Person sleep];
#import "Person.h"
@implementation Person
-(void)eat;
{
NSLog(@"吃飯");
}
+(void)sleep;
{
NSLog(@"睡覺");
}
@end
對(duì)象方法調(diào)用過程
通過 實(shí)例(p) 的isa指針 找到 實(shí)例( p)的 Class(objc_class類對(duì)象);
在Class(objc_class類對(duì)象)的結(jié)構(gòu)體中找到 cache(方法緩存)散列表 中根據(jù)method_name 看里面有沒有對(duì)應(yīng)的IMP(方法實(shí)現(xiàn));
如果沒有找到 就繼續(xù)在 Class(objc_class類對(duì)象)的methodLists(方法列表中)尋找對(duì)應(yīng)的selector 宏多,如果找到儿惫,填充到cache(方法緩存)中,并返回 selector;
如果Class(類)中沒有找到這個(gè)selector 伸但,就繼續(xù)在他的superClass中尋找肾请;
一旦找到對(duì)應(yīng)的selector,就直接執(zhí)行對(duì)應(yīng)selector方法實(shí)現(xiàn)的IMP(方法實(shí)現(xiàn));
若找不到對(duì)應(yīng)的selector,即將進(jìn)入 runtime的 消息轉(zhuǎn)發(fā)機(jī)制更胖。消息轉(zhuǎn)發(fā)不做處理 程序發(fā)生崩潰铛铁。
類方法的調(diào)用
上面我們有說過 我們創(chuàng)建的類 在編譯時(shí)會(huì)創(chuàng)建一個(gè)元類,而這個(gè)元類的對(duì)象 就是我們創(chuàng)建的這個(gè)類
通過Class(objc_class類對(duì)象) 的isa指針找到所屬元類却妨;
在進(jìn)行上述后續(xù)操作饵逐。
runtime可以干什么?
- 獲取類中所有的實(shí)例變量
class_copyIvarList()返回一個(gè)指向類的成員變量和屬性數(shù)組的指針
class_copyPropertyList()返回一個(gè)指向類的屬性數(shù)組的指針
///我是.m
#import "Person.h"
@interface Person ()
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *age;
@property (nonatomic,copy) NSString *sex;
@end
@implementation Person
{
NSNumber * phone;
}
@end
#import <Foundation/Foundation.h>
#import "Person.h"
#import <objc/runtime.h>// 包含對(duì)類、成員變量管呵、屬性梳毙、方法的操作
#import <objc/message.h>// 包含消息機(jī)制
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
// 返回所有的屬性和實(shí)例變量 class_copyIvarList
unsigned int methodCount = 0;
Ivar * ivars = class_copyIvarList([Person class], &methodCount);
for (unsigned int i = 0; i< methodCount; i++) {
Ivar ivar = ivars[i];
const char * name = ivar_getName(ivar);
const char * type = ivar_getTypeEncoding(ivar);
NSLog(@" Persons成員變量屬性為%s==類型%s",name,type);
}
free(ivars);
// 只返回 屬性方法 class_copyPropertyList
unsigned int method_Count = 0;
objc_property_t * properties = class_copyPropertyList([Person class], &method_Count);
for (unsigned int i = 0; i< method_Count; i++) {
objc_property_t property = properties[i];
const char * properName = property_getName(property);
NSLog(@" Person 屬性為%s",properName);
}
free(properties);
}
return 0;
}
2019-10-29 14:18:17.595647+0800 runtime[7816:359804] Hello, World!
2019-10-29 14:18:17.596917+0800 runtime[7816:359804] Persons成員變量屬性為phone==類型@"NSNumber"
2019-10-29 14:18:17.597031+0800 runtime[7816:359804] Persons成員變量屬性為_name==類型@"NSString"
2019-10-29 14:18:17.597093+0800 runtime[7816:359804] Persons成員變量屬性為_age==類型@"NSString"
2019-10-29 14:18:17.597145+0800 runtime[7816:359804] Persons成員變量屬性為_sex==類型@"NSString"
2019-10-29 14:18:17.597213+0800 runtime[7816:359804] Person 屬性為name
2019-10-29 14:18:17.597608+0800 runtime[7816:359804] Person 屬性為age
2019-10-29 14:18:17.597708+0800 runtime[7816:359804] Person 屬性為sex
Program ended with exit code: 0
- 使用runtime動(dòng)態(tài)添加一個(gè)類
objc_allocateClassPair:注冊(cè)一個(gè)新類或者元類 如想讓這個(gè)類成為基類那么參數(shù)superclass指針定為nil.參數(shù)extraByte是分配給類和元類對(duì)象尾部的索引ivars的字節(jié)數(shù),通常指為0
objc_registerClassPair:當(dāng)創(chuàng)建完新類后捐下,需要調(diào)用這個(gè)方法注冊(cè)這個(gè)類 之后這個(gè)類才可以在程序中使用
objc_disposeClassPair:用于銷毀一個(gè)類及其元類账锹,需要注意的是,如果程序運(yùn)行中還存在類或者其子類的實(shí)例坷襟,那么就不能調(diào)用此方法
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
//創(chuàng)建一個(gè)新類
Class myClass = objc_allocateClassPair([NSObject class], "myClass", 0);
//添加ivar
//@encode(aType):返回該類型的字符串
class_addIvar(myClass, "_address", sizeof(NSString*), log2(sizeof(NSString*)),@encode(NSString*));
class_addIvar(myClass, "_age", sizeof(NSUInteger), log2(sizeof(NSUInteger)), @encode(NSUInteger));
//注冊(cè)類
objc_registerClassPair(myClass);
//創(chuàng)建實(shí)例
id object = [[myClass alloc] init];
//為ivar賦值
[object setValue:@"china" forKey:@"address"];
[object setValue:@20 forKey:@"age"];
NSLog(@"address=%@,age=%@",[object valueForKey:@"address"],[object valueForKey:@"age"]);
//當(dāng)類或者它的子類的實(shí)例還存在 則不能調(diào)用 objc_disposeClassPair
object = nil;
//銷毀
objc_disposeClassPair(myClass);
}
return 0;
}
2019-10-29 15:06:02.931562+0800 runtime[8254:384872] Hello, World!
2019-10-29 15:06:02.936912+0800 runtime[8254:384872] address=china,age=20
Program ended with exit code: 0
- 在category中增加屬性(眾所周知正常來說不可以添加但是用runtime完全可以實(shí)現(xiàn))
給Person類添加個(gè)Category
#import <AppKit/AppKit.h>
#import"Person.h"
@interface Person (Category)
//不會(huì)生成添加屬性的getter和setter方法奸柬,必須我們手動(dòng)生成
@property (nonatomic, copy) NSString *phone;
@end
#import "Person+Category.h"
#import <AppKit/AppKit.h>
#import <objc/runtime.h>
@implementation Person (Category)
// 定義關(guān)聯(lián)的key
static const char *key = "phone";
/**
phone的getter方法
*/
-(NSString *)phone
{
// 根據(jù)關(guān)聯(lián)的key,獲取關(guān)聯(lián)的值婴程。
return objc_getAssociatedObject(self, key);
}
/**
phone的setter方法
*/
-(void)setPhone:(NSString *)phone
{
// 第一個(gè)參數(shù):給哪個(gè)對(duì)象添加關(guān)聯(lián)
// 第二個(gè)參數(shù):關(guān)聯(lián)的key廓奕,通過這個(gè)key獲取
// 第三個(gè)參數(shù):關(guān)聯(lián)的value
// 第四個(gè)參數(shù):關(guān)聯(lián)的策略
objc_setAssociatedObject(self, key, phone, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Person+Category.h"
#import <objc/runtime.h>// 包含對(duì)類、成員變量档叔、屬性桌粉、方法的操作
#import <objc/message.h>// 包含消息機(jī)制
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
Person * per = [[Person alloc]init];
per.phone = @"111000001011";
NSLog(@"Category不能添加屬性?可以的我用Category為Person添加了一個(gè)phone屬性衙四,phone=%@",per.phone);
}
return 0;
}
2019-10-29 15:26:13.400545+0800 runtime[8485:392320] Category不能添加屬性铃肯?可以的我用Category為Person添加了一個(gè)phone屬性,phone=111000001011
Program ended with exit code: 0
- runtime進(jìn)行消息傳遞
#import "Person.h"
@interface Person ()
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *age;
@property (nonatomic,copy) NSString *sex;
@end
@implementation Person
{
NSNumber * phone;
}
-(void)callPhone;
{
NSLog(@"電話正在撥打中传蹈。押逼。步藕。");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
Person * person = [[Person alloc]init];
// OC中,當(dāng)調(diào)用某個(gè)對(duì)象的方法時(shí)挑格,其實(shí)質(zhì)上就是向該對(duì)象發(fā)送了一條消息咙冗,比如:
//
// [person callPhone]; 本質(zhì)為以下代碼
objc_msgSend(person, @selector(callPhone));
}
return 0;
2019-10-29 15:53:47.954497+0800 runtime[8667:403765] Hello, World!
2019-10-29 15:53:47.955521+0800 runtime[8667:403765] 電話正在撥打中。漂彤。雾消。
Program ended with exit code: 0
- 使用runtime動(dòng)態(tài)添加方法[實(shí)現(xiàn)消息轉(zhuǎn)發(fā)] (http://www.reibang.com/p/7f868b0e2575)
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
//Person類中并不存在 sendMassage方法
Person * person = [[Person alloc]init];
SEL aSel = NSSelectorFromString(@"sendMassage");
[person performSelector:aSel];
}
return 0;
}
運(yùn)行
-[Person sendMassage]: unrecognized selector sent to instance 0x100626da0
運(yùn)行崩潰此異常為沒有找到sendMassage ,消息轉(zhuǎn)發(fā)過程以程序崩潰結(jié)束 但是可以利用runtime補(bǔ)救 程序消息轉(zhuǎn)發(fā)詳解請(qǐng)看這里[實(shí)現(xiàn)消息轉(zhuǎn)發(fā)] (http://www.reibang.com/p/7f868b0e2575)
//person.m 沒有找到sendMassage方法調(diào)用callPhone方法吧
-(void)callPhone;
{
NSLog(@"電話正在撥打中显歧。仪或。。");
}
//動(dòng)態(tài)補(bǔ)加方法
+(BOOL)resolveInstanceMethod:(SEL)sel
{
NSString * str = NSStringFromSelector(sel);
if ([str isEqualToString:@"sendMassage"]) {
// 得到實(shí)例方法
Method _Nonnull m = class_getInstanceMethod(self, @selector(callPhone));
// 返回方法的調(diào)用地址
IMP imp = method_getImplementation(m);
// 給類添加一個(gè)新的方法和該方法的具體實(shí)現(xiàn)
class_addMethod(self, @selector(sendMassage), imp, "");
}
return [super resolveInstanceMethod:sel];
}
- 利用kvc和runtime暴力訪問私有屬性和變量
Person * person = [[Person alloc]init];
//kvo 暴力訪問 私有屬性和私有變量
[person setValue:@"小明" forKey:@"name"];
NSString * pname = [person valueForKey:@"name"];
[person setValue:@"19" forKey:@"age"];
NSString * page = [person valueForKey:@"age"];
[person setValue:@"男" forKey:@"sex"];
NSString * psex = [person valueForKey:@"sex"];
[person setValue:@1111111111 forKey:@"phone"];
NSString * pphone = [person valueForKey:@"phone"];
NSLog(@"姓名%@,年齡%@,性別%@,電話%@",pname,page,psex,pphone);
// runtime如何來做士骤?
unsigned int count = 0;
//獲取所有屬性變量名字
Ivar * members= class_copyIvarList([Person class], &count);
for (unsigned int i = 0; i<count; i++) {
const char * memberName = ivar_getName(members[i]);
const char * memberType = ivar_getTypeEncoding(members[i]);
NSLog(@"屬性變量名字:%s 類型:%s",memberName,memberType);
}
//訪問私有變量和屬性
Ivar varname = members[0];
object_setIvar(person, varname, @"小R");
NSString * name = object_getIvar(person, varname);
Ivar varage = members[1];
object_setIvar(person, varage, @"1111111");
NSString * age = object_getIvar(person, varage);
Ivar varsex = members[2];
object_setIvar(person, varsex, @"未知");
NSString * sex = object_getIvar(person, varsex);
Ivar varphone = members[3];
object_setIvar(person, varphone,@12222222);
NSNumber * phone = object_getIvar(person, varphone);
NSLog(@"runtime訪問:姓名%@,年齡%@,性別%@,電話%@",name,age,sex,phone);
- 使用runtime進(jìn)行方法交換
為什么在load里面交換請(qǐng)看 [load方法和initialize方法異同] (http://www.reibang.com/p/d4ce4530246e)
///我是Person.m
+ (void)load
{
//獲取實(shí)例的方法
Method M1 = class_getInstanceMethod(self, @selector(callBB機(jī)));
Method M2 = class_getInstanceMethod(self, @selector(callPhone));
//交換方法
method_exchangeImplementations(M1, M2);
//獲取類方法
Method cl1 = class_getClassMethod(self, @selector(callQQ));
Method cl2 = class_getClassMethod(self, @selector(callWX));
//交換方法
method_exchangeImplementations(cl1, cl2);
}
-(void)callBB機(jī);
{
NSLog(@"打bb機(jī)中..");
}
-(void)callPhone;
{
NSLog(@"電話正在撥打中范删。。拷肌。");
}
+(void)callQQ
{
NSLog(@"打QQ中..");
}
+(void)callWX
{
NSLog(@"打微信中..");
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person * person = [[Person alloc]init];
NSLog(@"打電話------------");
[person callPhone];
NSLog(@"打BB機(jī)------------");
[person callBB機(jī)];
NSLog(@"打QQ------------");
[Person callQQ];
NSLog(@"打WX------------");
[Person callWX];
}
return 0;
運(yùn)行
2019-10-29 17:56:41.131014+0800 runtime[9536:457820] 打電話------------
2019-10-29 17:56:41.131757+0800 runtime[9536:457820] 打bb機(jī)中..
2019-10-29 17:56:41.131928+0800 runtime[9536:457820] 打BB機(jī)------------
2019-10-29 17:56:41.131989+0800 runtime[9536:457820] 電話正在撥打中到旦。。巨缘。
2019-10-29 17:56:41.132038+0800 runtime[9536:457820] 打QQ------------
2019-10-29 17:56:41.132083+0800 runtime[9536:457820] 打微信中..
2019-10-29 17:56:41.132127+0800 runtime[9536:457820] 打WX------------
2019-10-29 17:56:41.132173+0800 runtime[9536:457820] 打QQ中..
- 使用runtime為model賦值
建PersonModel類
///我是.h
#import <Foundation/Foundation.h>
@interface PersonModel : NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *age;
@property (nonatomic,copy) NSString *sex;
@property (nonatomic,copy) NSString *phone;
+(instancetype)modelWithDict:(NSDictionary*)dict;
@end
///我是.m
#import "PersonModel.h"
#import <objc/message.h>
@implementation PersonModel
+(instancetype)modelWithDict:(NSDictionary*)dict;
{
id object = [[self alloc]init];
unsigned int count = 0;
Ivar * ivarList = class_copyIvarList(self, &count);
for (int i = 0; i<count; i++) {
//獲取成員屬性
Ivar ivar = ivarList[i];
//獲取成員變量名稱
NSString * ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
//獲取成員變量類型
NSString * ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
//獲取Key
NSString * key = [ivarName substringFromIndex:1];
id value = dict[key];
//二級(jí)轉(zhuǎn)換:半段value是否是字典
// 并且是自定義對(duì)象才需要轉(zhuǎn)換
if ([value isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"NS"]){
//獲取class
Class modelClass = NSClassFromString(ivarType);
value = [modelClass modelWithDict:value];
}
if (value) {
[object setValue:value forKey:key];
}
}
return object;
}
@end
main函數(shù)賦值
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSDictionary * data = @{@"name":@"小花花",
@"age":@"10",
@"sex":@"女",
@"phone":@"1111111111111"};
PersonModel * model = [PersonModel modelWithDict:data];
NSLog(@"name:%@.age:%@.sex:%@.phone:%@",model.name,model.age,model.sex,model.phone);
}
return 0;
}
運(yùn)行 模型賦值成功
2019-10-30 11:21:59.319949+0800 runtime[18571:872011] name:小花.age:10.sex:女.phone:1111111111111
Program ended with exit code: 0
- 自動(dòng)歸檔解檔
當(dāng)我們需要將一個(gè)對(duì)象進(jìn)行歸檔時(shí)添忘,都要讓該對(duì)象的類遵守NSCoding協(xié)議,再實(shí)現(xiàn)歸檔和接檔方法若锁。以上面的PersonModel為例
/**
* 將對(duì)象寫入某個(gè)文件時(shí)需要調(diào)用搁骑,在該方法中說明哪些屬性需要存儲(chǔ)
*/
- (void)encodeWithCoder:(NSCoder *)coder;
{
[coder encodeObject:self.name forKey:@"name"];
[coder encodeObject:self.age forKey:@"age"];
[coder encodeObject:self.sex forKey:@"sex"];
[coder encodeObject:self.phone forKey:@"phone"];
}
/**
* 從文件中解析對(duì)象時(shí)會(huì)調(diào)用,在該方法中解析對(duì)象的屬性
*/
- (nullable instancetype)initWithCoder:(NSCoder *)coder;
{
if (self = [super init]) {
// 解析之后要賦值給屬性
_name = [coder decodeObjectForKey:@"name"];
_age = [coder decodeObjectForKey:@"age"];
_sex = [coder decodeObjectForKey:@"sex"];
_phone = [coder decodeObjectForKey:@"phone"];
}
return self;
}//
如果這個(gè)類屬性上百個(gè)那一個(gè)一個(gè)的寫會(huì)累死 而且還很low
應(yīng)該使用runtime來進(jìn)行
- (nullable instancetype)initWithCoder:(NSCoder *)coder
{
if (self = [super init]) {
Class c = self.class;
// 截取類和父類的成員變量
while (c && c != [NSObject class]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(c, &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivars[i])];
id value = [coder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
// 獲得c的父類
c = [c superclass];
free(ivar);
}
}
return self;
}
/**
* 從文件中解析對(duì)象時(shí)會(huì)調(diào)用又固,在該方法中解析對(duì)象的屬性
*/
- (void)encodeWithCoder:(NSCoder *)coder;
{
Class c = self.class;
// 截取類和父類的成員變量
while (c && c != [NSObject class]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(c, &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
id value = [self valueForKey:key];
[coder encodeObject:value forKey:key];
}
c = [c superclass];
// 釋放內(nèi)存
free(ivar);
}
}