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 …)
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類在運行階段,是按照一個單例保存