YYModel--源碼解析

via http://www.lijianfei.cn/2016/07/13/learnYYModelSomeGain/?utm_source=tuicool&utm_medium=referral

YYModel 作者性能優(yōu)化的幾個 Tip:

緩存

Model JSON 轉(zhuǎn)換過程中需要很多類的元數(shù)據(jù),如果數(shù)據(jù)足夠小,則全部緩存到內(nèi)存中。

查表

當遇到多項選擇的條件時,要盡量使用查表法實現(xiàn),比如 switch/case,C Array,如果查表條件是對象,則可以用 NSDictionary 來實現(xiàn)。

避免 KVC

Key-Value Coding 使用起來非常方便,但性能上要差于直接調(diào)用 Getter/Setter,所以如果能避免 KVC 而用 Getter/Setter 代替,性能會有較大提升。

避免 Getter/Setter 調(diào)用

如果能直接訪問 ivar,則盡量使用 ivar 而不要使用 Getter/Setter 這樣也能節(jié)省一部分開銷。

避免多余的內(nèi)存管理方法

在 ARC 條件下,默認聲明的對象是 strong 類型的,賦值時有可能會產(chǎn)生 retain/release 調(diào)用,如果一個變量在其生命周期內(nèi)不會被釋放,則使用 unsafe_unretained 會節(jié)省很大的開銷。

訪問具有 weak 屬性的變量時,實際上會調(diào)用 objc_loadWeak() 和 objc_storeWeak() 來完成,這也會帶來很大的開銷,所以要避免使用 weak 屬性。

創(chuàng)建和使用對象時,要盡量避免對象進入 autoreleasepool,以避免額外的資源開銷。

遍歷容器類時,選擇更高效的方法

相對于 Foundation 的方法來說,CoreFoundation 的方法有更高的性能,用 CFArrayApplyFunction() 和 CFDictionaryApplyFunction() 方法來遍歷容器類能帶來不少性能提升,但代碼寫起來會非常麻煩。

盡量用純 C 函數(shù)、內(nèi)聯(lián)函數(shù)

使用純 C 函數(shù)可以避免 ObjC 的消息發(fā)送帶來的開銷。如果 C 函數(shù)比較小,使用 inline 可以避免一部分壓棧彈棧等函數(shù)調(diào)用的開銷。

減少遍歷的循環(huán)次數(shù)

在 JSON 和 Model 轉(zhuǎn)換前,Model 的屬性個數(shù)和 JSON 的屬性個數(shù)都是已知的,這時選擇數(shù)量較少的那一方進行遍歷,會節(jié)省很多時間。

Runtime MetaClass
Objective-c 中的 每一個類 如: Person類 其實包含兩部分
普通類(類本身)如 person 類本身(我們創(chuàng)建的類)

元類(系統(tǒng)創(chuàng)建)Meta Person類,由系統(tǒng)默認創(chuàng)建
這個創(chuàng)建Meta Person類的過程,我們不知道而已

關(guān)于meta class的系統(tǒng)方法
判斷一個objc_class實例是否是Meta類的Class

BOOL class_isMetaClass(Class cls)

獲取一個NSObject類對應(yīng)的Meta類的Class

Class objc_getMetaClass(const char *name)

類 與 Meta類
類,存放對象的相關(guān)數(shù)據(jù)實例成員變量
實例方法

Meta類,存放類的先關(guān)數(shù)據(jù)類的成員變量(static 類成員變量)
類方法(+開頭的方法)

區(qū)分 對象方法 與 類方法 調(diào)用過程向一個對象發(fā)送消息通過對象的 isa指針找到 對象所屬類對應(yīng)的的 objc_class結(jié)構(gòu)體實例
然后開始查找objc_class實例的 method_list 查找 Method

向一個類發(fā)送消息通過類Class的 isa指針找到 Meta類對應(yīng)的 objc_class結(jié)構(gòu)體實例
然后從objc_class實例的 method_list 查找 Method

結(jié)合 Demo 中 Runtime_MataClass

import "RuntimeViewController.h"

@interface RuntimeViewController ()

@end

void ReportFunction(id self, SEL _cmd)
{
//1. 對象
NSLog(@"This object is %p.", self);

//2. 對象所屬的類
NSLog(@"Class is %@", [self class]);

//3. 所屬類的父類
NSLog(@"super is %@.",[self superclass]);

//4. 每一個類都有兩部分
//類的第一部分、類本身
//類的第二部分、元類
Class currentClass = [self class];
for (int i = 1; i < 10; i++)//i的次數(shù)隨便改都可以
{
NSLog(@"Following the isa pointer times = %d, ClassValue = %@, ClassAddress = %p", i, currentClass, currentClass);

//通過Class的 isa指針 找到 MetaClass
currentClass = object_getClass(currentClass);
}

//5. NSObject類本身
NSLog(@"NSObject's class is %p", [NSObject class]);

//6. NSObject類的元類
NSLog(@"NSObject's meta class is %p", object_getClass([NSObject class]));
}

@implementation RuntimeViewController

  • (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

[self setOSProps];

//運行時創(chuàng)建類
[self createClass];

[self imp_implementationWithBlock];
}

  • (void)setOSProps
    {
    self.view.backgroundColor = [UIColor whiteColor];
    self.navigationItem.title = @"Runtime Meta Learn";
    self.navigationController.navigationBar.translucent = YES;
    }

  • (void)createClass
    {
    //1. 創(chuàng)建一個Class
    Class MyClass = objc_allocateClassPair([NSObject class],
    "myclass",
    0);

//2. 添加一個NSString的變量,第四個參數(shù)是對其方式,第五個參數(shù)是參數(shù)類型
if (class_addIvar(MyClass, "itest", sizeof(NSString *), 0, "@")) {
NSLog(@"add ivar success");
}

//3. 添加一個函數(shù)
class_addMethod(MyClass,
@selector(report),
(IMP)ReportFunction,
"v@:");

//4. 注冊這個類到runtime系統(tǒng)中就可以使用他了
objc_registerClassPair(MyClass);

//5. 測試創(chuàng)建的類
[self test:MyClass];
}

  • (void)report {
    //什么都不做,只是為了OC對象能夠調(diào)用到c函數(shù)
    }

  • (void)test:(Class)class {

//1.
id obj = [[class alloc] init];

//2.
[obj report];
}

控制臺輸出如下

2016-07-12 11:37:23.281 LJFSourceCodeLearn[1353:75617] add ivar success
2016-07-12 11:37:23.281 LJFSourceCodeLearn[1353:75617] This object is 0x7ff42bc291d0.
2016-07-12 11:37:23.281 LJFSourceCodeLearn[1353:75617] Class is myclass
2016-07-12 11:37:23.282 LJFSourceCodeLearn[1353:75617] super is NSObject.
2016-07-12 11:37:23.282 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 1, ClassValue = myclass, ClassAddress = 0x7ff42bf0f180
2016-07-12 11:37:23.282 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 2, ClassValue = myclass, ClassAddress = 0x7ff42bf36550
2016-07-12 11:37:23.282 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 3, ClassValue = NSObject, ClassAddress = 0x110ecb198
2016-07-12 11:37:23.282 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 4, ClassValue = NSObject, ClassAddress = 0x110ecb198
2016-07-12 11:37:23.282 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 5, ClassValue = NSObject, ClassAddress = 0x110ecb198
2016-07-12 11:37:23.282 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 6, ClassValue = NSObject, ClassAddress = 0x110ecb198
2016-07-12 11:37:23.304 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 7, ClassValue = NSObject, ClassAddress = 0x110ecb198
2016-07-12 11:37:23.304 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 8, ClassValue = NSObject, ClassAddress = 0x110ecb198
2016-07-12 11:37:23.304 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 9, ClassValue = NSObject, ClassAddress = 0x110ecb198
2016-07-12 11:37:23.304 LJFSourceCodeLearn[1353:75617] NSObject's class is 0x110ecb170
2016-07-12 11:37:23.304 LJFSourceCodeLearn[1353:75617] NSObject's meta class is 0x110ecb198

方法object_getClass(id obj)會根據(jù) Class的私有成員變量isa指針 找到一個類,有兩種情況:對象的isa >>> 所屬類
類的isa >>> Meta 類
Meta類的isa >>> Meta NSObject
Meta NSObject的isa >>> 永遠指向自己,形成環(huán)路

times = 2時、根據(jù)myclass->isa指針,找到其對應(yīng)的 Meta myclass
times = 3時、根據(jù) Meta myclass->isa 指針,找到了 Meta NSObject
times = 4,5,6,7,8…、根據(jù) 元類NSObject 的 isa指針,最后都是指向自己

使用imp_implementationWithBlock()替代上面例子需要一個輔助的static c函數(shù)來完成運行時創(chuàng)建Class.

  • (void)imp_implementationWithBlock
    {
    //////////////////////////////////////////////////////
    ///創(chuàng)建一個類
    //////////////////////////////////////////////////////
    Class People = objc_allocateClassPair([NSObject class], "People", 0);

//////////////////////////////////////////////////////
///添加兩個變量
//////////////////////////////////////////////////////

if (class_addIvar(People, "name", sizeof(NSString *), 0, @encode(NSString *))) {
NSLog(@"add ivar name success");
}
if (class_addIvar(People, "age", sizeof(int), 0, @encode(int))) {
NSLog(@"add ivar age success");
}

//////////////////////////////////////////////////////
///創(chuàng)建方法的SEL
//////////////////////////////////////////////////////
SEL selector = sel_registerName("talk:");

//////////////////////////////////////////////////////
///創(chuàng)建方法的IMP指針,并指向Block給出的代碼
//////////////////////////////////////////////////////
IMP impl = imp_implementationWithBlock(^(id self, NSString *arg1){

//age變量值
//通過KVC
//int age = (int)[[self valueForKey:@"age"] integerValue];

//通過Ivar
Ivar ageIvar = class_getInstanceVariable([self class], "age");
int age = (int)[object_getIvar(self, ageIvar) integerValue];

//name變量值
//通過KVC
//NSString *name = [self valueForKey:@"name"];

//通過Ivar
Ivar nameIvar = class_getInstanceVariable([self class], "name");
NSString *name = object_getIvar(self, nameIvar);

NSLog(@"age = %d, name = %@, msgSay = %@", age, name, arg1);
});

//////////////////////////////////////////////////////
///添加一個方法, 將SEL與IMP組裝成一個Method結(jié)構(gòu)體實例,添加到Class中的 method_list數(shù)組
//////////////////////////////////////////////////////
class_addMethod(People, selector, impl, "v@:@");

//////////////////////////////////////////////////////
///注冊這個類到系統(tǒng)
//////////////////////////////////////////////////////
objc_registerClassPair(People);

//////////////////////////////////////////////////////
///生成一個實例
//////////////////////////////////////////////////////
id instanceP = [[People alloc] init];

//////////////////////////////////////////////////////
///給Ivar賦值
//////////////////////////////////////////////////////

//通過KVC賦值
[instanceP setValue:@"變量字符串值" forKey:@"name"];
//[instanceP setValue:@19 forKey:@"age"];

//通知Ivar賦值
Ivar ageIvar = class_getInstanceVariable(People, "age");
object_setIvar(instanceP, ageIvar, @19);

//////////////////////////////////////////////////////
///發(fā)送消息
//////////////////////////////////////////////////////
((void (*)(id, SEL, NSString *))(void *) objc_msgSend)(instanceP, selector, @"參數(shù)值");

//////////////////////////////////////////////////////
///釋放對象、銷毀類
//////////////////////////////////////////////////////
instanceP = nil;
objc_disposeClassPair(People);
}

方法的編碼格式:v@: >>> 返回值void,參數(shù)1:self,參數(shù)2:SEL >>> - (void)funcName;
v@:@ >>> 返回值void,參數(shù)1:self,參數(shù)2:SEL, 參數(shù)3:NSString* >>> - (void)funcName:(NSSring *)name;

對應(yīng)的Objective-C方法的SEL也應(yīng)該是 >>> talk:帶一個參數(shù)
imp_implementationWithBlock()接收一個添加方法被調(diào)用時的回調(diào)Block其格式為: method_return_type ^(id self, method_args …)

Meta Class 元類

Class objc_allocateClassPair(Class superclass,
const char *name,
size_t extraBytes)

函數(shù)名 objc_allocateClassPair.. 中的 ClassPair意思是說一對Class.
但是該函數(shù)只返回了一個Class
沒有被返回的另一個Class就是 MetaClass 元類,由系統(tǒng)創(chuàng)建.
Meta Class與普通類Class 都是結(jié)構(gòu)體 objc_class實例

Objective-c中的 對象 與 類

經(jīng)常寫獲取一個對象的類型Class的代碼

Class cls = [Person class];

就用到了Class這個數(shù)據(jù)結(jié)構(gòu),其定義為如下,可以得到Class是 結(jié)構(gòu)體objc_class實例的 指針類型

typedef struct objc_class *Class;

那么能夠成為一個類(Class)的數(shù)據(jù)結(jié)構(gòu)為

struct objc_class {

/*
又指向一個結(jié)構(gòu)體objc_class實例
類的isa指針,指向類的 元類 MetaClass
*/
Class isa OBJC_ISA_AVAILABILITY;

if !OBJC2

/**
指向其父類(普通類)
*/
Class super_class OBJC2_UNAVAILABLE;

const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
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;

struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;

endif

} OBJC2_UNAVAILABLE;
/* Use Class instead of struct objc_class * */

而Class數(shù)據(jù)結(jié)構(gòu)中最重要的一個數(shù)據(jù)項是 Class isa; 這樣的isa指針.
對象
能夠成為一個對象(Object)的數(shù)據(jù)結(jié)構(gòu)如下,可以看到對象數(shù)據(jù)結(jié)構(gòu)中最重要的就是Class isa;指針項

struct objc_object {

/**
對象的isa指針,指向其 所屬類本身(不是元類)
*/
Class isa OBJC_ISA_AVAILABILITY;
};

Class是struct objc_class的指針類型,那么struct objc_object也有其對于的指針類型
調(diào)用類方法與對象方法給對象發(fā)送消息時,消息是在對象所屬類的方法列表method_list或cache中查詢Method
給類發(fā)消息時,消息是在這個類的元類 meta class的方法列表或緩存中查詢Method

所以也就是說對象方法與類方法存放地點的不同
對象方法、存放在 類本身
類方法、存放在 類的元類MetaClass

所以,元類是必不可少的,它存儲了類的所有類方法
對象、類、元類三者之間的關(guān)系圖



對象的 isa指針 指向 所屬類
每一個類,都有一個 isa指針 指向一個 唯一的 MetaClass
每一個Meta Class,擁有的 isa指針 都指向 頂層 NSObject Meta Class
最上層的Meta Class(Meta NSObject)的isa指針指向自己,形成一個 回路
類本身的 super_class 指向其父類,如果該類為根類則值為 nil
每一個 MetaClass 的 super_class指針 都指向其 原本Class的superClass對應(yīng)的 MetaClass特列: 但是最上層的 Meta Class 的 super_class 指針,指向的卻是 NSObject 原本 Class

所有的 類 NSObject子類 都是 objc_class結(jié)構(gòu)體 的實例
NSObject類獲取Class的源碼如下:

  • (Class)class {
    return self;
    }

Class是 objc_class結(jié)構(gòu)體實例的指針變量類型
那么+[NSObject class]返回就是一個 objc_class結(jié)構(gòu)體實例
而 self 一般是 一個指向?qū)ο蟮刂返闹羔?br> 而此時的 self 所代表的就是 NSObject類本身

那么可以猜測: NSObject 是 objc_class的實例

@implementation RuntimeViewController

  • (void)viewDidLoad {
    [super viewDidLoad];

//名字
NSLog(@"%@", [RuntimeViewController class]);

//多次輸出其地址
NSLog(@"%p", [self class]);
NSLog(@"%p", [self class]);
NSLog(@"%p", [RuntimeViewController class]);

}

輸出結(jié)果

2016-07-12 13:30:27.264 LJFSourceCodeLearn[1640:121986] RuntimeViewController
2016-07-12 13:30:27.265 LJFSourceCodeLearn[1640:121986] 0x101d03cd0
2016-07-12 13:30:27.265 LJFSourceCodeLearn[1640:121986] 0x101d03cd0
2016-07-12 13:30:27.265 LJFSourceCodeLearn[1640:121986] 0x101d03cd0

每一個NSObject類在運行階段,是按照一個單例保存

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,084評論 2 375

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,753評論 0 9
  • Objective-C語言是一門動態(tài)語言,它將很多靜態(tài)語言在編譯和鏈接時期做的事放到了運行時來處理。這種動態(tài)語言的...
    有一種再見叫青春閱讀 603評論 0 3
  • Objective-C語言是一門動態(tài)語言,他將很多靜態(tài)語言在編譯和鏈接時期做的事情放到了運行時來處理。這種動態(tài)語言...
    tigger丨閱讀 1,417評論 0 8
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡介 Runt...
    樂樂的簡書閱讀 2,148評論 0 9
  • 簡介 Runtime 又叫運行時,是一套底層的 C 語言 API,其為 iOS 內(nèi)部的核心之一,我們平時編寫的 O...
    專業(yè)男神經(jīng)閱讀 914評論 0 2