Runtime

啟動流程

objc-os.mm的init方法是初始化的入口禽翼。

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    environ_init();
    tls_init();
    static_init();
    lock_init();
    exception_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}

幾個初始化

  • environ_init族跛,讀取環(huán)境配置方法礁哄,在這個方法里會讀取在Xcode 中配置的環(huán)境變量參數(shù)
  • tls_init桐绒,用于初始化不是使用pthread_key_create()創(chuàng)建的線程的析構(gòu)函數(shù)
  • static_init之拨,執(zhí)行 C++ 靜態(tài)構(gòu)造函數(shù)功能
  • lock_init蚀乔,初始化后臺線程和主線程優(yōu)先級
  • exception_init菲茬,異常初始化

map_images

在map_images函數(shù)中,內(nèi)部也是做了一個調(diào)用中轉(zhuǎn)。然后調(diào)用到map_images_nolock函數(shù)爵赵,內(nèi)部核心就是_read_images函數(shù)。先整體梳理一遍_read_images函數(shù)內(nèi)部的邏輯:

  1. 加載所有類到類的gdb_objc_realized_classes表中。
  2. 對所有類做重映射威彰。
  3. 將所有SEL都注冊到namedSelectors表中穴肘。
  4. 修復(fù)函數(shù)指針遺留评抚。
  5. 將所有Protocol都添加到protocol_map表中。
  6. 對所有Protocol做重映射邢笙。
  7. 初始化所有非懶加載的類氮惯,進(jìn)行rw想暗、ro等操作说莫。
  8. 遍歷已標(biāo)記的懶加載的類,并做初始化操作互婿。
  9. 處理所有Category擒悬,包括Class和Meta Class。
  10. 初始化所有未初始化的類侈净。

load_images

在load_images函數(shù)中主要做了兩件事畜侦,

  1. 首先通過prepare_load_methods函數(shù)準(zhǔn)備Class load list和Category load list躯保,
  2. 然后通過call_load_methods函數(shù)調(diào)用已經(jīng)準(zhǔn)備好的兩個方法列表

isa

在ARM 64之前途事,isa是一個指針尸变,存儲著ClassMeta-Class對象的內(nèi)存地址碱工。
ARM 64之后怕篷,isa是一個共用體酗昼,除了存儲Class麻削、Meta-Class對象的內(nèi)存地址,還保存了更多的信息电抚。ARM 64情況下蝙叛,isa共用體的定義公给。

union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    Class cls;
    uintptr_t bits;
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    struct {
        uintptr_t nonpointer        : 1;
        uintptr_t has_assoc         : 1;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
        uintptr_t magic             : 6;
        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 19;
#       define RC_ONE   (1ULL<<45)
#       define RC_HALF  (1ULL<<18)
    };
}

isa的位域的意義

  • nonpointer
    - 0 ,表示指針蔫缸,存儲Class對象地址
    - 1拾碌,使用共用體街望,存儲更多信息
  • has_assoc
    - 是否設(shè)置過關(guān)對象灾前。如果沒有,該對象會釋放地更快
  • has_cxx_dtor
    - 是否有C++析構(gòu)函數(shù)蔫敲。如果沒有奈嘿,該對象會釋放地更快
  • shiftcls
    - 類對象指么,元類對象的內(nèi)存地址
  • magic
    - 對象是否完成初始化
  • weakly_referenced
    - 是否被若引用指向過榴鼎。如果沒有巫财,該對象會釋放地更快
  • deallocating
    - 是否正在被釋放
  • has_sidetable_rc
    - 是否引用計數(shù)器過大哩陕,不能保存在isa中悍及。如果過大心赶,會保存在SideTable
  • extra_rc
    - 保存該對象的引用計數(shù)減1

method_t

struct method_t {
    SEL name;       // 底層結(jié)構(gòu)類似于char *
    const char *types;
    IMP imp;     // 該表函數(shù)的具體實現(xiàn)
};

SEL代表方法\函數(shù)名缨叫,一般叫做選擇器荔燎,底層結(jié)構(gòu)跟char *類似

  • 可以通過@selector()sel_registerName()獲得
  • 可以通過sel_getName()NSStringFromSelector()
  • 不同類中相同名字的方法有咨,所對應(yīng)的方法選擇器是相同的

types包含了函數(shù)返回值座享,參數(shù)編碼的字符串

// i 24  @ 0  : 8   i 16  f 20
/*  i 24纵装,表示所有參數(shù)占據(jù)的字節(jié)數(shù)
    @ 0橡娄,表示第一個參數(shù)挽唉,self,從第0個參數(shù)開始
    : 8匠童,表示第二個參數(shù)汤求,SEL严拒,從第8個開始
    i 16裤唠,表示(int)age
    f 20种蘸,表示(float)height
 */
- (void)test:(int)age height:(float)height {
 
}

IMP表示函數(shù)的具體實現(xiàn)

typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 

cache_t

struct cache_t {
    struct bucket_t *_buckets;      // 數(shù)組
    mask_t _mask;          // 數(shù)組長度
    mask_t _occupied;    // 已經(jīng)使用的長度
}

struct bucket_t {
private:
    cache_key_t _key;      // SEL
    IMP _imp;
}

散列表使用的hash算法,就是&運算

static inline mask_t cache_hash(cache_key_t key, mask_t mask) 
{
    return (mask_t)(key & mask);
}

如果發(fā)生Hash碰撞诫硕,則從前一個開始找章办,直到索引為0纲菌。如果還找不到,就再從數(shù)組最后一個嚣潜,倒序開始找懂算。

bucket_t * cache_t::find(cache_key_t k, id receiver)
{
    assert(k != 0);

    bucket_t *b = buckets();
    mask_t m = mask();
    mask_t begin = cache_hash(k, m);
    mask_t i = begin;
    do {
        if (b[i].key() == 0  ||  b[i].key() == k) {
            return &b[i];
        }
    } while ((i = cache_next(i, m)) != begin);

    // hack
    Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
    cache_t::bad_cache(receiver, (SEL)k, cls);
}

子類調(diào)用父類方法之后计技,先在子類找不到方法山橄,然后去父類找航棱,

  • 也是先找父類的緩存饮醇,再找父類的方法列表
  • 找到之后朴艰,會把方法緩存到子類的cache中

緩存的時候,如果緩存滿了侮穿,則清除所有緩存撮珠,并且2倍擴(kuò)容

void cache_t::expand()
{
    cacheUpdateLock.assertLocked();
    
    uint32_t oldCapacity = capacity();
    uint32_t newCapacity = oldCapacity ? oldCapacity*2 : INIT_CACHE_SIZE;

    if ((uint32_t)(mask_t)newCapacity != newCapacity) {
        // mask overflow - can't grow further
        // fixme this wastes one bit of mask
        newCapacity = oldCapacity;
    }

    reallocate(oldCapacity, newCapacity);
}

obj_msgSend()

OC中的方法調(diào)用,都是轉(zhuǎn)換成調(diào)用obj_msgSend()函數(shù)驶俊。整個過程可以分成3個階段:

  • 消息發(fā)送:根據(jù)OC對象模型圖免姿,從子類到父類,去找方法
  • 動態(tài)方法解析:可能會向類對象添加方法
  • 消息轉(zhuǎn)發(fā):可能將該方法調(diào)用想鹰,轉(zhuǎn)到其他對象去調(diào)用辑舷。
_class_lookupMethodAndLoadCache3
lookUpImpOrForward
getMethodNoSuper_nolock槽片、search_method_list、log_and_fill_cache
cache_getImp碌廓、log_and_fill_cache谷婆、getMethodNoSuper_nolock辽聊、log_and_fill_cache
_class_resolveInstanceMethod
_objc_msgForward_impcache

匯編調(diào)用

由于obj_msgSend()調(diào)用十分頻繁身隐,所以obj_msgSend()有一部分是使用匯編實現(xiàn)的贾铝。
一般如果是C函數(shù)是objc_msgSend垢揩,則對應(yīng)的匯編函數(shù)就是加上下劃線_objc_msgSend
匯編做了如下工作:

  • 判斷receiver是否為nil叁巨,如果是nil就直接返回
  • 查找緩存,如果緩存沒有命中蚀瘸,就在方法列表查找方法
objc-msg-arm64.s
ENTRY _objc_msgSend
b.le    LNilOrTagged
CacheLookup NORMAL
.macro CacheLookup
.macro CheckMiss
STATIC_ENTRY __objc_msgSend_uncached
.macro MethodTableLookup
__class_lookupMethodAndLoadCache3

查找方法

匯編函數(shù)是__class_lookupMethodAndLoadCache3贮勃,則對應(yīng)的runtime方法是_class_lookupMethodAndLoadCache3
在C函數(shù)還是會查找一遍緩存寂嘉,原因是:再執(zhí)行到這次的查找緩存之前泉孩,可能動態(tài)添加一些方法,緩存方法變化显蝌。

  • 在本類進(jìn)行查找
    1. 再次查找緩存曼尊,
    2. 查找方法骆撇,在本類對象的方法列表父叙,還可以分成二分查找(如果已經(jīng)排好序)和線性查找趾唱。如果找到了甜癞,會進(jìn)行緩存
  • 不斷地向上悠咱,在父類進(jìn)行查找析既。不管是
    1. 先找父類的緩存。如果找到了拂玻,在自己的類緩存
    2. 在找父類的方法列表檐蚜。如果找到了熬甚,在自己的類緩存

動態(tài)方法解析

當(dāng)通過上面的方法查找,找不到方法智厌,就會進(jìn)入動態(tài)方法解析。

  • 通過triedResolver敷扫,只解析一次
  • goto retry會重新進(jìn)入方法查找:先查找本類緩存葵第,在找本類方法列表卒密;再父類
  if (resolver  &&  !triedResolver) {
        runtimeLock.unlockRead();
        _class_resolveMethod(cls, sel, inst);
        runtimeLock.read();
        // Don't cache the result; we don't hold the lock so it may have 
        // changed already. Re-do the search from scratch instead.
        triedResolver = YES;
        goto retry;
    }

使用如下示例代碼哮奇,進(jìn)行動態(tài)方法解析

void c_anotherMethod(id self, SEL _cmd)
{
    NSLog(@"c_another class method");
}

- (void)anotherTest {
    NSLog(@"instance %s",__func__);
}

+ (BOOL)resolveClassMethod:(SEL)sel
{
    if (sel == @selector(test)) {
        // 第一個參數(shù)是object_getClass(self)
        class_addMethod(object_getClass(self), sel, (IMP)c_anotherMethod, "v16@0:8");
        return YES;
    }
    return [super resolveClassMethod:sel];
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
        Method anotherMethod = class_getInstanceMethod(self, @selector(anotherTest));
        class_addMethod(self,
                        sel,
                        method_getImplementation(anotherMethod),
                        method_getTypeEncoding(anotherMethod));
        return YES;
    }
    
    return [super resolveInstanceMethod:sel];
}

消息轉(zhuǎn)發(fā)

消息轉(zhuǎn)發(fā).png

首先是調(diào)用forwardingTargetForSelector:,如果返回不為nil贸伐,就進(jìn)入target的方法調(diào)用流程棍丐。forwarding:沒有開源歌逢,可以通過逆向秘案,了解其實現(xiàn)的阱高。
如果最終能夠走到forwardInvocation:赤惊,即使forwardInvocation:是空實現(xiàn)凰锡,該方法調(diào)用也能成功完成。

  1. 調(diào)用forwardInvocation之前员串,系統(tǒng)會先調(diào)用resolveInstanceMethod:寸齐,傳入的selector_forwardStackInvocation:
  2. 如果實現(xiàn)methodSignatureForSelector :渺鹦,但是沒有實現(xiàn)forwardInvocation :海铆,也會unrecognized selector
- (id)forwardingTargetForSelector:(SEL)aSelector {
    return [[ForwardTarget alloc] init];
}
// 方法簽名:返回值類型卧斟、參數(shù)類型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    if (aSelector == @selector(test)) {
        return [NSMethodSignature signatureWithObjCTypes:"v24@0:8@16"];
    }
    return [super methodSignatureForSelector:aSelector];
}

// NSInvocation封裝了一個方法調(diào)用珍语,包括:方法調(diào)用者板乙、方法名拳氢、方法參數(shù)
//    anInvocation.target 方法調(diào)用者
//    anInvocation.selector 方法名
//    anInvocation.methodSignature methodSignatureForSelector:方法返回的
//    [anInvocation getArgument:NULL atIndex:0]
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    [anInvocation invokeWithTarget:[[ForwardTarget alloc] init]];
}

Super

[super message]的底層實現(xiàn),最初是objc_msgSendSuper(arg, sel)馋评,其中第一個參數(shù)是結(jié)構(gòu)體放接,該結(jié)構(gòu)體包含兩個成員變量:

  1. 消息接收者,就是子類實例對象
  2. 父類類對象留特,找方法會從父類類對象開始找
struct objc_super {
    __unsafe_unretained _Nonnull id receiver; // 消息接收者
    __unsafe_unretained _Nonnull Class super_class; // 消息接收者的父類
};

后來實現(xiàn)變成objc_msgSendSuper2(arg, sel)纠脾,第一個參數(shù)是結(jié)構(gòu)體,其成員變量是:

  1. 消息接收者蜕青,仍然是子類對象
  2. 消息接收者的類對象苟蹈。但是其內(nèi)部實現(xiàn)右核,仍然是獲取父類類對象慧脱,從父類開始找
struct objc_super2 {
    __unsafe_unretained _Nonnull id receiver; // 消息接收者
    __unsafe_unretained _Nonnull Class current_class; // 消息接收者的類對象
};
- (instancetype)init
{
    if (self = [super init]) {
        NSLog(@"[self class] = %@", [self class]); // MJStudent
        NSLog(@"[self superclass] = %@", [self superclass]); // MJPerson

        NSLog(@"--------------------------------");

        // objc_msgSendSuper({self, [MJPerson class]}, @selector(class));
        NSLog(@"[super class] = %@", [super class]); // MJStudent
        NSLog(@"[super superclass] = %@", [super superclass]); // MJPerson
    }
    return self;
}

isKindOf

objc的源碼如下,要特別注意的是

  1. + (BOOL)isKindOfClass:贺喝,要求傳入的是metaClass菱鸥,但是最后一步會走到NSObject的class對象宗兼。
// 這句代碼的方法調(diào)用者不管是哪個類(只要是NSObject體系下的),都返回YES
NSLog(@"%d", [NSObject isKindOfClass:[NSObject class]]); // 1
NSLog(@"%d", [NSObject isMemberOfClass:[NSObject class]]); // 0
NSLog(@"%d", [MJPerson isKindOfClass:[Person class]]); // 0
NSLog(@"%d", [MJPerson isMemberOfClass:[Person class]]); // 0

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

面試題

以下代碼中采缚,是否能夠正常打印

@interface Person : NSObject
@property (copy, nonatomic) NSString *name;

- (void)print;
@end

@implementation MJPerson

- (void)print
{
    NSLog(@"my name is %@", self->_name);
}

@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    id cls = [MJPerson class];

    void *obj = &cls;

    [(__bridge id)obj print];
}

首先分析针炉,變量之間的關(guān)系挠他,如下圖所示

  1. 與正常調(diào)用實例方法的結(jié)構(gòu)相同扳抽,都包含指針變量,對象的ISA指針殖侵,類對象
  2. 所以可以調(diào)用到print方法
    變量之間的關(guān)系.png

執(zhí)行方法時贸呢,棧中變量地址的位置,如下圖所示

  1. 棧中的變量地址是從高到低拢军,即先出現(xiàn)的變量楞陷,占據(jù)高地址
  2. [super viewDidLoad];,上文說過茉唉,會有一個結(jié)構(gòu)體變量固蛾;接著是cls,接著是obj
  3. 根據(jù)實例對象的結(jié)構(gòu)圖度陆,訪問name成員變量艾凯,就是訪問self占據(jù)的指針
    棧中的變量地址.png

API使用

  • 成員變量是基本數(shù)據(jù)類型,需要先轉(zhuǎn)成指針懂傀,再bridge
    // 創(chuàng)建類
    Class newClass = objc_allocateClassPair([NSObject class], "MJDog", 0);
    class_addIvar(newClass, "_age", 4, 1, @encode(int));
    class_addIvar(newClass, "_weight", 4, 1, @encode(int));
    class_addMethod(newClass, @selector(run), (IMP)run, "v@:");
    // 注冊類
    objc_registerClassPair(newClass);
    // 如果是基本數(shù)據(jù)類型趾诗,需要先轉(zhuǎn)成指針,再bridge
    object_setIvar(person, ageIvar, (__bridge id)(void *)10);

具體應(yīng)用

  • 利用關(guān)聯(lián)對象(AssociatedObject)給分類添加屬性
  • 遍歷類的所有成員變量(修改textfield的占位文字顏色蹬蚁、字典轉(zhuǎn)模型恃泪、自動歸檔解檔)
  • 交換方法實現(xiàn)(交換系統(tǒng)的方法,method_exchangeImplementions會清空方法緩存
  • 利用消息轉(zhuǎn)發(fā)機(jī)制解決方法找不到的異常問題
+ (void)load { 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
        Class aClass = [self class]; 
 
        SEL originalSelector = @selector(viewWillAppear:); 
        SEL swizzledSelector = @selector(xxx_viewWillAppear:); 
 
        Method originalMethod = class_getInstanceMethod(aClass, originalSelector); 
        Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector); 
        
        // When swizzling a class method, use the following:
        // Class aClass = object_getClass((id)self);
        // ...
        // Method originalMethod = class_getClassMethod(aClass, originalSelector);
        // Method swizzledMethod = class_getClassMethod(aClass, swizzledSelector);
 
        BOOL didAddMethod = 
            class_addMethod(aClass, 
                originalSelector, 
                method_getImplementation(swizzledMethod), 
                method_getTypeEncoding(swizzledMethod)); 
 
        if (didAddMethod) { 
            class_replaceMethod(aClass, 
                swizzledSelector, 
                method_getImplementation(originalMethod), 
                method_getTypeEncoding(originalMethod)); 
        } else { 
            method_exchangeImplementations(originalMethod, swizzledMethod); 
        } 
    }); 
} 

數(shù)組和字典的方法交換

集合類型犀斋,由于使用了類簇.

  • 可變數(shù)組真正的類型是__NSArrayM
  • 可變字典真正的類型是__NSDictionaryM
  • 不可變字典真正的類型是__NSDictionaryI
   static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
       // 類簇:NSString贝乎、NSArray、NSDictionary叽粹,真實類型是其他類型
        Class cls = NSClassFromString(@"__NSArrayM");
        Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
        Method method2 = class_getInstanceMethod(cls, @selector(my_insertObject:atIndex:));
        method_exchangeImplementations(method1, method2);
    });

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class cls = NSClassFromString(@"__NSDictionaryM");
        Method method1 = class_getInstanceMethod(cls, @selector(setObject:forKeyedSubscript:));
        Method method2 = class_getInstanceMethod(cls, @selector(my_setObject:forKeyedSubscript:));
        method_exchangeImplementations(method1, method2);
        
        Class cls2 = NSClassFromString(@"__NSDictionaryI");
        Method method3 = class_getInstanceMethod(cls2, @selector(objectForKeyedSubscript:));
        Method method4 = class_getInstanceMethod(cls2, @selector(my_objectForKeyedSubscript:));
        method_exchangeImplementations(method3, method4);
    });
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末糕非,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子球榆,更是在濱河造成了極大的恐慌朽肥,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件持钉,死亡現(xiàn)場離奇詭異衡招,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)每强,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門始腾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來州刽,“玉大人,你說我怎么就攤上這事浪箭∷胍危” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵奶栖,是天一觀的道長匹表。 經(jīng)常有香客問我,道長宣鄙,這世上最難降的妖魔是什么袍镀? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮冻晤,結(jié)果婚禮上苇羡,老公的妹妹穿的比我還像新娘。我一直安慰自己鼻弧,他們只是感情好设江,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著攘轩,像睡著了一般叉存。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上撑刺,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天鹉胖,我揣著相機(jī)與錄音,去河邊找鬼够傍。 笑死甫菠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的冕屯。 我是一名探鬼主播寂诱,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼安聘!你這毒婦竟也來了痰洒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤浴韭,失蹤者是張志新(化名)和其女友劉穎丘喻,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體念颈,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡泉粉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嗡靡。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡跺撼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出讨彼,到底是詐尸還是另有隱情歉井,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布哈误,位于F島的核電站哩至,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏黑滴。R本人自食惡果不足惜憨募,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一紧索、第九天 我趴在偏房一處隱蔽的房頂上張望袁辈。 院中可真熱鬧,春花似錦珠漂、人聲如沸晚缩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荞彼。三九已至,卻和暖如春待笑,著一層夾襖步出監(jiān)牢的瞬間鸣皂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工暮蹂, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留寞缝,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓仰泻,卻偏偏與公主長得像荆陆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子集侯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉被啼,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,709評論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,554評論 33 466
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡介 Runt...
    樂樂的簡書閱讀 2,135評論 0 9
  • 我們常常會聽說 Objective-C 是一門動態(tài)語言,那么這個「動態(tài)」表現(xiàn)在哪呢棠枉?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,192評論 0 7
  • 有時候心甘情愿的去做一些事情辈讶,是不會感到累的命浴,越是辛苦,倒越是覺得幸福荞估。遇到他之前咳促,我從來沒有想過什么是喜歡稚新,在遇...
    七汐子閱讀 260評論 0 2