Category的本質(zhì)<二>load,initialize方法

Category的本質(zhì)<一>
Category的本質(zhì)<三>關(guān)聯(lián)對(duì)象
面試題1:Category中有l(wèi)oad方法嗎?load方法是什么時(shí)候調(diào)用?
面試題2:load,initialize的區(qū)別是什么?它們?cè)贑ategory中的調(diào)用順序以及出現(xiàn)繼承時(shí)它們之間的調(diào)用過(guò)程是怎么樣的?
那么這篇文章主要就是回答這兩個(gè)問(wèn)題。

load方法

load方法什么時(shí)候調(diào)用?
load方法是在runtime加載類和分類的時(shí)候調(diào)用。
我們創(chuàng)建了一個(gè)Person類和它的兩個(gè)分類,然后重寫(xiě)了各自的load方法:

//Person
+ (void)load{
    
    NSLog(@"Person + load");
}

//Person+Test1
+ (void)load{
    
    NSLog(@"Person (Test1) + load");
}

//Person+Test2
+ (void)load{
    
    NSLog(@"Person (Test2) + load");
}

然后我們什么也不做,運(yùn)行代碼,看到打印結(jié)果:

2018-07-24 20:45:08.369170+0800 interview - Category[14157:409819] Person + load
2018-07-24 20:45:08.371806+0800 interview - Category[14157:409819] Person (Test1) + load
2018-07-24 20:45:08.373190+0800 interview - Category[14157:409819] Person (Test2) + load

通過(guò)打印結(jié)果我們可以看到Person及其分類的load方法都被調(diào)用了,這就證實(shí)了load方法是由runtime加載類和分類的時(shí)候調(diào)用的。
然后我們?cè)俳oPerson類及其子類創(chuàng)建一個(gè)+ (void)test方法并實(shí)現(xiàn)它:

//Person
+ (void)test{
    
    NSLog(@"Person + test");
}

//Person+Test1
+ (void)test{
    
    NSLog(@"Person (Test1) + test");
}

//Person+Test2
+ (void)test{
    
    NSLog(@"Person (Test2) + test");
}

然后用Person類對(duì)象去調(diào)用test方法:

[Person test];

得到打印結(jié)果:

2018-07-24 21:07:32.886316+0800 interview - Category[14670:428685] Person + load
2018-07-24 21:07:32.887195+0800 interview - Category[14670:428685] Person (Test1) + load
2018-07-24 21:07:32.887461+0800 interview - Category[14670:428685] Person (Test2) + load
2018-07-24 21:07:33.050735+0800 interview - Category[14670:428685] Person (Test2) + test

通過(guò)打印結(jié)果我們可以看到,Person (Test2)的test方法被調(diào)用了,這個(gè)很好理解因?yàn)槲覀冊(cè)?a href="http://www.lxweimin.com/p/da463f413de7" target="_blank">Category的本質(zhì)<一>中說(shuō)的很清楚了,如果分類和類同時(shí)實(shí)現(xiàn)了一個(gè)方法,那么分類中的方法和類中的方法都會(huì)保存下來(lái)存入內(nèi)存中,并且分類的方法在前,類的方法在后,這樣在調(diào)用的時(shí)候就會(huì)首先找到分類的方法,給人的感覺(jué)就是好像類的方法被覆蓋了。
那么問(wèn)題來(lái)了,同樣是類方法,同樣是分類中實(shí)現(xiàn)了類的方法,為什么load方法不像test方法一樣,調(diào)用分類的實(shí)現(xiàn),而是類和每個(gè)分類中的load方法都被調(diào)用了呢?load方法到底有什么不同呢?
要想弄清楚其中的原理,我們還是要從runtime的源碼入手:

  • 1.找到objc-os.mm這個(gè)文件,然后找到這個(gè)文件的void _objc_init(void)這個(gè)方法,runtime的初始化都是在這個(gè)方法里面完成。
  • 2.這個(gè)方法的最后一行調(diào)用了函數(shù)_dyld_objc_notify_register(&map_images, load_images, unmap_image);,我們點(diǎn)進(jìn)load_images,這是加載模塊的意思。
    8855F735-18CF-41EA-B389-DED9B00AB168.png
  • 3.
    19E2523B-9492-4EA6-8510-2323D855B001.png
  • 4
    BC355F3A-CE9D-4C72-9BDE-C804D6B8A0D9.png
  • 5我們點(diǎn)進(jìn)call_class_loads();這個(gè)方法查看對(duì)類的load方法的調(diào)用過(guò)程:
    F892FCFD-7A59-4038-B5A0-BF892B13FD7E.png
  • 6.然后我們?cè)冱c(diǎn)進(jìn)call_category_loads()查看對(duì)分類的load方法的調(diào)用過(guò)程:
    AFACEDCA-1420-43D6-BB9A-0583A91B73A5.png

    那么這樣我們就搞清楚了為什么load方法不是像test方法一樣,執(zhí)行分類的實(shí)現(xiàn)

因?yàn)閘oad方法的調(diào)用并不是objc_msgSend機(jī)制,它是直接找到類的load方法的地址,然后調(diào)用類的load方法,然后再找到分類的load方法的地址,再去調(diào)用它。

而test方法是通過(guò)消息機(jī)制去調(diào)用的。首先找到類對(duì)象,由于test方法是類方法,存儲(chǔ)在元類對(duì)象中,所以通過(guò)類對(duì)象的isa指針找到元類對(duì)象,然后在元類對(duì)象中尋找test方法,由于分類也實(shí)現(xiàn)了test方法,所以分類的test方法是在類的test方法的前面,首先找到了分類的test方法,然后去調(diào)用它。

有繼承關(guān)系時(shí)load方法的調(diào)用順序

通過(guò)上面的分析我們確定了load方法的一個(gè)調(diào)用規(guī)則:先調(diào)用所有類的load方法,然后再調(diào)用所有分類的load方法。

下面我們?cè)賱?chuàng)建一個(gè)Student類繼承自Person類,并且為Student類創(chuàng)建兩個(gè)子類Student (Test1), Student (Test2),并且覆寫(xiě)load方法:

//Student
+ (void)load{
    
    NSLog(@"Student + load");
}

//Student (Test1)
+ (void)load{
    
    NSLog(@"Student (Test1) + load");
}

//Student (Test2)
+ (void)load{
    
    NSLog(@"Student (Test2) + load");
}

然后我們運(yùn)行一下程序,看打印結(jié)果:

2018-07-25 15:45:58.605156+0800 interview - Category[13869:359239] Person + load
2018-07-25 15:45:58.605684+0800 interview - Category[13869:359239] Student + load
2018-07-25 15:45:58.606420+0800 interview - Category[13869:359239] Student (Test2) + load
2018-07-25 15:45:58.606870+0800 interview - Category[13869:359239] Person (Test1) + load
2018-07-25 15:45:58.607293+0800 interview - Category[13869:359239] Student (Test1) + load
2018-07-25 15:45:58.607514+0800 interview - Category[13869:359239] Person (Test2) + load
2018-07-25 15:45:58.812025+0800 interview - Category[13869:359239] Person (Test2) + test

通過(guò)打印結(jié)果我們可以很清楚的看見(jiàn),Person類和Student類的load方法先被調(diào)用,然后調(diào)用分類的load方法。再運(yùn)行多次,都是Person類和Student類的load方法先被調(diào)用,然后分類的方法才被調(diào)用。并且總是Person類的load在Student類的load方法前面被調(diào)用,這會(huì)不會(huì)和編譯順序有關(guān)呢?我們改變一下編譯順序看看:

TARGETS -> Build Phases -> Complle Sources中文件的放置順序就是文件的編譯順序。

381E32DF-9B1D-4752-AFFB-D3925E70579D.png

目前是Person類在Student類的前面編譯,現(xiàn)在我們把Student類放到Person類的前面編譯:


4A6F780D-FC13-48B4-9C7C-88ADB832D617.png

然后我們?cè)龠\(yùn)行一下程序,查看打印結(jié)果:

2018-07-25 15:56:07.270034+0800 interview - Category[14070:367686] Person + load
2018-07-25 15:56:07.270619+0800 interview - Category[14070:367686] Student + load
2018-07-25 15:56:07.271107+0800 interview - Category[14070:367686] Student (Test2) + load
2018-07-25 15:56:07.271494+0800 interview - Category[14070:367686] Person (Test1) + load
2018-07-25 15:56:07.271762+0800 interview - Category[14070:367686] Student (Test1) + load
2018-07-25 15:56:07.272118+0800 interview - Category[14070:367686] Person (Test2) + load
2018-07-25 15:56:07.433068+0800 interview - Category[14070:367686] Person (Test2) + test

我們發(fā)現(xiàn)還是Person類的load方法在Student類前面被調(diào)用,所以好像和編譯順序無(wú)關(guān)呀。那么我們就需要思考一下是不是由于Student和Person之間的繼承關(guān)系導(dǎo)致的呢?
為了搞清楚這個(gè)問(wèn)題,我們只能從runtime的源碼入手。

  • 1.objc-os.mm中void _objc_init(void)這個(gè)入口方法,點(diǎn)進(jìn)load_images.
  • 2.在void load_images(const char *path __unused, const struct mach_header *mh)這個(gè)方法中,最后有個(gè)call_load_methods();方法,點(diǎn)擊進(jìn)去。
  • 3.在void call_load_methods(void)這個(gè)方法中,找到call_class_loads();這個(gè)方法,上面已經(jīng)講到,這是調(diào)用類的load方法。點(diǎn)進(jìn)去。
  • 4


    E6DFD1D9-2810-40D5-86E6-290482445E52.png
  • 5.為了搞清楚這里的classes數(shù)組的來(lái)歷,我們回退到void load_images(const char *path __unused, const struct mach_header *mh)這個(gè)方法,這個(gè)方法中有一個(gè)prepare_load_methods((const headerType *)mh);這個(gè)方法,根據(jù)方法名可能和我們的問(wèn)題有關(guān)。因此我們點(diǎn)進(jìn)這個(gè)方法查看一下
  • 6.
    5167447D-DDDA-4398-ADFF-9DE78DDEBB3D.png
  • 7.點(diǎn)進(jìn)
schedule_class_load(remapClass(classlist[i]));

這個(gè)方法:
571870F3-8F9C-4C24-82A6-3305ECE9B3ED.png

通過(guò)這個(gè)方法我們就可以很清晰的看到,當(dāng)要把一個(gè)類加入最終的這個(gè)classes數(shù)組的時(shí)候,會(huì)先去上溯這個(gè)類的父類,先把父類加入這個(gè)數(shù)組。
由于在classes數(shù)組中父類永遠(yuǎn)在子類的前面,所以在加載類的load方法時(shí)一定是先加載父類的load方法,再加載子類的load方法。

類的load方法調(diào)用順序搞清楚了我們?cè)賮?lái)看一下分類的load方法調(diào)用順序

我們還是看一下void prepare_load_methods(const headerType *mhdr)這個(gè)函數(shù)

66AC18CB-9401-4FC4-8F5E-3FAD776954ED.png

通過(guò)這個(gè)分析我們就能知道,分類的load方法加載順序很簡(jiǎn)單,就是誰(shuí)先編譯的,誰(shuí)的load方法就被先加載。

下面我們通過(guò)打印結(jié)果驗(yàn)證一下,這是編譯順序:
C0E77171-09EC-415E-85B0-6FB874EEB8B4.png

按照我們前面的分析,load方法的調(diào)用順序應(yīng)該是:
Person -> Student -> Person + Test1 -> Student + Test2 -> Student + Test1 -> Person + Test2。
我們看一下打印結(jié)果:

2018-07-25 16:48:10.271679+0800 interview - Category[15094:408222] Person + load
2018-07-25 16:48:10.272357+0800 interview - Category[15094:408222] Student + load
2018-07-25 16:48:10.272661+0800 interview - Category[15094:408222] Person (Test1) + load
2018-07-25 16:48:10.272872+0800 interview - Category[15094:408222] Student (Test2) + load
2018-07-25 16:48:10.273103+0800 interview - Category[15094:408222] Student (Test1) + load
2018-07-25 16:48:10.273434+0800 interview - Category[15094:408222] Person (Test2) + load
2018-07-25 16:48:10.441457+0800 interview - Category[15094:408222] Person (Test2) + test

打印結(jié)果完美的驗(yàn)證了我們的結(jié)論。

總結(jié) load方法調(diào)用順序

1.先調(diào)用類的load方法
  • 按照編譯先后順序調(diào)用(先編譯,先調(diào)用)
  • 調(diào)用子類的load方法之前會(huì)先調(diào)用父類的load方法
2.再調(diào)用分類的load方法
  • 按照編譯先后順序,先編譯,先調(diào)用

initialize方法

initialize方法的調(diào)用時(shí)機(jī)
  • initialize在類第一次接收到消息時(shí)調(diào)用,也就是objc_msgSend()。
  • 先調(diào)用父類的+initialize,再調(diào)用子類的initialize。
    我們首先給Student類和Person類覆寫(xiě)+initialize方法:
//Person
+ (void)initialize{
    
    NSLog(@"Person + initialize");
}

//Person+Test1
+ (void)initialize{
    
    NSLog(@"Person (Test1) + initialize");
}

//Person+Test2
+ (void)initialize{
    
    NSLog(@"Person (Test2) + initialize");
}

//Student
+ (void)initialize{
    
    NSLog(@"Student + initialize");
}

//Student (Test1)
+ (void)initialize{
    
    NSLog(@"Student (Test1) + initialize");
}

//Student (Test2)
+ (void)initialize{
    
    NSLog(@"Student (Test2) + initialize");
}

我們運(yùn)行程序,發(fā)現(xiàn)什么也沒(méi)有打印,說(shuō)明在運(yùn)行期沒(méi)有調(diào)用+initialize方法。
然后我們給Person類發(fā)送消息,也就是調(diào)用函數(shù):

[Person alloc];

打印結(jié)果:

2018-07-25 17:26:22.462601+0800 interview - Category[15889:437305] Person (Test2) + initialize

可以看到調(diào)用了Person類的分類的initialize方法。通過(guò)這個(gè)打印結(jié)果我們能看出initialize方法和load方法的不同,load方法由于是直接獲取方法的地址,然后調(diào)用方法,所以Person及其分類的load方法都會(huì)調(diào)用。而initialize方法則更像是通過(guò)消息機(jī)制,也即是objc_msgend(Person, @selector(initialize))這種來(lái)調(diào)用的。
然后我多次調(diào)用alloc方法:

[Person alloc];
[Person alloc];
[Person alloc];

打印結(jié)果:

018-07-25 17:26:22.462601+0800 interview - Category[15889:437305] Person (Test2) + initialize

可見(jiàn)initialize方法只在類第一次收到消息時(shí)調(diào)用。然后我們?cè)俳oStudent類發(fā)送消息:

[Student alloc];

打印結(jié)果:

2018-07-25 18:34:14.648279+0800 interview - Category[17187:473502] Person (Test2) + initialize
2018-07-25 18:34:14.648394+0800 interview - Category[17187:473502] Student (Test1) + initialize

我們看到不僅調(diào)用了Student類的initialize方法,而且還調(diào)用了Student類的父類,Person類的方法,因此我們猜測(cè)在調(diào)用類的initialize方法之前會(huì)先調(diào)用父類的initialize方法。
以上僅僅是我們根據(jù)打印結(jié)果的猜測(cè),還需要通過(guò)源碼來(lái)驗(yàn)證。
[Person alloc]就相當(dāng)于objc_msgSend([Person class], @selector(alloc)),說(shuō)明objc_msgSend()內(nèi)部會(huì)去調(diào)用initialize方法,判斷是第幾次接收到消息。

  • 1.我們?nèi)untime源碼中搜索class_getClassmethod方法,會(huì)在objc-class.mm這個(gè)文件中找到這個(gè)方法的實(shí)現(xiàn):
    2B3F2677-4F69-40A7-8E12-983A3ECCC2FE.png
  • 2.我們點(diǎn)進(jìn)class_getInstanceMethod(cls->getMeta(), sel);這個(gè)方法:
    C04ACE66-648D-48A7-B6AB-8F687260D4E7.png
  • 3.點(diǎn)進(jìn)這個(gè)方法:
    6D91C93E-4782-4044-8CDE-662AC2D599DE.png
  • 4.繼續(xù)尋找lookUpImpOrForward這個(gè)方法的實(shí)現(xiàn),我截取其中有價(jià)值的代碼塊:
    E221D122-E2BE-42BC-9C71-767B80358116.png
    這個(gè)代碼也說(shuō)明了每個(gè)類的+initialize方法只會(huì)被調(diào)用一次。
  • 5.我們點(diǎn)進(jìn)_class_initialize (_class_getNonMetaClass(cls, inst));尋找真正的實(shí)現(xiàn):
    FDE84505-3F22-4458-971D-64C0F0F20096.png
  • 6.然后我們通過(guò)callInitialize(cls);查看具體的調(diào)
    77228E87-0910-452F-B04D-6FA295164C07.png

    這樣一來(lái)+initialize方法的調(diào)用過(guò)程就很清楚了。
+initialize的調(diào)用過(guò)程:
  • 1查看本類的initialize方法有沒(méi)有實(shí)現(xiàn)過(guò),如果已經(jīng)實(shí)現(xiàn)過(guò)就返回,不再實(shí)現(xiàn)。
  • 2.如果本類沒(méi)有實(shí)現(xiàn)過(guò)initialize方法,那么就去遞歸查看該類的父類有沒(méi)有實(shí)現(xiàn)過(guò)initialize方法,如果沒(méi)有實(shí)現(xiàn)就去實(shí)現(xiàn),最后實(shí)現(xiàn)本類的initialize方法。并且initialize方法是通過(guò)objc_msgSend()實(shí)現(xiàn)的。
+initialize和+load的一個(gè)很大區(qū)別是,+initialize是通過(guò)objc_msgSend進(jìn)行調(diào)用的,所以有以下特點(diǎn):
  • 如果子類沒(méi)有實(shí)現(xiàn)+initialize方法,會(huì)調(diào)用父類的+initialize(所以父類的+initialize方法可能會(huì)被調(diào)用多次)
  • 如果分類實(shí)現(xiàn)了+initialize,會(huì)覆蓋類本身的+initialize調(diào)用。

下面我們把Student類及其分類中的+initialize這個(gè)方法的實(shí)現(xiàn)去掉,然后增加一個(gè)Teacher類繼承自Person類。然后我們給Student類和Teacher類都發(fā)送alloc消息:

    [Student alloc];
    [Teacher alloc];

這個(gè)時(shí)候也就是只有Person類及其分類實(shí)現(xiàn)了+initialize方法。那么打印結(jié)果會(huì)是怎樣呢?

2018-07-25 21:47:59.899995+0800 interview - Category[20981:582224] Person (Test2) + initialize
2018-07-25 21:47:59.900112+0800 interview - Category[20981:582224] Person (Test2) + initialize
2018-07-25 21:47:59.900240+0800 interview - Category[20981:582224] Person (Test2) + initialize

這里Person類的+initialize方法竟然被調(diào)用了三次,這多少有些出乎意外吧。下面我們來(lái)分析一下。

    BOOL studentInitialized = NO;
    BOOL personinitialized = NO;
    BOOL teacherInitialized = NO;
    
    [Student alloc];
    //判斷Student類是否初始化了,這里Student類還沒(méi)有被初始化,所以進(jìn)入條件語(yǔ)句。
    if(!studentInitialized){
        //判斷Student類的父類Person類是否初始化了
        if(!personinitialized){
            //這里Person類還沒(méi)有初始化,就利用objc_msgSend調(diào)用initialize方法
            objc_msgSend([Person class], @selector(initialize));
            //變更Person類是否初始化的狀態(tài)
            personinitialized = YES;
        }
        //利用objc_msgSend調(diào)用Student的initialize方法
        objc_msgSend([Student class], @selector(initialize));
        //變更Student是否初始化的狀態(tài)
        studentInitialized = YES
    }
    
    [Teacher alloc];
    
    //判斷Teacher類是否已經(jīng)初始化了,這里Teacher類還沒(méi)有初始化,進(jìn)入條件語(yǔ)句
    if(!teacherInitialized){
        //判斷其父類Person類是否初始化了,這里父類已經(jīng)初始化了,所以不會(huì)進(jìn)入這個(gè)條件語(yǔ)句
        if(!personinitialized){
            
            objc_msgSend([Person class], @selector(initialize));
            personinitialized = YES;
        }
        //利用objc_msgSend調(diào)用Teacher類的initialize方法
        objc_msgSend([Teacher class], @selector(initialize));
        //變更狀態(tài)
        teacherInitialized = YES;
    }

上面列出來(lái)的是調(diào)用initialize的偽代碼,下面再詳細(xì)說(shuō)明這個(gè)過(guò)程:

  • 1.Student類收到alloc消息,開(kāi)始著手準(zhǔn)備調(diào)用initialize方法。首先判斷自己有沒(méi)有初始化過(guò)。
  • 2.判斷自己沒(méi)有初始化過(guò),所以就去找自己的父類Person類,看Person類有沒(méi)有初始化過(guò),發(fā)現(xiàn)Person類也沒(méi)有初始化過(guò),且Person類也沒(méi)有父類,多以對(duì)Person類使用objc_msgSend([Person class], @selector(initialize))調(diào)用Person類的initialize方法。這是第一次調(diào)用Person類的initialize方法。
  • 3.父類處理完后,再通過(guò)objc_msgSend([Student class], @selector(initialize));調(diào)用Student類的initialize方法,但是由于Student類沒(méi)有實(shí)現(xiàn)initialize方法,所以通過(guò)其superclass指針找到父類Person類,然后調(diào)用了Person類的initialize實(shí)現(xiàn)。這是第二次調(diào)用Person類的initialize方法。
  • 4.Teacher類收到alloc方法,開(kāi)始準(zhǔn)備調(diào)用initialize放啊發(fā)。首先判斷自己有沒(méi)有被初始化過(guò)。
  • 5.判斷自己沒(méi)有被初始化過(guò)后,又開(kāi)始判斷其父類Person類有沒(méi)有被初始化過(guò),剛剛父類Person類已經(jīng)被初始化過(guò)。
  • 6.于是通過(guò)objc_msgSend([Teacher class], @selector(initialize))調(diào)用Teacher類的initialize方法。但是由于Teacher類沒(méi)有實(shí)現(xiàn)initialize方法,所以只能通過(guò)superclass指針去查找父類有沒(méi)有實(shí)現(xiàn)initialize方法,發(fā)現(xiàn)父類Person類實(shí)現(xiàn)了initialize方法,于是調(diào)用父類的initialize方法。這是第三次調(diào)用Person類的initialize方法。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,185評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,656評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,647評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,446評(píng)論 6 405
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,951評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,189評(píng)論 0 287
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,718評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,602評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,800評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,045評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,419評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,671評(píng)論 1 281
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,420評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,755評(píng)論 2 371

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

  • load 和 initialize 兩個(gè)方法算是兩個(gè)特殊的類方法了,今天偶然從草稿箱中看到還有本篇未完成的博文,如...
    RITL閱讀 1,505評(píng)論 8 13
  • iOS開(kāi)發(fā)中總能看到+load和+initialize的身影,網(wǎng)上對(duì)于這兩個(gè)方法有很多解釋,官方也有說(shuō)明,但有些細(xì)...
    朱曉輝閱讀 27,490評(píng)論 19 139
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,745評(píng)論 0 9
  • 本節(jié)將解釋下一下問(wèn)題: 1.Category的實(shí)現(xiàn)原理? 2.Category跟Extension的區(qū)別? 3.C...
    咖啡豆8888閱讀 1,045評(píng)論 0 5
  • 情商和智商其實(shí)并不沖突,我們通常把把聰明的人說(shuō)是智商高的人,把處事圓滑的人稱之情商高的人,其實(shí)不然。情商是智商...
    Miya婷閱讀 202評(píng)論 0 0