Runtime- 結(jié)合Demo, 讓你輕松搞定

Runtime.jpeg

關(guān)于Runtime的學習資料網(wǎng)上有很多了,但是大部分看起來有些晦澀難懂,看過一遍后讓人感覺有些走馬觀花, 還是理解不透Runtime.所以趁著這幾天的空閑時間, 我對自己理解的Runtime總結(jié)了一下鸟废,專門寫了一個Demo, 主要講一些常用的方法功能佩微,以實用為主旅敷,這樣才能更好更快的掌握Runtime的特性绞佩。結(jié)合著Demo學習會讓你更快掌握, 搞定后不論是在開發(fā)還是面試的時候, 我相信對您的作用會比較大
強烈建議結(jié)合著Demo代碼邊看代碼邊看文檔 Demo Github鏈接

一.Runtime簡介

我們應(yīng)該都知道 Objective-C 是一門動態(tài)語言丑掺,它會將一些工作放在代碼運行時才處理而并非編譯時获印。也就是說,有很多類和成員變量在我們編譯的時是不知道的街州,而在運行時兼丰,我們所編寫的代碼會轉(zhuǎn)換成完整的確定的代碼運行。

因此唆缴,只靠編譯器是不夠的鳍征,我們還需要一個運行時系統(tǒng)(Runtime system)來處理編譯后的代碼。

Runtime即我們通常叫的運行時面徽,也就是程序在運行的時候做的事情艳丛。是 Objective-C底層的一套C語言的API,是 iOS 內(nèi)部的核心之一匣掸,我們平時編寫的 Objective-C 代碼,底層都是基于它來實現(xiàn)的氮双,Objective-C代碼編譯后碰酝,其實都是Runtime形式的C語言代碼。

二.Runtime的作用

1.有些Objective-C不好實現(xiàn)的功能, 就可以使用Runtime, 比如:
  • 動態(tài)交換兩個方法的實現(xiàn)(常用于交換系統(tǒng)方法);
  • 動態(tài)添加對象的成員變量和成員方法;
  • 獲得某個類的所有成員變量及方法.
2.有時候項目中遇到很多具體的問題, 就需要使用Runtime來實現(xiàn)了,比如:
  • iOS黑魔法 Swizzle 的使用, 多用于攔截系統(tǒng)自帶的方法調(diào)用,比如攔截imageNamed:戴差、viewDidLoad送爸、alloc等;
  • 實現(xiàn)分類Category中可以增加屬性;
  • 實現(xiàn)NSCoding的自動歸檔和自動解檔暖释;
  • 實現(xiàn)字典和模型的自動轉(zhuǎn)換.

三.Runtime的使用

上面講的可能讓大家感覺還是不好理解, 比較書面, 下面我結(jié)合著具體的Demo來詳細上面說到的功能. 強烈建議結(jié)合著Demo代碼邊看代碼邊看文檔 Demo Github鏈接.

1.iOS黑魔法 Swizzle

要使用Swizzle, 首先需要引入頭文件 <objc/runtime.h>.

交換兩個方法的實現(xiàn)方法是:

void method_exchangeImplementations(Method m1 , Method m2)
  • 交換自定義類的方法實現(xiàn)
    創(chuàng)建一個Man類, 類中實現(xiàn)下面兩個方法, 同時需要在.h中聲明.
+ (void)eat {
    NSLog(@"吃");
}

+ (void)drink {
    NSLog(@"喝");
}

在使用這個Man類的時候, 調(diào)用方法:

[Man eat];
[Man drink];

打印出來的結(jié)果, 會先打印, 然后打印 .

接下來使用Swizzle, 交換兩個方法的實現(xiàn), 獲取類方法使用class_getClassMethod 袭厂,獲取對象方法使用class_getInstanceMethod.

// 獲取兩個類的類方法
Method m1 = class_getClassMethod([Man class], @selector(eat));
Method m2 = class_getClassMethod([Manclass], @selector(drink));
// 開始交換方法實現(xiàn)
method_exchangeImplementations(m1, m2);
// 交換后,還是先調(diào)用 eat,然后調(diào)用 drink
[Man eat];
[Man drink];

打印出來的結(jié)果是, 先打印 , 再打印, 能夠很明顯的看出調(diào)用的還是這兩個方法, 但方法的實現(xiàn)已經(jīng)交換.

  • 系統(tǒng)方法的攔截交換

比如遇到需求 iOS9 以上的版本需要使用另一套圖片, 這時候需要在一個個使用的地方判斷版本來加載不同的圖片嗎? 這樣會不會太繁瑣呢? 有好的解決方法嗎?

這時候就可以使用Swizzle, 來攔截UIImageimageName這個加載圖片的系統(tǒng)方法, 來交換成我們自己的方法.

(1) 創(chuàng)建一個UIImage的分類:(UIImage+Category);
(2) 在分類中實現(xiàn)一個自定義方法球匕,方法中寫要在系統(tǒng)方法中加入的語句纹磺,比如版本判斷修改圖片名;

//自定義方法
+ (UIImage *)yt_ImageNamed:(NSString *)name {
    double version = [[UIDevice currentDevice].systemVersion doubleValue];
    if (version >= 9.0) {
        name = [name stringByAppendingString:@"_ios9"];
    }
    return [UIImage yt_ImageNamed:name]; //方法交換后, 調(diào)用imageNamed方法, 讓有加載圖片的功能
}

注: 在自定義方法最后需要調(diào)用系統(tǒng)的imageNamed方法, 來實現(xiàn)加載圖片的功能, 因為交換了方法實現(xiàn), 所以這里調(diào)用的是交換后的自定義方法, 其實調(diào)用的是系統(tǒng)的imageNamed方法, 這里需要想想理解一下.

(3) Category中重寫 UIImageload 方法,實現(xiàn)方法的交換(只要能讓其執(zhí)行一次方法交換語句谐丢,load再合適不過了)
攔截交換:

+ (void)load {
    //獲取兩個類的類方法
    Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
    Method m2 = class_getClassMethod([UIImage class], @selector(yt_ImageNamed:));
    //開始交換方法實現(xiàn)
    method_exchangeImplementations(m1, m2); //注 在使用中, 如果iOS9以上版本使用另一版本的圖片, 就可以交換系統(tǒng)的方法, 直接使用 imageNamed方法, 調(diào)用的是yt_ImageNamed的實現(xiàn)
}

這樣就實現(xiàn)了攔截交換系統(tǒng)方法的功能, 在項目中遇到類似的問題可以靈活運用.

2.分類Category中創(chuàng)建屬性

大家都知道, 一般情況下在 iOS 分類中是無法設(shè)置屬性的,如果在分類的聲明中寫 @property 只能為其生成 getset 方法的聲明蚓让,但無法生成成員變量乾忱,就是雖然點語法能調(diào)用出來,但程序執(zhí)行后會crash.

針對分類中創(chuàng)建屬性, Runtime可以巧妙的實現(xiàn),使用一下方法:

void objc_setAssociatedObject(id object , const void *key ,id value ,objc_AssociationPolicy policy)

講需要設(shè)置的屬性值綁定到當前類即可, 具體步驟如下:
(1).創(chuàng)建一個分類Category历极,比如給任何一個對象都添加一個name屬性窄瘟,就是NSObject添加分類(NSObject+Category);
(2).先在.h 中 @property聲明出 getset方法,方便點語法調(diào)用;

@interface NSObject (Category)

@property (nonatomic, copy) NSString *name; //聲明屬性, 系統(tǒng)生成set和get方法,方便點語法調(diào)用

@end

(3).在.m 中重寫namesetget 方法趟卸,內(nèi)部利用 Runtime 給屬性賦值和取值.

#import "NSObject+Category.h"
#import <objc/runtime.h>

//.m中重寫set和get方法, 內(nèi)部利用runtime給屬性賦值和取值
@implementation NSObject (Category)

char nameKey; //用于取值的key

//set
- (void)setName:(NSString *)name{
    //將name值和對象關(guān)聯(lián)起來, 將name值存儲到當前對象中
    /*參數(shù):
        object: 給哪個對象設(shè)置屬性;
        key: 一個屬性對應(yīng)一個key, 存儲后需要通過這個key取出值, key可為double,int等任意類型, 建議用char可節(jié)省字節(jié);
        value: 給屬性設(shè)置的值;
        policy: 存儲策略 (assign, copy, retain);
     */
    objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY);
}

//get
- (NSString *)name{
    return objc_getAssociatedObject(self, &nameKey);
}

@end
3.獲取類的所有成員變量

一個對象在歸檔和解檔的 encodeWithCoderinitWithCoder: 方法中需要該對象所有的屬性進行 decodeObjectForKey:encodeObject: 蹄葱,一般情況下需要對每個屬性都寫歸解檔, 添加或刪除屬性對應(yīng)也要修改, 十分的不方便, 但是通過 Runtime 我們聲明中無論寫多少個屬性,都不需要再修改實現(xiàn)中的代碼了锄列。

(1)比如一個 Person 類,需要對它的成員變量進行歸解檔, 步驟如下:

  • 通過runtime 獲取當前所有成員變量名, 然后獲取到各個變量值, 以變量名為 key進行歸檔:
//歸檔
- (void)encodeWithCoder:(NSCoder *)coder
{
    [super encodeWithCoder:coder];
    
    //獲取所有成員變量
    unsigned int outCount = 0;
    /*
     參數(shù):
     1.哪個類
     2.接收值的地址, 用于存放屬性的個數(shù)
     3.返回值: 存放所有獲取到的屬性, 可調(diào)出名字和類型
     */
    Ivar *ivarArray = class_copyIvarList([self class], &outCount);
    
    for (int i = 0; i < outCount; i++) {
        Ivar ivar = ivarArray[i];
        //將每個成員變量名轉(zhuǎn)換為NSString對象類型
        NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
        
        //忽略不需要歸檔的屬性
        if ([[self ignoredNames] containsObject:key]) {
            continue; //跳過本次循環(huán)
        }
        
        //通過成員變量名, 取出成員變量的值
        id value = [self valueForKey:key];
        //再把值歸檔
        [coder encodeObject:value forKey:key];
        //這兩部就相當于 [coder encodeObject: @(self.name) forKey:@"_name"];
    }
    free(ivarArray);
}
  • 通過 runtime獲取到所有成員變量名, 以變量名為 key 解檔取出值:
//解檔
- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        //獲取所有成員變量
        unsigned int outCount = 0;
        
        Ivar *ivarArray = class_copyIvarList([self class], &outCount);
        
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivarArray[i];
            //獲取每個成員變量名并轉(zhuǎn)換為NSString對象類型
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            
            //忽略不需要解檔的屬性
            if ([[self ignoredNames] containsObject:key]) {
                continue;
            }
            
            //根據(jù)變量名解檔取值, 無論是什么類型
            id value = [coder decodeObjectForKey:key];
            //取出的值再設(shè)置給屬性
            [self setValue:value forKey:key];
            //這兩步相當于以前的 self.name = [coder decodeObjectForKey:@"_name"];
        }
        free(ivarArray); //釋放內(nèi)存
    }
    return self;
}

以上就實現(xiàn)了利用 runtime 進行歸解檔, 比之前一個個變量進行方便了很多, 但是在實際的運用中, 如果遇到一個類需要歸解檔就這樣寫, 多個需要重復(fù)寫, 這時候可以 在 NSObject 的分類中時間歸解檔, 這樣各個類使用時候只需要簡單的幾句就可以實現(xiàn), 步驟如下:
(1).為 NSObject 創(chuàng)建分類, 并在 .h 中聲明歸解檔的方法, 便于子類的使用;

@interface NSObject (Extension)

- (NSArray *)ignoredNames;
- (void)encode:(NSCoder *)aCoder; //重寫方法, 避免覆蓋系統(tǒng)方法
- (void)decode:(NSCoder *)aDecoder;

@end

(2)歸檔:

- (void)encode:(NSCoder *)aCoder{
    
    //一層層父類往上查找, 對父類的屬性執(zhí)行歸解檔方法
    Class c = self.class;
    while (c && c != [NSObject class]) {
        
        unsigned int outCount = 0;
        Ivar *ivarArray = class_copyIvarList([self class], &outCount);
        
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivarArray[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            
            //如果有實現(xiàn)該方法再去調(diào)用
            if ([self respondsToSelector:@selector(ignoredNames)]) {
                if ([[self ignoredNames] containsObject:key]) {
                    continue;
                }
            }
            
            id value = [self valueForKey:key];
            [aCoder encodeObject:value forKey:key]; //歸檔
        }
        free(ivarArray);
        c = [c superclass]; //向上查找父類
    }
    
}

(3).解檔:

- (void)decode:(NSCoder *)aDecoder{
    
    Class c = self.class;
    while (c && c != [NSObject class]) {
        
        unsigned int outCount = 0;
        Ivar *ivarAaary = class_copyIvarList([self class], &outCount);
        
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivarAaary[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            
            if ([self respondsToSelector:@selector(ignoredNames)]) {
                if ([[self ignoredNames] containsObject:key]) {
                    continue;
                }
            }
            
            id value = [aDecoder decodeObjectForKey:key];
            [self setValue:value forKey:key]; //解檔并賦值
        }
        free(ivarAaary);
        c = [c superclass];
    }
    
}

上面的代碼聲明的方法, 我換了一個方法名(不然會覆蓋系統(tǒng)原來的方法M荚啤),同時加了一個忽略屬性方法是否被實現(xiàn)的判斷邻邮,便于在使用時候?qū)Σ恍枰M行歸解檔的屬性進行判斷, 同時還加上了對父類屬性的歸解檔循環(huán)竣况。

這樣再使用之后只需要簡單的幾行代碼就可以實現(xiàn)歸解檔, 例如對 Cat 類進行歸解檔:

@implementation Car

//設(shè)置需要忽略的屬性
- (NSArray *)ignoredNames{
    return @[@"head"];
}

//在系統(tǒng)方法中調(diào)用自定義方法
- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super init];
    if (self) {
        [self decode:coder];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)coder
{
    [self encode:coder];
}

@end
4.字典轉(zhuǎn)模型

一般我們都是使用 KVC 進行字典轉(zhuǎn)模型,但是它還是有一定的局限性筒严,例如:模型屬性和鍵值對對應(yīng)不上會crash(雖然可以重寫setValue:forUndefinedKey: 方法防止報錯)丹泉,模型屬性是一個對象或者數(shù)組時不好處理等問題,所以無論是效率還是功能上鸭蛙,利用 runtime 進行字典轉(zhuǎn)模型都是比較好的選擇.

字典轉(zhuǎn)模型我們需要考慮三種特殊情況:
1.字典的key和模型的屬性匹配不上;
2.模型中嵌套模型(模型屬性是另外一個模型對象);
3.數(shù)組中裝著模型(模型的屬性是一個數(shù)組摹恨,數(shù)組中是一個個模型對象).

針對上面的三種特殊情況,我們一個個詳解下處理過程.
(1).先是字典的 key 和模型的屬性不對應(yīng)的情況娶视。
不對應(yīng)的情況有兩種晒哄,一種是字典的鍵值大于模型屬性數(shù)量,這時候我們不需要任何處理,因為 runtime 是先遍歷模型所有屬性揩晴,再去字典中根據(jù)屬性名找對應(yīng)值進行賦值勋陪,多余的鍵值對也當然不會去看了;另外一種是模型屬性數(shù)量大于字典的鍵值對硫兰,這時候由于屬性沒有對應(yīng)值會被賦值為nil诅愚,就會導(dǎo)致crash,我們只需加一個判斷即可,代碼如下:

- (void)setDict:(NSDictionary *)dict {
    
    Class c = self.class;
    while (c &&c != [NSObject class]) {
        
        unsigned int outCount = 0;
        Ivar *ivars = class_copyIvarList(c, &outCount);
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            
            // 成員變量名轉(zhuǎn)為屬性名(去掉下劃線 _ )
            key = [key substringFromIndex:1];
            // 取出字典的值
            id value = dict[key];
            
            // 如果模型屬性數(shù)量大于字典鍵值對數(shù)理劫映,模型屬性會被賦值為nil而報錯,這時候判斷值是nil的話, 忽略這個模型的屬性即可.
            if (value == nil) continue;
            
            // 將字典中的值設(shè)置到模型上
            [self setValue:value forKeyPath:key];
        }
        free(ivars);
        c = [c superclass];
    }
}

(2).模型屬性是另外一個模型對象的情況, 這時候我們就需要利用 runtimeivar_getTypeEncoding 方法獲取模型對象類型违孝,對該模型對象類型再進行字典轉(zhuǎn)模型,也就是進行遞歸泳赋,需要注意的是我們要排除系統(tǒng)的對象類型雌桑,例如NSString,下面的方法中我添加了一個類方法方便遞歸祖今。

#import "NSObject+JSONExtension.h"
#import <objc/runtime.h>

@implementation NSObject (JSONExtension)

- (void)setDict:(NSDictionary *)dict {
    
    Class c = self.class;
    while (c &&c != [NSObject class]) {
        
        unsigned int outCount = 0;
        Ivar *ivars = class_copyIvarList(c, &outCount);
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            
            // 成員變量名轉(zhuǎn)為屬性名(去掉下劃線 _ )
            key = [key substringFromIndex:1];
            // 取出字典的值
            id value = dict[key];
            
            // 如果模型屬性數(shù)量大于字典鍵值對數(shù)理校坑,模型屬性會被賦值為nil而報錯
            if (value == nil) continue;
            
            // 獲得成員變量的類型
            NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
            
            // 如果屬性是對象類型
            NSRange range = [type rangeOfString:@"@"];
            if (range.location != NSNotFound) {
                // 那么截取對象的名字(比如@"Dog",截取為Dog)
                type = [type substringWithRange:NSMakeRange(2, type.length - 3)];
                // 排除系統(tǒng)的對象類型
                if (![type hasPrefix:@"NS"]) {
                    // 將對象名轉(zhuǎn)換為對象的類型千诬,將新的對象字典轉(zhuǎn)模型(遞歸)
                    Class class = NSClassFromString(type);
                    value = [class objectWithDict:value];
                }
            }
            
            // 將字典中的值設(shè)置到模型上
            [self setValue:value forKeyPath:key];
        }
        free(ivars);
        c = [c superclass];
    }
}

+ (instancetype )objectWithDict:(NSDictionary *)dict {
    NSObject *obj = [[self alloc]init];
    [obj setDict:dict];
    return obj;
}

(3).第三種情況是模型的屬性是一個數(shù)組耍目,數(shù)組中是一個個模型對象,我們既然能獲取到屬性類型,那就可以攔截到模型的那個數(shù)組屬性徐绑,進而對數(shù)組中每個數(shù)據(jù)遍歷并字典轉(zhuǎn)模型邪驮,但是我們不知道數(shù)組中的模型都是什么類型,我們可以聲明一個方法傲茄,該方法目的不是讓其調(diào)用毅访,而是讓其實現(xiàn)并返回數(shù)組中模型的類型, 這樣就可以對數(shù)組中的數(shù)據(jù)進行字典轉(zhuǎn)模型.
在分類中聲明了 arrayObjectClass 方法, 子類調(diào)用返回數(shù)組中模型的類型即可.

@interface NSObject (JSONExtension)

- (void)setDict: (NSDictionary *)dict;
+ (instancetype)objectWithDict: (NSDictionary *)dict;

//告訴數(shù)組中都是什么類型的模型對象
- (NSString *)arrayObjectClass;

@end

然后進行字典轉(zhuǎn)模型:

#import "NSObject+JSONExtension.h"
#import <objc/runtime.h>

@implementation NSObject (JSONExtension)

- (void)setDict:(NSDictionary *)dict{
    
    Class c = self.class;
    while (c && c != [NSObject class]) {
        unsigned int outCount = 0;
        Ivar *ivarArray = class_copyIvarList([self class], &outCount);
        
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivarArray[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            
            //成員變量名轉(zhuǎn)為屬性名(去掉下劃線_)
            key = [key substringFromIndex:1];
            //取出字典的值
            id value = dict[key];
            
            //如果模型屬性數(shù)量大于字典鍵值對數(shù)量,則key對應(yīng)dict中沒有值, 模型屬性會被賦值為nil而報錯
            if (value == nil) {
                continue;
            }
            
            //獲得成員變量的類型
            NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
            
            //如果屬性是對象類型
            NSRange range = [type rangeOfString:@""];
            if (range.location != NSNotFound) {
                //那么截取對象的名字(比如@"Dog", 截取為Dog)
                type = [type substringWithRange:NSMakeRange(2, type.length - 3)];
                //排除系統(tǒng)的對象類型
                if (![type hasPrefix:@"NS"]) {
                    //將對象名轉(zhuǎn)換為對象的類型, 將新的對象字典轉(zhuǎn)模型(遞歸)
                    Class class = NSClassFromString(type);
                    value = [class objectWithDict:value];
                }else if ([type isEqualToString:@"NSArray"]){
                    //如果是數(shù)組類型, 將數(shù)組中的每個模型進行字典轉(zhuǎn)模型
                    NSArray *array = (NSArray *)value;
                    NSMutableArray *mArray = [NSMutableArray array];//先創(chuàng)建一個臨時數(shù)組存放模型
                    
                    //獲取到每個模型的類型
                    id class;
                    if ([self respondsToSelector:@selector(arrayObjectClass)]) {
                        NSString *classStr = [self arrayObjectClass];
                        class = NSClassFromString(classStr);
                    }else{
                        NSLog(@"數(shù)組內(nèi)模型是未知類型");
                        return;
                    }
                    
                    //將數(shù)組中的所有模型進行字典轉(zhuǎn)模型
                    for (int i = 0; i < array.count; i++) {
                        [mArray addObject:[class objectWithDict:value[i]]];
                    }
                    
                    value = mArray;
                }
            }
            
            //將字典中的值設(shè)置到模型上
            [self setValue:value forKey:key];
        }
    }
    
}

+ (instancetype)objectWithDict:(NSDictionary *)dict{
    NSObject *obj = [[self alloc] init];
    [obj setDict:dict];
    return obj;
}

@end

以上介紹了幾點Runtime的特性, 并結(jié)合我們開發(fā)中可能遇到的情況就行講解, 這樣大家可以更好的理解, 建議大家對照著我的 Demo 詳細看下, 自己也試一試, 只有自己動手才能真正的理解.

有什么問題可以隨時給我留言, 我看到后會第一時間回復(fù), 如果看完文章感覺對您有所幫忙的話, 不妨關(guān)注喜歡下哦, 看 demo 時候麻煩也 star下!!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市盘榨,隨后出現(xiàn)的幾起案子喻粹,更是在濱河造成了極大的恐慌,老刑警劉巖草巡,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件磷斧,死亡現(xiàn)場離奇詭異,居然都是意外死亡捷犹,警方通過查閱死者的電腦和手機弛饭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來萍歉,“玉大人侣颂,你說我怎么就攤上這事∏购ⅲ” “怎么了憔晒?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵藻肄,是天一觀的道長。 經(jīng)常有香客問我拒担,道長嘹屯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任从撼,我火速辦了婚禮州弟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘低零。我一直安慰自己婆翔,他們只是感情好,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布掏婶。 她就那樣靜靜地躺著啃奴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪雄妥。 梳的紋絲不亂的頭發(fā)上最蕾,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機與錄音老厌,去河邊找鬼瘟则。 笑死,一個胖子當著我的面吹牛梅桩,可吹牛的內(nèi)容都是我干的壹粟。 我是一名探鬼主播拜隧,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼宿百,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了洪添?” 一聲冷哼從身側(cè)響起垦页,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎干奢,沒想到半個月后痊焊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡忿峻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年薄啥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逛尚。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡垄惧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绰寞,到底是詐尸還是另有隱情到逊,我是刑警寧澤铣口,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站觉壶,受9級特大地震影響脑题,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜铜靶,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一叔遂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧旷坦,春花似錦掏熬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至捆蜀,卻和暖如春疮丛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辆它。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工誊薄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锰茉。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓呢蔫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親飒筑。 傳聞我的和親對象是個殘疾皇子片吊,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355

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

  • 引導(dǎo) 對于從事 iOS 開發(fā)人員來說,所有的人都會答出「 Runtime 是運行時 」协屡,什么情況下用 Runtim...
    Winny_園球閱讀 4,209評論 3 75
  • 對于從事 iOS 開發(fā)人員來說俏脊,所有的人都會答出【runtime 是運行時】什么情況下用runtime?大部分人能...
    夢夜繁星閱讀 3,721評論 7 64
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,103評論 1 32
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 29,392評論 8 265
  • 前言,文章是轉(zhuǎn)載的肤晓,因為之前收藏的爷贫,今天突然發(fā)現(xiàn)沒了。不知道什么原因补憾,簡書搜索不到了漫萄,有幾篇同樣轉(zhuǎn)載的,但是代碼沒...
    安靜就好_閱讀 2,634評論 6 42