iOS-RunTime介紹及使用

一、RunTime概念

RunTime簡稱運行時,我們總是聽說OC是動態(tài)語言運行時機制,也就是系統(tǒng)在運行時候的一些機制,其中最重要的是消息機制。C語言,函數(shù)的調(diào)用在編譯的時候會決定調(diào)用哪個函數(shù),如果調(diào)用未實現(xiàn)的函數(shù)就會報錯,而OC語言屬于動態(tài)調(diào)用過程,在編譯時并不能決定真正調(diào)用哪個函數(shù),只有在真正的運行的時候才會根據(jù)函數(shù)的名稱找到對應(yīng)函數(shù)來調(diào)用,當(dāng)調(diào)用該對象上某個方法,而該對象上沒有實現(xiàn)這個方法的時候,可以通過“消息轉(zhuǎn)發(fā)”進行解決,也就是說,在編譯截斷,OC可以調(diào)用任何函數(shù),即使是這個函數(shù)沒有實現(xiàn),只要聲明過就不會報錯。

二、OC調(diào)用方法在RunTime中的具體實現(xiàn)

《一》RunTime消息機制

消息機制是運行時里面最重要的機制,OC是動態(tài)語言,本質(zhì)都是發(fā)送消息,每個方法在運行時會被動態(tài)轉(zhuǎn)化為消息發(fā)送,即:objc_msgSend(receiver, selector)
比如:

  • OC代碼實例方法調(diào)用底層的實現(xiàn):
BackView *backView = [[BackView alloc] init];
[backView changeBgColor];

//編譯時底層轉(zhuǎn)化
//objc對象的isa指針指向他的類對象,從而可以找到對象上的方法
//SEL:方法編號,根據(jù)方法編號就可以找到對應(yīng)方法的實現(xiàn)。
[backView performSelector:@selector(changeBgColor)];
//performSelector本質(zhì)即為運行時,發(fā)送消息,誰做事情就調(diào)用誰 
objc_msgSend(backView, @selector(changeBgColor));
// 帶參數(shù)
objc_msgSend(backView, @selector(changeBgColor:),[UIColor RedColor]);
  • OC代碼類方法調(diào)用底層的實現(xiàn)
//本質(zhì)是將類名轉(zhuǎn)化成類對象,初始化方法其實是創(chuàng)建類對象。
[BackView changeBgColor];
//BackView 只是表示一個類名,調(diào)用方法其實是用的類對象去調(diào)用的。(類對象既然稱為對象,那它也是一個實例。類對象中也有一個isa指針指向它的元類(meta class),即類對象是元類的實例。元類內(nèi)部存放的是類方法列表,根元類的isa指針指向自己,superclass指針指向NSObject類。)
//編譯時底層轉(zhuǎn)化

//RunTime 調(diào)用類方法同樣,類方法也是類對象去調(diào)用,所以需要獲取類對象,然后使用類對象去調(diào)用方法
Class backViewClass = [BackView class];
[backViewClass performSelector:@selector(changeBgColor)];
//performSelector本質(zhì)即為運行時,發(fā)送消息,誰做事情就調(diào)用誰 

//類對象發(fā)送消息
objc_msgSend(backViewClass, @selector(changeBgColor));
// 帶參數(shù)
objc_msgSend(backViewClass, @selector(changeBgColor:),[UIColor RedColor]);

selector(SEL):是一個SEL方法選擇器。
SEL其主要作用是快速的通過SEL其主要作用是快速的通過方法名字查找到對應(yīng)方法的函數(shù)指針,然后調(diào)用其函數(shù)。SEL其本身是一個Int類型的地址,地址中存放著方法的名字。
對于一個類中。每一個方法對應(yīng)著一個SEL。所以一個類中不能存在2個名稱相同的方法,即使參數(shù)類型不同,因為SEL是根據(jù)方法名字生成的,相同的方法名稱只能對應(yīng)一個SEL。

  • 消息傳遞的底層實現(xiàn)
    這里我們要先說一下,一個Objc對象如何進行內(nèi)存布局的,我們先看一下objc_class源碼:
// runtime.h(類在runtime中的定義)

struct objc_class {
  Class isa OBJC_ISA_AVAILABILITY; //isa指針指向Meta Class,因為Objc的類的本身也是一個Object,為了處理這個關(guān)系,runtime就創(chuàng)造了Meta Class,當(dāng)給類發(fā)送[NSObject alloc]這樣消息時,實際上是把這個消息發(fā)給了Class Object
  #if !__OBJC2__
  Class super_class OBJC2_UNAVAILABLE; // 父類
  const char *name OBJC2_UNAVAILABLE; // 類名
  long version OBJC2_UNAVAILABLE; // 類的版本信息,默認為0
  long info OBJC2_UNAVAILABLE; // 類信息,供運行期使用的一些位標(biāo)識
  long instance_size OBJC2_UNAVAILABLE; // 該類的實例變量大小
  struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
  struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
  struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法緩存,對象接到一個消息會根據(jù)isa指針查找消息對象,這時會在method Lists中遍歷,如果cache了,常用的方法調(diào)用時就能夠提高調(diào)用的效率。
  struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協(xié)議鏈表
  #endif
  } OBJC2_UNAVAILABLE;

objc對象內(nèi)存布局:

<1>所有父類的成員變量和自己的成員變量都會存放在該對象所對應(yīng)的存儲空間中。
<2>每個對象內(nèi)部都有一個isa指針,指向它的類對象,類對象中存放著本對象的:
    1、對象方法列表(對象能夠接受的消息列表,保存再它所對應(yīng)的類對象中)
    2、成員變量的列表
    3、屬性列表

每一個類都有一個方法列表Method List,保存著類里面所有的方法,根據(jù)SEL傳入的方法編號找到方法,然后找到方法的實現(xiàn),然后在方法的實現(xiàn)里面實現(xiàn)。

  • 消息發(fā)送動態(tài)查找對應(yīng)的方法
    <1>實例對象調(diào)用方法后,底層調(diào)用[objc performSelector:@selector(SEL)];方法,編譯器將代碼轉(zhuǎn)化為objc_msgSend(receiver, selector)。
    <2>objc_msgSend函數(shù)中,首先通過objcisa指針找到objc對應(yīng)的class,在class中先去cache中通過SEL查找對應(yīng)函數(shù)的 method,如果找到則通過 method中的函數(shù)指針跳轉(zhuǎn)到對應(yīng)的函數(shù)中去執(zhí)行。
    <3>如果在cacha中未找到,再去methodList中查找,如果能找到,則將method加入到cache中,以方便下次查找,并通過method中的函數(shù)指針跳轉(zhuǎn)到對應(yīng)的函數(shù)中去執(zhí)行。
    <4>如果在methodlist中未找到,則去superClass中去查找,如果能找到,則將method加入到cache中,以方便下次查找,并通過method中的函數(shù)指針跳轉(zhuǎn)到對應(yīng)的函數(shù)中去執(zhí)行。

  • 消息傳遞的過程
    <接上↑>objc在向一個對象發(fā)送消息時,runtime庫會根據(jù)對象的isa指針找到該對象實際所屬的類,然后在該類中的方法列表以及其父類方法列表中尋找方法運行,即:objc_msgSend(receiver, selector)。如果,在最頂層的父類中依然找不到相應(yīng)的方法時,程序在運行時會掛掉并拋出異常unrecognized selector sent to XXX 。但是在這之前,objc的運行時會給出三次拯救程序崩潰的機會:
    <1> Method resolution
    objc運行時會調(diào)用+resolveInstanceMethod:或者 +resolveClassMethod:實例方法和類方法),讓你有機會提供一個函數(shù)實現(xiàn)。如果你添加了函數(shù),那運行時系統(tǒng)就會重新啟動一次消息發(fā)送的過程,否則 ,運行時就會移到下一步,消息轉(zhuǎn)發(fā)(Message Forwarding)。
    <2> Message Forwarding

    • <1>Fast forwarding
      如果目標(biāo)對象實現(xiàn)了-forwardingTargetForSelector:,Runtime 這時就會調(diào)用這個方法,給你把這個消息轉(zhuǎn)發(fā)給其他對象的機會。 只要這個方法返回的不是nil和self,整個消息發(fā)送的過程就會被重啟,當(dāng)然發(fā)送的對象會變成你返回的那個對象。否則,就會繼續(xù)Normal Fowarding。 這里叫Fast,只是為了區(qū)別下一步的轉(zhuǎn)發(fā)機制。因為這一步不會創(chuàng)建任何新的對象,但下一步轉(zhuǎn)發(fā)會創(chuàng)建一個NSInvocation對象,所以相對更快點。
    • <2>Normal forwarding
      這一步是Runtime最后一次給你挽救的機會。首先它會發(fā)送-methodSignatureForSelector:消息獲得函數(shù)的參數(shù)和返回值類型。如果-methodSignatureForSelector:返回nil,Runtime則會發(fā)出-doesNotRecognizeSelector:消息,程序這時也就掛掉了。如果返回了一個函數(shù)簽名,Runtime就會創(chuàng)建一個NSInvocation對象并發(fā)送-forwardInvocation:消息給目標(biāo)對象。

《二》使用RunTime動態(tài)的添加對象的成員變量和方法

  • 動態(tài)添加方法
    動態(tài)給某各類添加方法,相當(dāng)于懶加載機制。這里我們以實例方法為例,首先我們先不實現(xiàn)對象方法,當(dāng)調(diào)用performSelector:方法的時候,再去動態(tài)加載方法調(diào)用。[bg performSelector:@selector(changeBgColor)];當(dāng)編譯時是不會報錯的,運行時才會報錯,因為這里我們BaseView類中并沒有實現(xiàn)changeBgColor這個方法,當(dāng)去類的Method List中發(fā)現(xiàn)找不到changeBgColor方法,會報錯找不到這個方法。這里我們就用到了上面提到的消息轉(zhuǎn)發(fā)機制。當(dāng)調(diào)用了沒有實現(xiàn)的對象方法時,就會調(diào)用+(BOOL)resolveInstanceMethod:(SEL)sel方法,當(dāng)調(diào)用了沒有實現(xiàn)的類方法的時候,就會調(diào)用+(BOOL)resolveClassMethod:(SEL)sel方法。所以通過這兩個方法就可以動態(tài)添加方法,參數(shù)sel即表示沒有實現(xiàn)的方法。一個objective - C方法最終都是一個C函數(shù),默認任何一個方法都有兩個參數(shù)。self : 方法調(diào)用者 _cmd : 調(diào)用方法編號。我們可以使用函數(shù)class_addMethod為類添加一個方法以及實現(xiàn)。
    動態(tài)添加方法:
+(BOOL)resolveInstanceMethod:(SEL)sel
{
    // 動態(tài)添加changeBgColor方法
    // 首先判斷sel是不是changeBgColor方法 也可以轉(zhuǎn)化成字符串進行比較。    
    if (sel == @selector(changeBgColor)) {
    /** 
     第一個參數(shù): cls:給哪個類添加方法
     第二個參數(shù): SEL name:添加方法的編號
     第三個參數(shù): IMP imp: 方法的實現(xiàn),函數(shù)入口,函數(shù)名可與方法名不同(建議與方法名相同)
     第四個參數(shù): types :方法類型,需要用特定符號,參考API
     */
      class_addMethod(self, sel, (IMP) newChangeBgColor , "v@:");
        // 處理完返回YES
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

void newChangeBgColor(id self ,SEL _cmd)
{

}

types:方法類型表:


type方法類型表.png
  • 動態(tài)添加變量
    1-> 動態(tài)獲取類中的所有屬性(包括私有)
Ivar *ivar = class_copyIvarList([self.baseView class], &count);

2->遍歷屬性找到對應(yīng)屬性字段

const char *varName = ivar_getName(var);

3->修改對應(yīng)的字段

object_setIvar(self.baseView, var, @"newName");

具體:

-(void)addNewName{
    unsigned int count = 0;
    Ivar *ivar = class_copyIvarList([baseView class], &count);
    for (int i = 0; i<count; i++) {
        Ivar var = ivar[i];
        const char *varName = ivar_getName(var);
        NSString *name = [NSString stringWithUTF8String:varName];

        if ([name isEqualToString:@"oldName"]) {
            object_setIvar(baseView, var, @"newName");
            break;
        }
    }
  
    self.nameLabel.text = baseView.oldName;
}

《三》動態(tài)交換方法

當(dāng)遇到所使用的系統(tǒng)方法或者不可修改的靜態(tài)庫方法功能不夠時,需要給此類方法擴展一些功能。比如我們有個BaseView類中有個changeBgColor的方法,此時我們想在這個方法里做些操作,我們定義一個newChangeBgColor的方法。因為交換只需進行一次,所以我們在BaseView的Categary中的load方法中,當(dāng)加載分類的時候交換方法即可。交換方法的本質(zhì)其實是交換兩個方法的實現(xiàn)
即:
1根據(jù)SEL方法編號在Method List中找到方法
2交換兩個IMP指針指向的方法實現(xiàn)

交換方法內(nèi)部實現(xiàn).png
+(void)load
{
    // 獲取要交換的兩個方法
    // 獲取類方法  用Method 接受一下
    // class :獲取哪個類方法 
    // SEL :獲取方法編號,根據(jù)SEL就能去對應(yīng)的類找方法。
    Method oldChangeColorMethod = class_getClassMethod([UIImage class], @selector(changeBgColor));
    // 獲取第二個類方法
    Method newChangeColorMethod = class_getClassMethod([UIImage class], @selector(newChangeBgColor));
    // 交換兩個方法的實現(xiàn) 方法一 ,方法二。
    method_exchangeImplementations(oldChangeColorMethod, newChangeColorMethod);
    // IMP其實就是 implementation的縮寫:表示方法實現(xiàn)。
}

注意:交換方法的時候newMethod里就不能再調(diào)用oldMethod方法了,因為調(diào)用oldMethod方法實質(zhì)上相當(dāng)于調(diào)用newMethod方法,會循環(huán)引用造成死循環(huán)。

《四》RunTim動態(tài)添加屬性

XCode運行在Category的.h文件聲明@property編譯通過,但運行時如果沒有runtime處理,進行賦值取值,就會報錯。

  • @property的本質(zhì)是什么
    @property = ivar + getter + setter;
    說人話:

“屬性”(property)有兩大概念:ivar(實例變量)、存取方法(access method = getter + setter)。

“屬性”(property)作為OC的一項特性,主要的作用就在于封裝對象總的數(shù)據(jù)。OC 對象通常會把其所需要的數(shù)據(jù)保存為各種實例變量,實例變量一般通過“存取方法”(access method)來訪問,其中,“獲取方法”(getter)用于讀取變量值,而“設(shè)置方法”(setter)用于寫入變量值。在正規(guī)的OC編碼風(fēng)格中,存取方法有著嚴格的命名規(guī)范,正因為有了這種嚴格的命名規(guī)范,所以O(shè)C可以根據(jù)名稱自動創(chuàng)建出存取方法,其實也可以把屬性當(dāng)做一種關(guān)鍵字,可以表示:

編譯器會自動寫出一套存取方法,用以訪問給定類型中具有給定名稱的變量,所以你也可以這么說:@property=getter+setter;
比如下面的這個類:

@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end

上述代碼寫出來的類與下面這種寫法等效:

@interface Person : NSObject
- (NSString *)firstName;
- (void)setFirstName:(NSString *)firstName;
- (NSString *)lastName;
- (void)setLastName:(NSString *)lastName;
@end

而objc_property是一個結(jié)構(gòu)體,包括name和attributes,定義如下:

struct property_t {
    const char *name;
    const char *attributes;
};

而attributes本質(zhì)是objc_property_attribute_t,定義了property的一些屬性,定義如下:

/// Defines a property attribute
typedef struct {
    const char *name;           /**< The name of the attribute */
    const char *value;          /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;

而attributes的具體內(nèi)容是什么呢?其實,包括:類型,原子性,內(nèi)存語義和對應(yīng)的實例變量。
例如:我們定義一個string的property@property (nonatomic, copy) NSString *string;
,通過 property_getAttributes(property)
獲取到attributes并打印出來之后的結(jié)果為T@"NSString",C,N,V_string

其中T就代表類型,可參閱Type Encodings,C就代表Copy,N代表nonatomic,V就代表對于的實例變量。
ivar、getter、setter 是如何生成并添加到這個類中的?

“自動合成”( autosynthesis)
完成屬性定義后,編譯器會自動編寫訪問這些屬性所需的方法,此過程叫做“自動合成”(autosynthesis)。需要強調(diào)的是,這個過程由編譯 器在編譯期執(zhí)行,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼。除了生成方法代碼 getter、setter 之外,編譯器還要自動向類中添加適當(dāng)類型的實例變量,并且在屬性名前面加下劃線,以此作為實例變量的名字。在前例中,會生成兩個實例變量,其名稱分別為 _firstName 與 _lastName。也可以在類的實現(xiàn)代碼里通過 @synthesize 語法來指定實例變量的名字.

@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end

屬性是怎么實現(xiàn)的呢?

1、OBJC_IVAR_$類名$屬性名稱 :該屬性的“偏移量” (offset),這個偏移量是“硬編碼” (hardcode),表示該變量距離存放對象的內(nèi)存區(qū)域的起始地址有多遠。
2、setter 與 getter 方法對應(yīng)的實現(xiàn)函數(shù)
3、ivar_list :成員變量列表
4、method_list :方法列表
5、prop_list :屬性列表
也就是說我們每次在增加一個屬性,系統(tǒng)都會在 ivar_list 中添加一個成員變量的描述,在 method_list 中增加 setter 與 getter 方法的描述,在屬性列表中增加一個屬性的描述,然后計算該屬性在對象中的偏移量,然后給出 setter 與 getter 方法對應(yīng)的實現(xiàn),在 setter 方法中從偏移量的位置開始賦值,在 getter 方法中從偏移量開始取值,為了能夠讀取正確字節(jié)數(shù),系統(tǒng)對象偏移量的指針類型進行了類型強轉(zhuǎn).
如何在@protocol和category中使用@property?
1、在 protocol 中使用 property 只會生成 setter 和 getter 方法聲明,我們使用屬性的目的,是希望遵守我協(xié)議的對象能實現(xiàn)該屬性

2、category 使用 @property 也是只會生成 setter 和 getter 方法的聲明,但是不會自動生成私有屬性,如果我們真的需要給 category 增加屬性的實現(xiàn),需要借助于運行時的兩個函數(shù):

1、動態(tài)添加屬性
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
參數(shù)一:id object : 給哪個對象添加屬性,這里要給自己添加屬性,用self。
參數(shù)二:void * == id  key : 屬性名,根據(jù)key獲取關(guān)聯(lián)對象的屬性的值,在objc_getAssociatedObject中通過次key獲得屬性的值并返回。
參數(shù)三:id value : 關(guān)聯(lián)的值,也就是set方法傳入的值給屬性去保存。
參數(shù)四:objc_AssociationPolicy policy : 策略,屬性以什么形式保存。
>>>>>
 typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
 OBJC_ASSOCIATION_ASSIGN = 0,  // 指定一個弱引用相關(guān)聯(lián)的對象
 OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // 指定相關(guān)對象的強引用,非原子性
 OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  // 指定相關(guān)的對象被復(fù)制,非原子性
 OBJC_ASSOCIATION_RETAIN = 01401,  // 指定相關(guān)對象的強引用,原子性
 OBJC_ASSOCIATION_COPY = 01403     // 指定相關(guān)的對象被復(fù)制,原子性   
};
獲得屬性
objc_getAssociatedObject(id object, const void *key);
參數(shù)一:id object : 獲取哪個對象里面的關(guān)聯(lián)的屬性。
參數(shù)二:void * == id  key : 什么屬性,與objc_setAssociatedObject中的key相對應(yīng),即通過key值取出value。
此時已經(jīng)成功給NSObject添加name屬性,并且NSObject對象可以通過點語法為屬性賦值。

下面這個也是一樣的:

-(void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, @"name",name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSString *)name
{
    return objc_getAssociatedObject(self, @"name");    
}
《RunTime字典轉(zhuǎn)模型》

通過給NSObject添加分類,聲明并實現(xiàn)使用Runtime字典轉(zhuǎn)模型的類方法:

+ (instancetype)modelWithDict:(NSDictionary *)dict

KVC字典轉(zhuǎn)模型和RunTime轉(zhuǎn)模型的區(qū)別:

KVC:KVC字典轉(zhuǎn)模型實現(xiàn)原理是遍歷字典中所有Key,然后去模型中查找相對應(yīng)的屬性名,要求屬性名與Key必須一一對應(yīng),字典中所有key必須在模型中存在。
RunTime:RunTime字典轉(zhuǎn)模型實現(xiàn)原理是遍歷模型中的所有屬性名,然后去字典查找相對應(yīng)的Key,也就是以模型為準,模型中有哪些屬性,就去字典中找那些屬性。

RunTime字典轉(zhuǎn)模型的優(yōu)點:當(dāng)服務(wù)器返回的數(shù)據(jù)過多,而我們只使用其中很少一部分時,沒有用的屬性就沒有必要定義成屬性浪費不必要的資源。只保存最有用的屬性即可。

字典轉(zhuǎn)模型簡要過程:
1、創(chuàng)建模型對象
2、使用class_copyIvarList方法copy成員屬性列表

unsigned int count = 0;
Ivar *ivarList = class_copyIvarList(self, &count);

參數(shù)一:__unsafe_unretained Class cls : 獲取哪個類的成員屬性列表。這里是self,因為誰調(diào)用分類中類方法,誰就是self。
參數(shù)二:unsigned int *outCount : 無符號int型指針,這里創(chuàng)建unsigned int型count,&count就是他的地址,保證在方法中可以拿到count的地址為count賦值。傳出來的值為成員屬性總數(shù)。
返回值:Ivar * : 返回的是一個Ivar類型的指針 。指針默認指向的是數(shù)組的第0個元素,指針+1會向高地址移動一個Ivar單位的字節(jié),也就是指向第一個元素。Ivar表示成員屬性。
3、遍歷成員屬性列表,獲得屬性列表

for (int i = 0 ; i < count; i++) {
     // 獲取成員屬性
     Ivar ivar = ivarList[i];
}

4、使用ivar_getName(ivar)獲得成員屬性名,因為成員屬性名返回的是C語言字符串,將其轉(zhuǎn)化成OC 字符串

NSString *propertyName = [NSString stringWithUTF8String:ivar_getName(ivar)];
或者ivar_getTypeEncoding(ivar)方法

5、獲得的成員屬性名是帶的成員屬性,去掉,獲得屬性名,也就是字典的key。

// 獲取key
NSString *key = [propertyName substringFromIndex:1];

6、獲取字典中key對應(yīng)的Value。

id value = dict[key];

7、給模型屬性賦值,并將模型返回

if (value) {
// KVC賦值:不能傳空
[objc setValue:value forKey:key];
}
return objc;

二級模型轉(zhuǎn)化方法:

+ (instancetype)modelWithDict:(NSDictionary *)dict{
    // 1.創(chuàng)建對應(yīng)類的對象
    id objc = [[self alloc] init];
    // count:成員屬性總數(shù)
    unsigned int count = 0;
   // 獲得成員屬性列表和成員屬性數(shù)量
    Ivar *ivarList = class_copyIvarList(self, &count);
    for (int i = 0 ; i < count; i++) {
        // 獲取成員屬性
        Ivar ivar = ivarList[i];
        // 獲取成員名
       NSString *propertyName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        // 獲取key
        NSString *key = [propertyName substringFromIndex:1];
        // 獲取字典的value key:屬性名 value:字典的值
        id value = dict[key];
        // 獲取成員屬性類型
        NSString *propertyType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        // 二級轉(zhuǎn)換
        // value值是字典并且成員屬性的類型不是字典,才需要轉(zhuǎn)換成模型
        if ([value isKindOfClass:[NSDictionary class]] && ![propertyType containsString:@"NS"]) {
            // 進行二級轉(zhuǎn)換
            // 獲取二級模型類型進行字符串截取,轉(zhuǎn)換為類名
            NSRange range = [propertyType rangeOfString:@"\""];
            propertyType = [propertyType substringFromIndex:range.location + range.length];
            range = [propertyType rangeOfString:@"\""];
            propertyType = [propertyType substringToIndex:range.location];
            // 獲取需要轉(zhuǎn)換類的類對象
           Class modelClass =  NSClassFromString(propertyType);
           // 如果類名不為空則進行二級轉(zhuǎn)換
            if (modelClass) {
                // 返回二級模型賦值給value
                value =  [modelClass modelWithDict:value];
            }
        }
        if (value) {
            // KVC賦值:不能傳空
            [objc setValue:value forKey:key];
        }
    }
    // 返回模型
    return objc;
}

總結(jié)

上述對RunTime的總結(jié)只是一些自己平時的積累,借鑒了一些好的博文資料加上自己的一些理解,還有很多東西沒有理解到位,還請多多指教。

最后編輯于
?著作權(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)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,099評論 0 9
  • 我們常常會聽說 Objective-C 是一門動態(tài)語言,那么這個「動態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,351評論 0 7
  • 本文轉(zhuǎn)載自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex閱讀 892評論 0 1
  • 《封神傳奇》周五上映。 (不出意外)被口水淹沒。 打一星的占70%多 有沒注意到—— 近年來華語“魔(奇)幻大片”...
    Sir電影閱讀 3,541評論 9 46
  • 現(xiàn)在轉(zhuǎn)念的速度越來越快了,從朝外的眼光到向內(nèi)看自己。今天身體非常不舒服,從公司出來剛好一條信息不對,我的語氣語調(diào)就...
    粟莎閱讀 217評論 0 1

友情鏈接更多精彩內(nèi)容