load、initialize
Load和Initialize往死了問是一種怎樣的體驗(yàn)色洞?
懶惰的 initialize 方法
load流程
initialize代碼
1戏锹、先調(diào)用父類superclass的initialize
2、再調(diào)用當(dāng)前類的initialize
void _class_initialize(Class cls)
{
Class supercls;
BOOL reallyInitialize = NO;
// 1. 未初始化過的父類調(diào)用 initialize 方法
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
{
// 2. 通過加鎖來(lái)設(shè)置 RW_INITIALIZING:正在初始化
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
}
if (reallyInitialize) {
// 3. 成功設(shè)置標(biāo)志位火诸,向當(dāng)前類發(fā)送 +initialize 消息
_setThisThreadIsInitializingClass(cls);
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
// 4. 完成初始化锦针,如果父類已經(jīng)初始化完成,設(shè)置 RW_INITIALIZED:初始化完成置蜀,
// 否則奈搜,在父類初始化完成之后再設(shè)置標(biāo)志位。
monitor_locker_t lock(classInitLock);
if (!supercls || supercls->isInitialized()) {
_finishInitializing(cls, supercls);
} else {
_finishInitializingAfter(cls, supercls);
}
return;
} else if (cls->isInitializing()) {
// 5. 當(dāng)前線程正在初始化當(dāng)前類盯荤,直接返回馋吗,否則,會(huì)等待其它線程初始化結(jié)束后秋秤,再返回
if (_thisThreadIsInitializingClass(cls)) {
return;
} else {
monitor_locker_t lock(classInitLock);
while (!cls->isInitialized()) {
classInitLock.wait();
}
return;
}
} else if (cls->isInitialized()) {
// 6. 初始化成功后宏粤,直接返回
return;
} else {
_objc_fatal("thread-safe class init in objc runtime is buggy!");
}
}
initialize與main的先后有特殊情況:在A類的load 方法中調(diào)用了B類的類方法
@implementation Father
+ (void)load {
NSLog(@"father==> load===%@", [Dog class]);
}
+(void)initialize {
NSLog(@"Father===>initialize");
}
@end
#warning 打印結(jié)果如下
2017-08-09 11:19:09.838 tests[34274:8415363] Dog===>initialize
2017-08-09 11:19:09.839 tests[34274:8415363] father==> load===Dog
2017-08-09 11:19:09.839 tests[34274:8415363] Dog==> load
2017-08-09 11:19:09.840 tests[34274:8415363] child==> load
2017-08-09 11:19:09.840 tests[34274:8415363] child + hahha==> load
2017-08-09 11:19:09.840 tests[34274:8415363] main
在 Initialize 方法內(nèi)部可以進(jìn)行一些不方便在編譯期進(jìn)行初始化的靜態(tài)變量的賦值
#warning Person.m
// int 等基本類型可以在編譯期進(jìn)行賦值
static int numCount = 0;
// 對(duì)象無(wú)法在編譯器進(jìn)行賦值
static NSMutableArray *dataSource;
+ (void)initialize {
if (self == [Person class]) {
// 不能在編譯期賦值的對(duì)象在這里進(jìn)行賦值
dataSource = [[NSMutableArray alloc] init];
}
}
避免父類的initialize被調(diào)用多次(如果當(dāng)前類沒有initialize方法就會(huì)調(diào)用父類的),可以通過類名判斷
+ (void)initialize {
if (self == [ClassName self]) {
// ... do the initialization ...
}
}
總結(jié):
1灼卢、load 方法內(nèi)部一般用來(lái)實(shí)現(xiàn) Method Swizzle
2绍哎、Initialize方法一般用來(lái)初始化全局變量或者靜態(tài)變量(對(duì)象類型)
一些NSObject方法
各種performSelector方法
/**
向接收方發(fā)送一條指定的消息,并返回消息的結(jié)果
@param aSelector 需要執(zhí)行的方法(方法允許您發(fā)送在運(yùn)行時(shí)才確定的消息鞋真。這意味著您可以傳遞一個(gè)變量選擇器作為參數(shù))
@param object1 參數(shù)1
@param object2 參數(shù)2
@return 返回消息的結(jié)果
*/
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
/**
在當(dāng)前線程中調(diào)用一個(gè)方法蛇摸,這個(gè)方法是異步的,必須在主線程調(diào)用灿巧,子線程無(wú)效赶袄,子線程可以用dispatch_after來(lái)代替
注意:內(nèi)部大概是創(chuàng)建一個(gè)定時(shí)器NSTimer
@param aSelector 需要執(zhí)行的方法
@param anArgument 傳遞的參數(shù)
@param delay 指定的時(shí)間之后
@param modes Runloop模式(默認(rèn)NSDefaultRunLoopMode)
*/
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
/**
取消方法調(diào)用請(qǐng)求
(對(duì)于使用performSelector:withObject:afterDelay:方法(僅限于此方法)注冊(cè)的執(zhí)行請(qǐng)求)
(不過僅限于當(dāng)前run loop揽涮,而不是所有的)
@param aTarget 指定對(duì)象
@param aSelector 指定方法
@param anArgument 指定參數(shù)
*/
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
/**
主線程上執(zhí)行某個(gè)對(duì)象的方法
@param aSelector 需要執(zhí)行的方法
@param arg 方法參數(shù)
@param wait YES,則當(dāng)前線程被阻塞
@param array Runloop模式(默認(rèn)NSDefaultRunLoopMode)
*/
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
/**
指定線程線程上執(zhí)行某個(gè)對(duì)象的方法
@param aSelector 需要執(zhí)行的方法
@param thr 指定線程
@param arg 方法參數(shù)
@param wait YES饿肺,則當(dāng)前線程被阻塞
@param array Runloop模式(默認(rèn)NSDefaultRunLoopMode)
*/
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
/**
后臺(tái)線程中調(diào)用接收者的方法
(這個(gè)方法會(huì)在程序中創(chuàng)建一個(gè)新的線程蒋困。由aSelector表示的方法必須像程序中的其它新線程一樣去設(shè)置它的線程環(huán)境)
@param aSelector 需要執(zhí)行的方法
@param arg 方法參數(shù)
*/
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;
methodForSelector
NSObject類提供了兩個(gè)方法來(lái)獲取一個(gè)selector對(duì)應(yīng)的方法實(shí)現(xiàn)的地址,如下所示:
- (IMP)methodForSelector:(SEL)aSelector
+ (IMP)instanceMethodForSelector:(SEL)aSelector
獲取到了方法實(shí)現(xiàn)的地址敬辣,我們就可以直接將IMP以函數(shù)形式來(lái)調(diào)用雪标。
對(duì)于methodForSelector:方法,如果接收者是一個(gè)對(duì)象溉跃,則aSelector應(yīng)該是一個(gè)實(shí)例方法汪疮;如果接收者是一個(gè)類痛单,則aSelector應(yīng)該是一個(gè)類方法脯燃。
對(duì)于instanceMethodForSelector:方法帮辟,其只是向類對(duì)象索取實(shí)例方法的實(shí)現(xiàn)。如果接收者的實(shí)例無(wú)法響應(yīng)aSelector消息龄糊,則產(chǎn)生一個(gè)錯(cuò)誤逆粹。
instancesRespondToSelector與respondToSelector的區(qū)別
1、 instancesRespondToSelector只能寫在類名后面炫惩,respondsToSelector可以寫在類名和實(shí)例名后面僻弹。
2、[類 instancesRespondToSelector]判斷的是該類的實(shí)例是否包含某方法他嚷,等效于:[該類的實(shí)例respondsToSelector]蹋绽。
3、[類 respondsToSelector]用于判斷是否包含某個(gè)類方法筋蓖。
+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
- (BOOL)respondsToSelector:(SEL)aSelector;
-(BOOL)conformsToProtocol 與 +(BOOL)conformsToProtocol 的區(qū)別
// 當(dāng)前類檢測(cè)不到卸耘,不會(huì)檢查父類
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
// 當(dāng)前類檢測(cè)不到,會(huì)檢查父類
+ (BOOL)conformsToProtocol:(Protocol *)protocol;
class扭勉、superclass、isKindOfClass苛聘、isMemberOfClass
// 該方法返回類對(duì)象
+ (Class)class;
// 獲取接收者的父類類對(duì)象
+ (Class)superclass;
// 查看一個(gè)類是否是另一個(gè)類的子類,
+ (BOOL)isSubclassOfClass:(Class)aClass;
isKindOfClass涂炎、isMemberOfClass
- 聯(lián)系:兩者都能檢測(cè)一個(gè)對(duì)象是不是某個(gè)類的成員
- 區(qū)別:isKindOfClass可以檢測(cè)到父類,如:ClassA *a = [ClassA alloc] init];,[a isKindOfClass:[NSObject class]] 可以檢查出 a 是否是 NSObject派生類
+ (Class)class设哗、- (Class)class區(qū)別
BBObj *obj = [BBObj new];
1唱捣、[BBObj class] = BBObj類對(duì)象
2、[obj j class] = object_getClass(obj) = obj->isa,就是BBObj類對(duì)象
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
isKindOfClass网梢、isMemberOfClass
源碼
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
例子:
BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [[UIButton class] isKindOfClass:[UIButton class]];
BOOL res4 = [[UIButton class] isMemberOfClass:[UIButton class]];
NSLog(@"%d %d %d %d", res1, res2, res3, res4);//輸出 1 0 0 0
解析:
(1)+ (BOOL)isKindOfClass:(Class)cls方法內(nèi)部震缭,會(huì)先去獲得object_getClass的類,而object_getClass的源碼實(shí)現(xiàn)是去調(diào)用當(dāng)前類的obj->getIsa()战虏,最后在ISA()方法中獲得meta class的指針拣宰。(+ (BOOL)isKindOfClass:(Class)cls是用meta class來(lái)對(duì)比cls)
(2)接著在isKindOfClass中有一個(gè)循環(huán)党涕,先判斷class是否等于meta class,不等就繼續(xù)循環(huán)判斷是否等于super class巡社,不等再繼續(xù)取super class膛堤,如此循環(huán)下去。
(3)[NSObject class]執(zhí)行完之后調(diào)用isKindOfClass晌该,第一次判斷先判斷NSObject 和 NSObject的meta class是否相等肥荔,之前講到meta class的時(shí)候放了一張很詳細(xì)的圖,從圖上我們也可以看出朝群,NSObject的meta class與本身不等燕耿。接著第二次循環(huán)判斷NSObject與meta class的superclass是否相等。還是從那張圖上面我們可以看到:Root class(meta) 的superclass 就是 Root class(class)姜胖,也就是NSObject本身誉帅。所以第二次循環(huán)相等,于是第一行res1輸出應(yīng)該為YES谭期。
其他三個(gè)同樣這么擼下去堵第,全是0
特殊的
NSArray *arr = [[NSArray alloc] init];
BOOL a = [arr isMemberOfClass:[NSArray class]];// a = NO;因?yàn)閍rr是__NSArray0, NSArray是一個(gè)抽象的基類。這種模式就是了[類簇模式](http://blog.csdn.net/u013016828/article/details/41720353).
自定義對(duì)象的歸檔與解檔大神終結(jié)入口1----大神終結(jié)入口2
NSData *cityData = [NSKeyedArchiver archivedDataWithRootObject:model];// model要現(xiàn)實(shí)NSCoding協(xié)議隧出,initWithCoder:和encodeWithCoder
model = [NSKeyedUnarchiver unarchiveObjectWithData:data];
// 1.遵循NSCoding協(xié)議
@interface Person : NSObject <NSCoding>
// 2.設(shè)置屬性
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic) NSInteger age;
@end
@implementation Person
// 解檔
- (id)initWithCoder:(NSCoder *)aDecoder {
// 父類現(xiàn)實(shí)就調(diào)用[super initWithCoder:aDecoder]
// 父類沒有現(xiàn)實(shí)[super init]
if (self = [super init]) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
// 歸檔
- (void)encodeWithCoder:(NSCoder *)aCoder {
// 父類現(xiàn)實(shí)就調(diào)用[super encodeWithCoder:aCoder];
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInteger:self.age forKey:@"age"];
}
@end