runtime

autoreleasePool

autoreleasePool认识
autoreleasePool怎么加进去的,什么时候push,什么时候pop,什么时候会用
Autoreleasepool的原理?所使用的的数据结构是什么
  1. 自动释放池,当由autorelease修饰或者@autoreleasePool代码中的临时变量会被加到自动是释放池中,当离开@autoreleasePool代码或者,RunLoop即将进入休眠时会对所有放入自动的释放池的对象发送release消息。
  2. autoreleasePool是由autoreleasePoolPage组成的双向链表实现的,autoreleasePoolPage中会有指向上个节点和下个节点的执行,还有当前page栈顶的地址,自动释放池是可以嵌套的,在单个释放池开始的时候会执行push操作,插入一个哨兵对象,标记单个自动释放池的开始地址,当这个自动释放池要结束的时候会进行pop操作,对当前地址到哨兵对象之间的对象发送release消息,并将其从page中删除。
  3. push操作时机: 使用@autoreleasePool代码块,刚进入RunLoop时,RunLoop即将唤醒时。pop:离开@autoreleasePool代码块,RunLoop即将进入休眠时。
  4. 每一个线程创建的时候就会有一个autorelease pool的创建,并且在线程退出的时候,清空整个autorelease pool。

关联对象

如何给关联对象设置成弱引用
- (void)setContext:(CDDContext*)object {
   id __weak weakObject = object;
   id (^block)() = ^{ return weakObject; };    
   objc_setAssociatedObject(self, @selector(context), block, OBJC_ASSOCIATION_COPY); 
} 
   
- (CDDContext*)context {
   id (^block)() = objc_getAssociatedObject(self, @selector(context));  
   id curContext = (block ? block() : nil); 
   return curContext; 
}
关联对象的应用?系统如何实现关联对象的
关联对象的如何进行内存管理的?
objc_associatedObject(observer, key, value, police)
AssociationsManager
AssociationsHashMap <observer, ObjcAssociationsMap>
ObjcAssociationsMap <key ObjcAssociation>
ObjcAssociation: value police

set:根据police去存,如果是retain则进行root_retain操作,如果是copy则进行copy操作, 将isa_t has_assoc设置YES

weak

weak unowned的区别

todo:

weak 原理,一个对象如果有多个weak指针指向,怎么销毁

对象释放时,调用clearDeallocating函数根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个enrty从weak表中删除,然后清除对象的记录。

weak的实现原理?SideTable的结构是什么样的
struct SideTables {

}

struct SideTable {
    spinlock_t lock;
    RefcountMap refcnts;
    weak_table_t weak_table;
}

struct weak_table_t {
    // 存储弱引用对象的相关信息
    weak_entry_t *weak_entries;
    // 数组中的个数
    size_t num_entries;
    uintptr_t mask;
    // 可能会发生的hash冲突的最大次数
    uintptr_t max_hash_displacement;
}

struct weak_entry_t {
    // 被弱引用的对象
    DisguisedPtr<objc_object> referent;
    union{
        struct {
            // 弱引用该对象指针地址的hash数组
            weak_referrer_t *referents;
            // 是否使用动态hash数组标记位
            uintptr_t out_of_line_ness: 2
            uintptr_t num_refs;
            uintptr_t mask;
            // 可能会发生的hash冲突的最大次数
            uintptr_t max_hash_displacement;
        }
        struct {
            // 弱引用该对象指针地址的带下固定的hash数组4个
            weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
        }
    }
}
当弱引用该对象的指针数目小于等于WEAK_INLINE_COUNT则使用inline_referrers否则使用out_of_line_ness。
weak 与 assign 得区别?代理使用assign会出现问题吗?

assign一般用于值引用类型,如果会代理使用assign,会造成野指针的问题。
野指针:当对象被释放或者收回,但是对该指针么有任何的修改,以至于该指针仍旧指向已经回收的内存地址,这将会产生无法预料的结果。assign可以修饰值引用类型是因为其存的是值而不是指针。

其它内存

内存泄漏的几种方法/ARC下哪些情况会造成内存泄漏
 block delegate NSTimer CF需要自己释放 通知(iOS9之后苹果修复) 相互引用
block一定会造成强引用吗

不一定,还是要根据是否产生了循环引用来分析。

循环引用,为什么要在block中加strong,不加会怎样
delloc的调用顺序
ARC的实现原理?ARC下对retain & release做了哪些优化

Category

为什么Category 不能添加属性,为什么还要设计ro,直接添加不是很方便么,如果将flag改变ro能不能写入,改变了会怎样

运行时对象的内存布局早已经确定了,如果此时添加成员变量会破坏内存布局。
在编译期间确定对象的内存布局,并且将对象的方法和协议收集记录起来,

Category 和主类方法谁先调用

只会调用category里的方法,category的调用顺序根据文件的编译顺序compile sources(只会调用最后参加编译的分类)
+load:load的执行顺序是先类后分类,分类的顺序是按照参加编译的顺序来的

category & extension区别,能给NSObject添加Extension吗,结果如何

extension在编译期决议,他就是类的一部分,在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类,他伴随类的产生而产生,extension可以添加实例变量。不能给NSObject添加Extension,因为在extension中添加的方法或属性必须在源类的文件的.m文件中实现才可以。

分类的作用?底层是怎样得?

作用:

  1. 将私有方法公开
  2. 将类的实现分开在几个不同的文件里。

底层:category加载过程:

  1. 编译器生成了实例方法列表和属性列表。
  2. 编译器在DATA段下的objc_catlist section里保存了一个的category_t数组。
  3. 在load_images时吧category的实例方法,协议以及属性添加到类上
typedef struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethod;
    struct method_list_t *classMethods;
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
}

消息转发:

methodswizzling的应用,方法交换得底层?

方法交换:

Method originalMethod = class_getInstanceMethod(theClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(swizzleClass ?: theClass, swizzledSelector);
// 先向theClass中添加原方法,imp指针指向新方法
BOOL success = class_addMethod(theClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (success) {
     // 如果成功则将theClass中的swizzledSelector方法的imp替换为老方法的imp
     class_replaceMethod(theClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
     // 如果失败则表明原方法之前就存在,则直接交互
     method_exchangeImplementations(originalMethod, swizzledMethod);
}
做这个处理是为了防止修改的是父类里的方法,这样的话会影响到他的兄弟类
NSObject缺少方法
- (id)wbSafeKit_forwardingTargetForSelector:(SEL)selector {
    BOOL aBool = [self respondsToSelector:@selector(selector)];
    if (aBool || signature) {
        return [self wbSafeKit_forwardingTargetForSelector:selector];
    } else {
        safeKit_reportBug...
        // 如果调用了没有实现的方法,则给当前类添加这个方法,之后再继续执行
        [safeKitObj addAnyFunc:selector];
        return safeKitObj;
    }
}

- (void)addAnyFunc:(SEL)selector {
    funcTypeEncodeing:有多少参数则需要拼多少@
    class_addMethod([WSafeKitObj class], selector, (IMP)safeKitGodIMP, funcTypeEncodeing);
}
NSString/NSNumber

判断是否为对方,如果为对方则直接创建对方

NSArray/NSMutableArray
  1. objectAtIndex;
  2. addObject;
  3. insert/remove/replace/set..index
NSDictory(__NSPlaceholderDictionary)/NSMutableDictory(__NSDictoryM)
  1. setObject:forKey object/key/try
  2. removeObjectForKey key
埋了什么点,如果给按钮点击埋点怎么实现,为什么交换方法要自己调用自己不会递归么

不会递归,因为交换方法调用方法的imp指针指向的不是自己。

SEL怎么找到imp的,消息发送流程

实例对象的isa指向的是类对象,首先会到cache中去找Method,如果找不到则去MethodList中去找,如果找不到则通过superClass从其父类中去找,直到NSObject的类对象。如果找到了则获取Method中的imp指针。
类的isa指针指向的是元类对象。

消息转发流程,引出错误捕捉
OC的消息转发机制:
怎么保证自己的类一定能调用到自己写的方法?(不知道有没有人会写一个分类,里面有和自己的类方法名字一模一样)(美团)
Method originalMethod = class_getInstanceMethod(theClass, originalSelector);
在方法调用的时候,方法查询-> 动态解析-> 消息转发 之前做了什么
说说消息转发机制的优劣(和其它语言相比)
中内省的几个方法有哪些?内部实现原理是什么

内省

isSubclassOfClass:是否是另一个类型的子类
isAncestorOfObject:是否是另一个类型非父类
respondsToSelector:是否能够相应某个方法
conformsToProtocol:是否遵循某个协议

class方法和object_getClass方法有什么区别

  • 实例class方法就直接返回object_getClass(self)
  • 类class方法直接返回self,而object_getClass

isKindOfClass&isMemberOfClass

int main(int argc, const char * argv[]) { 
    @autoreleasepool {
        BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; 
        BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; 
        BOOL re3 = [(id)[AKPerson class] isKindOfClass:[AKPerson class]]; 
        BOOL re4 = [(id)[AKPerson class] isMemberOfClass:[AKPerson class]]; 
        NSLog(@"\n re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n", re1, re2, re3, re4); 
        // 題目2 
        BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; 
        BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; 
        BOOL re7 = [(id)[AKPerson alloc] isKindOfClass:[AKPerson class]]; 
        BOOL re8 = [(id)[AKPerson alloc] isMemberOfClass:[AKPerson class]]; 
        NSLog(@"\n re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n", re5, re6, re7, re8); 
    } 
    return 0; 
}
+ (BOOL)isKindOfClass:(Class)cls {
    // 获取类对象的isa指针(此时指向的是元类)
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
        // 如果元类/元类的父类/NSObject类对象 == cls则返回YES
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    // 获取实例对象的isa指针(此时指向的是类对象)
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        // 如果类对象/类对象的父类 == cls则返回YES
        if (tcls == cls) return YES;
    }
    return NO;
}

+ (BOOL)isMemberOfClass:(Class)cls {
    // 获取类对象的isa指针(此时指向的是元类)
    // 如果元类 == cls则返回YES
    return object_getClass((id)self) == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    // 获取实例对象的isa指针(此时指向的是类对象)
    // 如果类对象 == cls则返回YES
    return [self class] == cls;
}

Class object_getClass(id obj) { 
    if (obj) return obj->getIsa(); 
    else return Nil; 
}

- (Class)class {
    return object_getClass(self);
}

通知

实现原理(结构设计、通知如何存储的、name&observer&SEL之间的关系等)
typedef struct Obs {
    id observer;
    SEL selector;
    struct Obs *next; 指向的下一个节点
    int retained; 结构保留引用计数
    struct NCTbl *link; 
}


typedef struct NCTbl {
    // 存储没有传入名字的通知 
    GSIMapTable nameless;
    // 存储传入名字的通知
    GSIMapTable named;
    Observation *wildcard;
    NSRecursiveLock *_lock
}

nameless表中:
GSIMapTable 的结构如下
object:Observation
object:Observation
object:Observation

named表中:
GSIMapTable 的结构如下
name : maptable 
name : maptable 
name : maptable

maptable的结构如下
object : Observation
object : Observation
object : Observation
NSNotificationCenter接受消息和发送消息是在一个线程里吗?如何异步发送消息

由于TABLE资源的问题同一个现场会安顺序执行

postNoticationName:

lockNCTable(TABLE);
...//找到对应的observer对象
unlockNCTable(TABLE);
...//执行performSelector
NSNotificationQueue是异步还是同步发送?在哪个线程响应
NSNotificationQueue和runloop的关系

NSNotificationQueue是依赖runLoop的,

typedef NS_ENUM(NSUInteger, NSPostingStyle) {
    // 当runloop处于空闲状态时post
    NSPostWhenIdle = 1,
    // 当当前runloop完成之后立即post
    NSPostASAP = 2,
    // 立即post
    NSPostNow= 3
};
如何保证通知接收的线程在主线程
  1. 在主线程post
  2. 接收到通知后切主线程
  3. 使用blcok接口addObserverForName:queue... 指定queue为主队列
页面销毁时不移除通知会崩溃吗

之前崩溃是因为持有Observer的是__unsafe_unretain指针对象,当对象释放时会有野指针的问题
现在使用的是weak所以不会有问题

多次添加同一个通知会是什么结果?多次移除通知呢

源码没有进行重复过滤,所以添加一同一个通知,等于就是添加了两次,post会触发两次

下面的方式能接收到通知吗?为什么
NSObject *object = [[NSObject alloc] init];

struct objc_object {
    isa_t isa;
}

struct objc_class : objc_object {
    isa_t isa;
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;
}

union isa_t {
    isa_t() {}
    isa_t(uintptr_t value) : bits(value) {}
    
    Class cls;
    unitpr_t bits;
    
    struct {
        uintptr_t indexed:1
        uintptr_t has_assoc:1
        uintptr_t has_cxx_dtor:1
        uintptr_t shiftcls:44
        uintptr_t magic:6
        uintptr_t weakly_referenced:1
        uintptr_t deallocating:1
        uintptr_t has_sidetable_tc:1
        uintptr_t extra_tc:8
    }
}

indexed: 
0 表示raw isa,也就是没有结构体的部分,访问对象的isa会直接返回一个cls的指针。
1 表示isa不是指针,关于类的指针都是保存在shiftcls中。

has_assoc:
对象含有或者曾经含有关联引用,没有关联引用的可以更快的释放。

weakly_referenced:
对象被指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放。

deallocating:
对象正在释放内存

has_sidetable_rc:
对象的引用计数太大了,存不下

extra_rc:
对象的引用计数超过1,会存在这里,如果引用计数为10,extra_rc的值就为9

class_data_bits_t *data;

struct class_rw_t {
    uint32_t flags;
    uint32_t version;
    
    const class_ro_t *ro;
    
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    
    Class firstSubclass;
    Class nextSiblingClass;
}

struct class_ro_t {
   uint32_t flags;
   uint32_t instanceStart;
   uint32_t instanceSize;
   uint32_t reserved;
   
   const uint8_t * ivarLayout;
   
   const char *name;
   method_list_t *baseMethodList;
   protocol_list_t *baseProtocols;
   const ivar_list_t *ivars;
   
   const uint8_t *weakIvarLayout;
   property_list_t *baseProperties;
}



在编译期间类的结构中的class_data_bits_t *data指向的是一个class_ro_t *指针,然后再加在ObjC运行时的过程中再realizeClass方法中:
1. 从class_data_bits_t调用data方法,结果从class_rw_t强制转换为class_ro_t指针。
2. 初始化一个class_rw_t结构体。
3. 设置结构体ro的值以及flag。
4. 最后设置正确的data。

之后realizeClass调用methodizeClass方法来将类自己实现的方法,属性和遵循的协议加载到methods,properties和protocols列表中

类再内存的位置是在编译期就决定的,

在OC中,对象的方法并没有存储在对象的结构体中,当实例方法被调用时,他要通过自己持有的isa来查找对应的类,然后在这里的class_data_bits_t结构体中查找对应方法的实现,如果找不到对应的方法实现,通过super_class从用父类去查询,直到根类。

SideTable

struct SideTable {
    spinlock_t slock;
    RefcountMap refcnts;
    weak_table_t weak_table;
}

方法的本质是什么?SEL/IMP分别是什么?二者有什么联系?

  1. 方法的本质是发送消息:

    • 快速查找:objc_msgSend~cache_t.
    • 慢速查找:lookUpImpOrForward 递归自己和自己的父类
    • 查不到消息:resolveInstanceMethod 动态方法解析
    • 消息快速转发:forwardingTargetForSelector
    • 慢速转发:methodSignatureForSelector & forwardInvocation
  2. SEL/IMP分别是什么?二者有什么联系?

    • SEL是方法编号,在read_images期间就编译进了内存。
    • IMP是函数实现指针,找IMP就是找函数的过程。
struct objc_method {
    SEL method_name;
    Char *method_types;
    IMP method_imp;
}

method_name 类型为SEL:
selector 方法选择器,也是方法的编号,是类成员方法的指针,无论什么类里,只要方法名相同,SEL实际时根据方法名hash化了的字符串。而对于字符串的比较仅仅需要比较他们的地址就可以了,SEL的存在加快了查询方法的速度。


method_types 是一个char指针,存储着方法的参数类型和返回值类型
method_imp 方法实现,类型为IMP

在类的(实例和类方法)调度表(dispatch table)中的每一个实例代表一个方法Method,其名叫做选择器SEL,并对应着一种方法实现称之为IMP,有了Method就可以使用SEL找到对应的IMP,SEL就是为了查找方法的最终实现IMP。

内省

isSubclassOfClass:是否是另一个类型的子类
isAncestorOfObject:是否是另一个类型非父类
respondsToSelector:是否能够相应某个方法
conformsToProtocol:是否遵循某个协议

class方法和object_getClass方法有什么区别

  • 实例class方法就直接返回object_getClass(self)
  • 类class方法直接返回self,而object_getClass

isKindOfClass&isMemberOfClass

int main(int argc, const char * argv[]) { 
    @autoreleasepool {
        BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; 
        BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; 
        BOOL re3 = [(id)[AKPerson class] isKindOfClass:[AKPerson class]]; 
        BOOL re4 = [(id)[AKPerson class] isMemberOfClass:[AKPerson class]]; 
        NSLog(@"\n re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n", re1, re2, re3, re4); 
        // 題目2 
        BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; 
        BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; 
        BOOL re7 = [(id)[AKPerson alloc] isKindOfClass:[AKPerson class]]; 
        BOOL re8 = [(id)[AKPerson alloc] isMemberOfClass:[AKPerson class]]; 
        NSLog(@"\n re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n", re5, re6, re7, re8); 
    } 
    return 0; 
}
+ (BOOL)isKindOfClass:(Class)cls {
    // 获取类对象的isa指针(此时指向的是元类)
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
        // 如果元类/元类的父类/NSObject类对象 == cls则返回YES
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    // 获取实例对象的isa指针(此时指向的是类对象)
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        // 如果类对象/类对象的父类 == cls则返回YES
        if (tcls == cls) return YES;
    }
    return NO;
}

+ (BOOL)isMemberOfClass:(Class)cls {
    // 获取类对象的isa指针(此时指向的是元类)
    // 如果元类 == cls则返回YES
    return object_getClass((id)self) == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    // 获取实例对象的isa指针(此时指向的是类对象)
    // 如果类对象 == cls则返回YES
    return [self class] == cls;
}

Class object_getClass(id obj) { 
    if (obj) return obj->getIsa(); 
    else return Nil; 
}

- (Class)class {
    return object_getClass(self);
}

[self class] & [super class]

AKStudentAKPerson的子類, 主程序初始化AKStudent會打印什麼?

#import "AKStudent.h" @implementation AKStudent 
- (instancetype)init { 
    if (self = [super init]) { 
        NSLog(@"[self class] = %@", NSStringFromClass([self class])); 
        NSLog(@"[super class] = %@", NSStringFromClass([super class])); 
    } 
    return self; 
} 
@end

结果:
[self class] = AKStudent
[super class] = AKStudent
[super class] -> objc_msgSendSuper 

objc_msgSendSuper(
    void /* struct objc_super *super, SEL op, ... */ ) 
    struct objc_super { 
        __unsafe_unretained _Nonnull id receiver; 
        __unsafe_unretained _Nonnull Class super_class; 
    };
)

objc_msgSend走的是消息查找流程,会递归查找class方法;objc_msgSendSuper直接跳过self的查找,从objc_super结构体开始。

Class与内存地址

下面地址会?Compile Error / Runtime Crash / NSLog…?

@interface Sark : NSObject
@property (nonatomic, copy) NSString *name;
- (void)speak;
@end
@implementation Sark
- (void)speak {
    NSLog(@"my name's %@", self.name);
}
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    id cls = [Sark class];
    void *obj = &cls;
    [(__bridge id)obj speak];
}
@end
  1. obj被转换成一个指向SarkClass的指针,然后使用id转换成了objc_object类型。obj现在已经是一个Sark类型的实例对象了。所以可以调用speak方法。

  2. 正确答案会输出 my name is <ViewController: 0x7ff6d9f31c50>

struct __rw_objc_super arg = {
    (id)self,
    (id)class_getSuperclass(objc_getClass("ViewController"))
 };
 
 所以按照viewDidLoad执行时各个变量入栈书讯从高到低为 self,_cmd,self.class,self,obj.
 
 1. 第一个`self`和第二个`_cmd`是隐藏参数。
 2. 第三个self.class和第四个self是[super viewDidLoad]方法执行时候的参数。
 (id)class_getSuperclass(objc_getClass("ViewController"))?
 
 Objc中的对象是一个指向ClassObject地址的变量,即id obj = &ClassObject,而对象的实例变量 `void *ivar = &obj + offset(N)`
 原因还是和上面的类似。按`viewDidLoad`执行时各个变量入栈顺序从高到底为`self`,`_cmd`,`self.class`,`self`,`myName`,`obj`。`obj`往上偏移32位,就是self。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,501评论 6 544
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,673评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,610评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,939评论 1 318
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,668评论 6 412
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,004评论 1 329
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,001评论 3 449
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,173评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,705评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,426评论 3 359
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,656评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,139评论 5 364
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,833评论 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,247评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,580评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,371评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,621评论 2 380

推荐阅读更多精彩内容

  • runtime 和 runloop 作为一个程序员进阶是必须的,也是非常重要的, 在面试过程中是经常会被问到的, ...
    made_China阅读 1,218评论 0 7
  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 248评论 1 0
  • 引导 对于从事 iOS 开发人员来说,所有的人都会答出「 Runtime 是运行时 」,什么情况下用 Runtim...
    Winny_园球阅读 4,229评论 3 75
  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 3,732评论 7 64
  • 主要参考链接: http://yulingtianxia.com/blog/2014/11/05/objectiv...
    Kevin_Junbaozi阅读 3,327评论 0 10