Runtime

為什么說Objective-C是動態(tài)語言

  • 概念
    動態(tài)語言 是指程序在運行時可以改變其結(jié)構(gòu):新的函數(shù)可以被引進,已有的函數(shù)可以被刪除等在結(jié)構(gòu)上的變化棒仍,類型的檢查是在運行時做的,優(yōu)點為方便閱讀川蒙,清晰明了庙睡,缺點為不方便調(diào)試。所謂的動態(tài)類型語言揪漩,意思就是類型的檢查是在運行時做的

    靜態(tài)類型語言 的類型判斷是在運行前判斷(如編譯階段)旋恼,比如C#、java就是靜態(tài)類型語言,主要優(yōu)點在于其結(jié)構(gòu)非常規(guī)范奄容,便于調(diào)試冰更,方便類型安全

  • Objective-C是動態(tài)語言

    • Objective-C 可以通過Runtime 這個運行時機制,在運行時動態(tài)的添加變量昂勒、方法蜀细、類等

    • Objective-C具有相當(dāng)多的動態(tài)特性,基本的戈盈,也是經(jīng)常被提到和用到的有動態(tài)類型(Dynamic typing)审葬,動態(tài)綁定(Dynamic binding)和動態(tài)加載(Dynamic loading)

    • object-c類的類型和數(shù)據(jù)變量的類型都是在運行是確定的,而不是在編譯時- 確定奕谭。例如:多態(tài)特性涣觉,我們可以使用父類對象來指向子類對象,并且可以用來調(diào)用子類的方法

所以說 Objective-C是動態(tài)語言

Runtime 數(shù)據(jù)結(jié)構(gòu)

Runtime 又叫運行時血柳,是一套底層的 C 語言 API官册,其為 iOS 內(nèi)部的核心之一,我們平時編寫的 OC 代碼难捌,底層都是基于它來實現(xiàn)的

    1. 在Runtime 之前先來看下 結(jié)構(gòu)體

結(jié)構(gòu)體簡單用法

struct Books
{
    char  title[50];
    char  author[50];
    char  subject[100];
    int   book_id;
} book = {"C 語言", "RUNOOB", "編程語言", 123456};


struct Student {
    char *name;
    int num;
    int age;
    char group;
    float score;
    
} stu1 = {"Tom",12,16,'A',1233.9};


int main(int argc, const char * argv[]) {
    
  
    //*********結(jié)構(gòu)體*********
    printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);

    
    //*********結(jié)構(gòu)體指針*********
    //pstu指向結(jié)構(gòu)體變量stu1的地址
    struct Student *pstu = &stu1;
    
    //指針引用結(jié)構(gòu)體變量成員的方式是: (*指針變量名).成員名
    //注意膝宁,*p 兩邊的括號不可省略鸦难,因為成員運算符“.”的優(yōu)先級高于指針運算符“*”,所以如果 *p 兩邊的括號省略的話员淫,那么 *p.num 就等價于 *(p.num) 了合蔽。
    printf("%s的學(xué)號是%d,年齡是%d介返,在%c組拴事,今年的成績是%.1f!\n", (*pstu).name, (*pstu).num, (*pstu).age, (*pstu).group, (*pstu).score);
    
    //->”是“指向結(jié)構(gòu)體成員運算符
    printf("%s的學(xué)號是%d圣蝎,年齡是%d刃宵,在%c組,今年的成績是%.1f徘公!\n", pstu->name, pstu->num, pstu->age, pstu->group, pstu->score);


    return 0;
}

    1. Class 的在OC底層中的定義

typedef struct objc_class *Class; 很明顯是一個 結(jié)構(gòu)體類型的指針

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

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


/// An opaque type that represents an Objective-C class. 不透明的類型牲证,表示一個Objective-C類
typedef struct objc_class *Class;
    1. id 的在OC底層中的定義

typedef struct objc_object *id; 很明顯也是一個 結(jié)構(gòu)體類型的指針

/// Represents an instance of a class.表示類的實例 
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class. 指向類實例的指針
typedef struct objc_object *id;

可以發(fā)現(xiàn):
每個objective-c對象都有一個隱藏的 數(shù)據(jù)結(jié)構(gòu),這個數(shù)據(jù)結(jié)構(gòu)是Objective-C對象的第一個成員變量关面,它就是isa指針坦袍。

  • 這個指針指向哪呢?
    指向Class isa等太,指向的是這個 對象所對應(yīng)的類(Class,其實類也是一個對象)

    1. NSObject 的在OC底層中的定義
@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}

不考慮@interface關(guān)鍵字在編譯時的作用捂齐,可以把NSObject更接近C語言結(jié)構(gòu)表示為:


//第一步變形
struct NSObject {
    Class isa ;
}

//第二步變形:根據(jù)第1點,Class 在底層的定義澈驼,變形
struct NSObject{
     struct objc_class *isa
}

到這一步為止辛燥,所以還得研究 結(jié)構(gòu)體 objc_class

    1. 分析結(jié)構(gòu)體 objc_class
struct objc_class {
    
    //isa 指針
    //對象也有一個isa指針,指向Class isa缝其,指向的是這個 對象所對應(yīng)的類(Class,其實類也是一個對象)
    //指向metaclass挎塌,
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    //指向其父類,如果這個類是根類内边,則為NULL榴都。
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    
    //類名
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;

    //類的版本信息,初始化默認為0漠其,可以通過runtime函數(shù)class_setVersion和class_getVersion進行修改嘴高、讀取
    long version                                             OBJC2_UNAVAILABLE;

    //一些標(biāo)識信息,如CLS_CLASS (0x1L) 表示該類為普通 class ,其中包含對象方法和成員變量;CLS_META (0x2L) 表示該類為 metaclass和屎,其中包含類方法;
    long info                                                OBJC2_UNAVAILABLE;

    //該類的實例變量大小(包括從父類繼承下來的實例變量);
    long instance_size                                       OBJC2_UNAVAILABLE;

    //用于存儲每個成員變量的地址
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;

    // 與 info 的一些標(biāo)志位有關(guān),如CLS_CLASS (0x1L),則存儲對象方法拴驮,如CLS_META (0x2L),則存儲類方法;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;

    //指向最近使用的方法的指針柴信,用于提升效率套啤;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;

    //存儲該類遵守的協(xié)議
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

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


/// An opaque type that represents an Objective-C class. 不透明的類型,表示一個Objective-C類
typedef struct objc_class *Class;

附加解釋:

Class isa:指向metaclass随常,也就是靜態(tài)的Class潜沦。對象obj也有一個isa指針萄涯,指向Class isa,指向的是這個 對象所對應(yīng)的類(Class,其實類也是一個對象)
這是解釋類方法也就是實例方法唆鸡,因為類也是對象

metaclass的isa指向根metaclass涝影,如果該metaclass是根metaclass則指向自身;
metaclass 的super_class指向父metaclass
如果該metaclass是根metaclass則指向該metaclass對應(yīng)的類争占;

重點就是isa指針燃逻,Objective-C對類對象和實例對象中的isa所指向的類結(jié)構(gòu)做了不同的命名,類對象中isa指向類結(jié)構(gòu)稱為metaclass燃乍,它存儲類的static類成員變量與static類成員方法(+開頭的方法)唆樊;實例對象中的isa指向類結(jié)構(gòu)稱作class宛琅,它存儲類的普通成員變量與普通成員方法(-開頭的方法)刻蟹。

    1. SEL 的在OC底層中的定義

也是一個結(jié)構(gòu)體指針

/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;

Rumtime 消息機制

提到消息機制,不能不提到 SEL嘿辟,SEL又叫選擇器舆瘪,是表示一個方法的selector的指針,他的定義typedef struct objc_selector *SEL; 正因為是結(jié)構(gòu)體指針红伦,所以也有人說SEL是一個對象

比如
創(chuàng)建一Person個類 繼承NSObject英古、
創(chuàng)建一Son個類 繼承Person、
創(chuàng)建一個Student 繼承NSObject昙读,分別實現(xiàn)如下代碼

- (instancetype)init
{
    self = [super init];
    if (self) {
        SEL sel1 = @selector(eat);
        NSLog(@"sel : %p", sel1);
    }
    return self;
}
- (void)eat {
    NSLog(@"吃了");
    
}

輸出如下:

2019-01-19 17:55:05.029309+0800 RuntimeDemo[3106:91635] sel : 0x10552fa4f

2019-01-19 17:55:05.029515+0800 RuntimeDemo[3106:91635] sel : 0x10552fa4f
2019-01-19 17:55:05.029619+0800 RuntimeDemo[3106:91635] sel : 0x10552fa4f

2019-01-19 17:55:05.029757+0800 RuntimeDemo[3106:91635] sel : 0x10552fa4f

Son 繼承Person召调,所以會調(diào)用父類的init 方法,會打印兩次
會發(fā)現(xiàn)有的SEL蛮浑,打印的地址相同

由此證明唠叛,不同類的相同SEL是同一個對象。

所以在 Objective-C同一個類(及類的繼承體系)中沮稚,不能存在2個同名的方法艺沼,即使參數(shù)類型不同也不行。相同的方法只能對應(yīng)一個SEL蕴掏。這也就導(dǎo)致 Objective-C在處理相同方法名且參數(shù)個數(shù)相同但類型不同的方法方面的能力很差

不同類的實例對象執(zhí)行相同的selector時障般,會在各自的方法列表中去根據(jù)selector去尋找自己對應(yīng)的IMP

  • 問題一: 不同對象調(diào)用相同的方法怎么找到SEL?

一般的調(diào)用方法如下

- (void) setUpTest01 {
    Person *p = [[Person alloc] init];
    [p eat];
}

也可以這么些

- (void)setUpTest02 {
    
    Person *p = [Person alloc];
    
    p = [p init];
    
    //[p eat];
    [p performSelector:@selector(eat)];
}

Runtime進行方法調(diào)用本質(zhì)上是發(fā)送消息盛杰,發(fā)送消息是怎么發(fā)送的呢挽荡?

通過Runtime 就會發(fā)現(xiàn),在底層使用了 objc_msgSend 函數(shù)

//消息機制
- (void)setUpTest03 {
    //Person *p = [Person alloc];
    //類方法 其實類也是一個對象即供,oc 表示類 類型定拟,swift 表示元 類型
    Person *p = objc_msgSend([Person class], @selector(alloc));
    
    //p = [p init];
    p = objc_msgSend(p, @selector(init));

    //objc_msgSend(p,@selector(eat))    //編譯報錯
    /**
         1. target -> build setting ,搜索 msg
         2. Enable Strict Cheaking of objc_msgSend Calls 設(shè)置為No
         、設(shè)置以后就不會編譯錯誤了募狂,因為oc 不推薦使用底層去實現(xiàn)
     */
 
    objc_msgSend(p,@selector(eat));
    //objc_msgSend(<#id  _Nullable self#>, <#SEL  _Nonnull op, ...#>) //... 可擴展參數(shù)
}
  • 解讀 objc_msgSend
- self办素,調(diào)用當(dāng)前方法的對象角雷。
- _cmd,當(dāng)前被調(diào)用方法的SEL
objc_msgSend(<#id  _Nullable self#>, <#SEL  _Nonnull op, ...#>) //... 可擴展參數(shù)

繼續(xù)分解性穿,有些函數(shù)不懂沒關(guān)系

- (void)setUpTest04 {
    //Person *p = [Person alloc];
    //類方法 其實類也是一個對象勺三,oc 表示類 類型,swift 表示元 類型
    //Person *p = objc_msgSend(objc_getClass("Person"), @selector(alloc));
    Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"));

    //p = objc_msgSend(p, @selector(init));
    p = objc_msgSend(p, sel_registerName("init"));
    
   // objc_msgSend(p,@selector(eat));
    objc_msgSend(p,sel_registerName("eat"));
}

工程中的所有的SEL組成一個Set集合需曾,Set的特點就是唯一吗坚,因此SEL是唯一的。
因此呆万,如果我們想到這個方法集合中查找某個方法時商源,只需要去 找到這個方法對應(yīng)的SEL就行了,SEL實際上就是根據(jù)方法名hash化了的一個字符串谋减,而對于字符串的比較僅僅需要比較他們的地址就可以了牡彻,可以說速度 上無語倫比!出爹!但是庄吼,有一個問題,就是數(shù)量增多會增大hash沖突而導(dǎo)致的性能下降(或是沒有沖突严就,因為也可能用的是perfect hash)总寻。但是不管使用什么樣的方法加速,如果能夠?qū)⒖偭繙p少(多個方法可能對應(yīng)同一個SEL)梢为,那將是最犀利的方法渐行。那么,我們就不難理解铸董,為什么 SEL僅僅是函數(shù)名了祟印。

  • IMP

    IMP實際上是一個函數(shù)指針,指向方法實現(xiàn)的首地址

    /// A pointer to the function of a method implementation. 
    #if !OBJC_OLD_DISPATCH_PROTOTYPES
    typedef void (*IMP)(void /* id, SEL, ... */ ); 
    #else
    typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
    #endif
    
    • 解讀參數(shù)
      第一個參數(shù)是指向self的指針(如果是實例方法袒炉,則是類實例的內(nèi)存地址旁理;如果是類方法,則是指向元類的指針)我磁,
      第二個參數(shù)是方法選擇器(selector)孽文,接下來是方法的實際參數(shù)列表。

    • SEL就是為了查找方法的最終實現(xiàn)IMP的夺艰。由于每個方法對應(yīng)唯一的SEL芋哭,因此我們可以通過SEL方便快速準(zhǔn)確地獲得它所對應(yīng)的 IMP,查找過程將在下面討論郁副。取得IMP后减牺,我們就獲得了執(zhí)行這個方法代碼的入口點,此時,我們就可以像調(diào)用普通的C語言函數(shù)一樣來使用這個函數(shù)指針 了

  • Method

struct objc_method {

     //方法名
    SEL _Nonnull method_name                                 OBJC2_UNAVAILABLE;
    char * _Nullable method_types                            OBJC2_UNAVAILABLE;
    
    //方法的實現(xiàn)
    IMP _Nonnull method_imp                                  OBJC2_UNAVAILABLE;
}   

/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;

/// An opaque type that represents an instance variable.
typedef struct objc_ivar *Ivar;

/// An opaque type that represents a category.
typedef struct objc_category *Category;

/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;

我們可以看到該結(jié)構(gòu)體中包含一個SEL和IMP拔疚,實際上相當(dāng)于在SEL和IMP之間作了一個映射肥隆。有了SEL

當(dāng)消息發(fā)送給一個對象時,objc_msgSend通過對象的isa指針獲取到類的結(jié)構(gòu)體稚失,然后在方法分發(fā)表里面查找方法的selector栋艳。如果 沒有找到selector,則通過objc_msgSend結(jié)構(gòu)體中的指向父類的指針找到其父類句各,并在父類的分發(fā)表里面查找方法的selector吸占。依 此,會一直沿著類的繼承體系到達NSObject類凿宾。一旦定位到selector矾屯,函數(shù)會就獲取到了實現(xiàn)的入口點,并傳入相應(yīng)的參數(shù)來執(zhí)行方法的具體實 現(xiàn)初厚。如果最后沒有定位到selector件蚕,則會走消息轉(zhuǎn)發(fā)流程,這個我們在后面討論惧所。
為了加速消息的處理骤坐,運行時系統(tǒng)緩存使用過的selector及對應(yīng)的方法的地址绪杏。這點我們在前面討論過下愈,不再重復(fù)。

KVO的底層實現(xiàn)

  • 1.KVO是基于runtime機制實現(xiàn)的
  • 2.當(dāng)某個類的屬性對象第一次被觀察時蕾久,系統(tǒng)就會在運行期動態(tài)地創(chuàng)建該類的一個派生類势似,在這個派生類中重寫基類中任何被觀察屬性的setter 方法。派生類在被重寫的setter方法內(nèi)實現(xiàn)真正的通知機制
  • 3.如果原類為Person僧著,那么生成的派生類名為NSKVONotifying_Person
  • 4.每個類對象中都有一個isa指針指向當(dāng)前類履因,當(dāng)一個類對象的第一次被觀察,那么系統(tǒng)會偷偷將isa指針指向動態(tài)生成的派生類盹愚,從而在給被監(jiān)控屬性賦值時執(zhí)行的是派生類的setter方法
  • 5.鍵值觀察通知依賴于NSObject 的兩個方法: willChangeValueForKey: 和 didChangevlueForKey:栅迄;在一個被觀察屬性發(fā)生改變之前, willChangeValueForKey:一定會被調(diào)用皆怕,這就 會記錄舊的值毅舆。而當(dāng)改變發(fā)生后,didChangeValueForKey:會被調(diào)用愈腾,繼而 observeValueForKey:ofObject:change:context: 也會被調(diào)用憋活。
 
     1. command + Q 關(guān)閉工程項目
     2. 在次運行項目,在Person 創(chuàng)建對象之后加上一個斷點
     3. 在 控制臺可以看出 _p 目錄下 NSObject ——> isa 又個isa 指針虱黄,指向(class)Person
     4. 調(diào)用 [_p FF_addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil] 之后
     5. 在 控制臺可以看出 _p 目錄下 NSObject ——> isa 又個isa 指針悦即,指向(class)FFKVOPerson
     (self->_p->isa:NSKVONotifying_Person),
     6. 監(jiān)聽屬性的值age 是否被修改,其實是在NSKVONotifying_Person類里面重寫了set 方法辜梳,一旦改變粱甫,就是通知父類做一系列操作
     7. 當(dāng)我 用成員變量_name 時候,無法監(jiān)聽作瞄,所以證明只能觀察重寫的 set 方法
     

代碼如下:


#import "NSObject+KVO.h"
#import <objc/message.h>


@implementation NSObject (KVO)

- (void)FF_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context
{
    NSLog(@"%@",self);
    
    //1. 動態(tài)添加一個類
    NSString *oldClassName = NSStringFromClass([self class]);
    NSString *newClassName = [@"FFKVO" stringByAppendingString:oldClassName];
    const char *newClass = [newClassName UTF8String];
    
    //定義一個類
    Class myClass = objc_allocateClassPair([self class], newClass, 0);
    
    //重寫setAge(添加一個set方法)
    class_addMethod(myClass, @selector(setAge:), (IMP)setAge, "v@:");
    
    //注冊這個類
    objc_registerClassPair(myClass);
    
    //改變isa 指針的指向
    //NSKVONotifying_A類剖析:在這個過程魔种,被觀察對象的 isa 指針從指向原來的A類,被KVO機制修改為指向系統(tǒng)新創(chuàng)建的子類 NSKVONotifying_A類粉洼,來實現(xiàn)當(dāng)前類屬性值改變的監(jiān)聽节预;
    object_setClass(self, myClass);

    //關(guān)聯(lián)對象
    objc_setAssociatedObject(self, (__bridge const void *)@"objc", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    
}

//默認參數(shù)!属韧!
void setAge(id self,SEL _cmd ,int age) {
    
    //1保存一下自己
    id class = [self class];
    
    //2.讓自己指向父類
    object_setClass(self, class_getSuperclass([self class]));
   
    NSLog(@"修改完畢 %d",age);
    
   
    //3.
    objc_msgSend(self,@selector(setAge:),age);
   
    //取出觀察者
    id observer = objc_getAssociatedObject(self, (__bridge const void *)@"objc");
    
    NSDictionary *dic = @{@"new": [NSNumber numberWithInt:age]};
    
    objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),@"age",self,dic,nil);
    
    //4.改回類型 針對 3
    object_setClass(self, class);
    
    
}


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末安拟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子宵喂,更是在濱河造成了極大的恐慌糠赦,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锅棕,死亡現(xiàn)場離奇詭異拙泽,居然都是意外死亡,警方通過查閱死者的電腦和手機裸燎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門顾瞻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人德绿,你說我怎么就攤上這事荷荤。” “怎么了移稳?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵蕴纳,是天一觀的道長。 經(jīng)常有香客問我个粱,道長古毛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任都许,我火速辦了婚禮稻薇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘梭稚。我一直安慰自己颖低,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布弧烤。 她就那樣靜靜地躺著忱屑,像睡著了一般蹬敲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上莺戒,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天伴嗡,我揣著相機與錄音,去河邊找鬼从铲。 笑死瘪校,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的名段。 我是一名探鬼主播阱扬,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼伸辟!你這毒婦竟也來了麻惶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤信夫,失蹤者是張志新(化名)和其女友劉穎窃蹋,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體静稻,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡警没,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了振湾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杀迹。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖恰梢,靈堂內(nèi)的尸體忽然破棺而出佛南,到底是詐尸還是另有隱情,我是刑警寧澤嵌言,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站及穗,受9級特大地震影響摧茴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜埂陆,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一苛白、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧焚虱,春花似錦购裙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春薇芝,著一層夾襖步出監(jiān)牢的瞬間蓬抄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工夯到, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嚷缭,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓耍贾,卻偏偏與公主長得像阅爽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子荐开,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

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

  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 729評論 0 2
  • 我們常常會聽說 Objective-C 是一門動態(tài)語言优床,那么這個「動態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,182評論 0 7
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉誓焦,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,686評論 0 9
  • 一胆敞、Runtime簡介 Runtime簡稱運行時。OC就是運行時機制杂伟,也就是在運行時候的一些機制移层,其中最主要的是消...
    林安530閱讀 1,060評論 0 2
  • 文中的實驗代碼我放在了這個項目中。 以下內(nèi)容是我通過整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 913評論 0 6