OC中的runtime是什么 ?runtime可以干什么永品?

什么是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)用過程
  1. 通過 實(shí)例(p) 的isa指針 找到 實(shí)例( p)的 Class(objc_class類對(duì)象);

  2. 在Class(objc_class類對(duì)象)的結(jié)構(gòu)體中找到 cache(方法緩存)散列表 中根據(jù)method_name 看里面有沒有對(duì)應(yīng)的IMP(方法實(shí)現(xiàn));

  3. 如果沒有找到 就繼續(xù)在 Class(objc_class類對(duì)象)的methodLists(方法列表中)尋找對(duì)應(yīng)的selector 宏多,如果找到儿惫,填充到cache(方法緩存)中,并返回 selector;

  4. 如果Class(類)中沒有找到這個(gè)selector 伸但,就繼續(xù)在他的superClass中尋找肾请;

  5. 一旦找到對(duì)應(yīng)的selector,就直接執(zhí)行對(duì)應(yīng)selector方法實(shí)現(xiàn)的IMP(方法實(shí)現(xiàn));

  6. 若找不到對(duì)應(yīng)的selector,即將進(jìn)入 runtime的 消息轉(zhuǎn)發(fā)機(jī)制更胖。消息轉(zhuǎn)發(fā)不做處理 程序發(fā)生崩潰铛铁。

類方法的調(diào)用
  1. 上面我們有說過 我們創(chuàng)建的類 在編譯時(shí)會(huì)創(chuàng)建一個(gè)元類,而這個(gè)元類的對(duì)象 就是我們創(chuàng)建的這個(gè)類

  2. 通過Class(objc_class類對(duì)象) 的isa指針找到所屬元類却妨;

  3. 在進(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
 
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);
///我是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);
    }
   
}

參考資料

博文:『Runtime』詳解(一)基礎(chǔ)知識(shí)
博文: iOS Runtime詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末仲器,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子仰冠,更是在濱河造成了極大的恐慌乏冀,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洋只,死亡現(xiàn)場(chǎng)離奇詭異辆沦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)识虚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門肢扯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來姿搜,“玉大人仁烹,你說我怎么就攤上這事⊥舱迹” “怎么了妻献?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵蛛株,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我育拨,道長(zhǎng)谨履,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任熬丧,我火速辦了婚禮笋粟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘析蝴。我一直安慰自己害捕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布闷畸。 她就那樣靜靜地躺著尝盼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪佑菩。 梳的紋絲不亂的頭發(fā)上盾沫,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音殿漠,去河邊找鬼赴精。 笑死,一個(gè)胖子當(dāng)著我的面吹牛绞幌,可吹牛的內(nèi)容都是我干的蕾哟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼莲蜘,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼谭确!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起菇夸,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤琼富,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后庄新,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鞠眉,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年择诈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了械蹋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡羞芍,死狀恐怖哗戈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荷科,我是刑警寧澤唯咬,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布纱注,位于F島的核電站,受9級(jí)特大地震影響胆胰,放射性物質(zhì)發(fā)生泄漏狞贱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一蜀涨、第九天 我趴在偏房一處隱蔽的房頂上張望瞎嬉。 院中可真熱鬧,春花似錦厚柳、人聲如沸氧枣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽便监。三九已至,卻和暖如春宰闰,著一層夾襖步出監(jiān)牢的瞬間茬贵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來泰國打工移袍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留解藻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓葡盗,卻偏偏與公主長(zhǎng)得像螟左,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子觅够,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355