OC--load座柱、initialize迷帜、一些NSObject方法

load、initialize

Load和Initialize往死了問是一種怎樣的體驗(yàn)色洞?
懶惰的 initialize 方法

+ load與+ initialize的異同

load流程

load.png

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!");
    }
}

initalize.png

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

解析:


Runtime 各種Class關(guān)系.jpg

(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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末踏志,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子胀瞪,更是在濱河造成了極大的恐慌针余,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凄诞,死亡現(xiàn)場(chǎng)離奇詭異圆雁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)帆谍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門伪朽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人汛蝙,你說我怎么就攤上這事烈涮。” “怎么了窖剑?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵坚洽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我西土,道長(zhǎng)讶舰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮跳昼,結(jié)果婚禮上般甲,老公的妹妹穿的比我還像新娘。我一直安慰自己庐舟,他們只是感情好欣除,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挪略,像睡著了一般历帚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上杠娱,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天挽牢,我揣著相機(jī)與錄音,去河邊找鬼摊求。 笑死禽拔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的室叉。 我是一名探鬼主播睹栖,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼茧痕!你這毒婦竟也來(lái)了野来?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤踪旷,失蹤者是張志新(化名)和其女友劉穎曼氛,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體令野,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡舀患,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了气破。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片聊浅。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖现使,靈堂內(nèi)的尸體忽然破棺而出低匙,到底是詐尸還是另有隱情,我是刑警寧澤朴下,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布努咐,位于F島的核電站苦蒿,受9級(jí)特大地震影響殴胧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一团滥、第九天 我趴在偏房一處隱蔽的房頂上張望竿屹。 院中可真熱鬧,春花似錦灸姊、人聲如沸拱燃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)碗誉。三九已至,卻和暖如春父晶,著一層夾襖步出監(jiān)牢的瞬間哮缺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工甲喝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尝苇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓埠胖,卻偏偏與公主長(zhǎng)得像糠溜,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子直撤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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