Runtime 在實(shí)際開發(fā)中的應(yīng)用

runtime再實(shí)際開發(fā)中主要應(yīng)用

1.動(dòng)態(tài)添加一個(gè)類

2.通過runtime獲取一個(gè)類的所有屬性,我們可以做些什么

? ?2.1. 打印一個(gè)類的所有ivar, property 和 method(簡單直接的使用)

? ?2.2.動(dòng)態(tài)變量控制

? ?3.3.在NSObject的分類中增加方法來避免使用KVC賦值的時(shí)候出現(xiàn)崩潰

? ??3.4.自動(dòng)的歸檔和解檔

? ? 3.5.字典轉(zhuǎn)模型

3.利用runtime的動(dòng)態(tài)交換方法實(shí)現(xiàn),我們可以做什么?

? ? ?3.1. 方法簡單的交換 ?

? ? ?3.2. 攔截系統(tǒng)方法(Swizzle 黑魔法),也可以說成對(duì)系統(tǒng)的方法進(jìn)行替換

? ? ? 3.3. 運(yùn)行時(shí)實(shí)現(xiàn)多繼承的效果

4.動(dòng)態(tài)添加方法

5.利用運(yùn)行時(shí)set和get這兩個(gè)API,可以讓類別可以添加屬性

6.萬能界面跳轉(zhuǎn)(使用了runtime的N多個(gè)方法?

7.插件開發(fā)



一、動(dòng)態(tài)添加一個(gè)類(“KVO”的實(shí)現(xiàn)是利用了runtime能夠動(dòng)態(tài)添加類)

原來當(dāng)你對(duì)一個(gè)對(duì)象進(jìn)行觀察時(shí), 系統(tǒng)會(huì)自動(dòng)新建一個(gè)類繼承自原類, 然后重寫被觀察屬性的setter方法. 然后重寫的setter方法會(huì)負(fù)責(zé)在調(diào)用原setter方法前后通知觀察者. 然后把原對(duì)象的isa指針指向這個(gè)新類, 我們知道, 對(duì)象是通過isa指針去查找自己是屬于哪個(gè)類, 并去所在類的方法列表中查找方法的, 所以這個(gè)時(shí)候這個(gè)對(duì)象就自然地變成了新類的實(shí)例對(duì)象.

就像KVO一樣, 系統(tǒng)是在程序運(yùn)行的時(shí)候根據(jù)你要監(jiān)聽的類, 動(dòng)態(tài)添加一個(gè)新類繼承自該類, 然后重寫原類的setter方法并在里面通知observer的.

那么, 如何動(dòng)態(tài)添加一個(gè)類呢? 直接上代碼:

// 創(chuàng)建一個(gè)類(size_t extraBytes該參數(shù)通常指定為0, 該參數(shù)是分配給類和元類對(duì)象尾部的索引ivars的字節(jié)數(shù)。)

Class clazz = objc_allocateClassPair([NSObjectclass],"GoodPerson",0);// 添加ivar// @encode(aType) : 返回該類型的C字符串class_addIvar(clazz,"_name",sizeof(NSString*), log2(sizeof(NSString*)), @encode(NSString*));

class_addIvar(clazz,"_age",sizeof(NSUInteger), log2(sizeof(NSUInteger)), @encode(NSUInteger));// 注冊該類objc_registerClassPair(clazz);// 創(chuàng)建實(shí)例對(duì)象idobject = [[clazz alloc] init];// 設(shè)置ivar[object setValue:@"Tracy"forKey:@"name"];

Ivar ageIvar = class_getInstanceVariable(clazz,"_age");

object_setIvar(object, ageIvar, @18);// 打印對(duì)象的類和內(nèi)存地址NSLog(@"%@", object);// 打印對(duì)象的屬性值NSLog(@"name = %@, age = %@", [object valueForKey:@"name"], object_getIvar(object, ageIvar));// 當(dāng)類或者它的子類的實(shí)例還存在,則不能調(diào)用objc_disposeClassPair方法object =nil;// 銷毀類objc_disposeClassPair(clazz);

運(yùn)行結(jié)果為:

2016-09-04 17:04:08.328 Runtime-實(shí)踐篇[13699:1043458]?

2016-09-04 17:04:08.329 Runtime-實(shí)踐篇[13699:1043458] name = Tracy, age = 18

這樣, 我們就在程序運(yùn)行時(shí)動(dòng)態(tài)添加了一個(gè)繼承自NSObject的GoodPerson類, 并為該類添加了name和age成員變量.

二、通過runtime獲取一個(gè)類的所有屬性,我們可以做些什么

1. 打印一個(gè)類的所有ivar, property 和 method(簡單直接的使用)

Person *p = [[Person alloc] init];

[p setValue:@"Kobe"forKey:@"name"];

[p setValue:@18forKey:@"age"];//? ? p.address = @"廣州大學(xué)城";p.weight=110.0f;// 1.打印所有ivarsunsignedintivarCount =0;// 用一個(gè)字典裝ivarName和valueNSMutableDictionary*ivarDict = [NSMutableDictionarydictionary];

Ivar *ivarList = class_copyIvarList([p class], &ivarCount);for(inti =0; i < ivarCount; i++){NSString*ivarName = [NSStringstringWithUTF8String:ivar_getName(ivarList[i])];idvalue = [p valueForKey:ivarName];if(value) {

ivarDict[ivarName] = value;

}else{

ivarDict[ivarName] = @"值為nil";

}

}// 打印ivarfor(NSString*ivarName in ivarDict.allKeys) {NSLog(@"ivarName:%@, ivarValue:%@",ivarName, ivarDict[ivarName]);

}// 2.打印所有propertiesunsignedintpropertyCount =0;// 用一個(gè)字典裝propertyName和valueNSMutableDictionary*propertyDict = [NSMutableDictionarydictionary];

objc_property_t *propertyList = class_copyPropertyList([p class], &propertyCount);for(intj =0; j < propertyCount; j++){NSString*propertyName = [NSStringstringWithUTF8String:property_getName(propertyList[j])];idvalue = [p valueForKey:propertyName];if(value) {

propertyDict[propertyName] = value;

}else{

propertyDict[propertyName] = @"值為nil";

}

}// 打印propertyfor(NSString*propertyName in propertyDict.allKeys) {NSLog(@"propertyName:%@, propertyValue:%@",propertyName, propertyDict[propertyName]);

}// 3.打印所有methodsunsignedintmethodCount =0;// 用一個(gè)字典裝methodName和argumentsNSMutableDictionary*methodDict = [NSMutableDictionarydictionary];

Method *methodList = class_copyMethodList([p class], &methodCount);for(intk =0; k < methodCount; k++){

SEL methodSel = method_getName(methodList[k]);NSString*methodName = [NSStringstringWithUTF8String:sel_getName(methodSel)];unsignedintargumentNums = method_getNumberOfArguments(methodList[k]);

methodDict[methodName] = @(argumentNums -2);// -2的原因是每個(gè)方法內(nèi)部都有self 和 selector 兩個(gè)參數(shù)}// 打印methodfor(NSString*methodName in methodDict.allKeys) {NSLog(@"methodName:%@, argumentsCount:%@", methodName, methodDict[methodName]);

}

打印結(jié)果為 :

2. 動(dòng)態(tài)變量控制

在程序中,XiaoMing的age是10,后來被runtime變成了20,來看看runtime是怎么做到的:

-(void)changeAge{unsignedintcount =0;//動(dòng)態(tài)獲取XiaoMing類中的所有屬性[當(dāng)然包括私有]Ivar *ivar = class_copyIvarList([self.xiaoMingclass], &count);//遍歷屬性找到對(duì)應(yīng)age字段for(inti =0; i

Ivar var = ivar[i];constchar*varName = ivar_getName(var);NSString*name = [NSStringstringWithUTF8String:varName];if([name isEqualToString:@"_age"]) {//修改對(duì)應(yīng)的字段值成20object_setIvar(self.xiaoMing, var, @"20");break;

}

}NSLog(@"XiaoMing's age is %@",self.xiaoMing.age);

}

3. 在NSObject的分類中增加方法來避免使用KVC賦值的時(shí)候出現(xiàn)崩潰

在有些時(shí)候我們需要通過KVC去修改某個(gè)類的私有變量,但是又不知道該屬性是否存在,如果類中不存在該屬性,那么通過KVC賦值就會(huì)crash,這時(shí)也可以通過運(yùn)行時(shí)進(jìn)行判斷。同樣我們在NSObject的分類中增加如下方法。

/** * 判斷類中是否有該屬性 * *@paramproperty 屬性名稱 * *@return判斷結(jié)果 */-(BOOL)hasProperty:(NSString *)property {

?????BOOL flag = NO;

????u_int count =0;

????Ivar *ivars = class_copyIvarList([self class], &count);for(inti =0; i < count; i++) ? ? ? ? ? ? ?{constchar*propertyName = ivar_getName(ivars[i]);

????NSString *propertyString = [NSString ????????????stringWithUTF8String:propertyName];if([propertyString ?isEqualToString:property]){

flag = YES;

}

}

}

4. 自動(dòng)的歸檔和解檔

博主在學(xué)習(xí) Runtime 之前,歸檔的時(shí)候是醬紫寫的:

-?(void)encodeWithCoder:(NSCoder?*)aCoder{

[aCoder?encodeObject:self.name?forKey:@"name"];

[aCoder?encodeObject:self.ID?forKey:@"ID"];

}

-?(id)initWithCoder:(NSCoder?*)aDecoder{

if(self?=?[superinit])?{

self.ID?=?[aDecoder?decodeObjectForKey:@"ID"];

self.name?=?[aDecoder?decodeObjectForKey:@"name"];

}

returnself;

}

那么問題來了,如果當(dāng)前 Model 有100個(gè)屬性的話,就需要寫100行這種代碼

[aCoder?encodeObject:self.name?forKey:@"name"];

想想都頭疼,通過 Runtime 我們就可以輕松解決這個(gè)問題:

1.使用 class_copyIvarList 方法獲取當(dāng)前 Model 的所有成員變量.

2.使用 ivar_getName 方法獲取成員變量的名稱.

3.通過 KVC 來讀取 Model 的屬性值(encodeWithCoder:),以及給 Model 的屬性賦值(initWithCoder:).

舉個(gè)栗子,新建一個(gè) Model 類,其.m文件如下:

#import?"TestModel.h"

#import?#import?@implementation?TestModel

-?(void)encodeWithCoder:(NSCoder?*)aCoder{

unsigned?int?outCount?=?0;

Ivar?*vars?=?class_copyIvarList([self?class],?&outCount);

for(int?i?=?0;?i?<?outCount;?i?++)?{

Ivarvar=?vars[i];

const?char?*name?=?ivar_getName(var);

NSString?*key?=?[NSString?stringWithUTF8String:name];

//?注意kvc的特性是,如果能找到key這個(gè)屬性的setter方法,則調(diào)用setter方法

//?如果找不到setter方法,則查找成員變量key或者成員變量_key,并且為其賦值

//?所以這里不需要再另外處理成員變量名稱的“_”前綴

id?value?=?[self?valueForKey:key];

[aCoder?encodeObject:value?forKey:key];

}

}

-?(nullable?instancetype)initWithCoder:(NSCoder?*)aDecoder{

if(self?=?[superinit])?{

unsigned?int?outCount?=?0;

Ivar?*vars?=?class_copyIvarList([self?class],?&outCount);

for(int?i?=?0;?i?<?outCount;?i?++)?{

Ivarvar=?vars[i];

const?char?*name?=?ivar_getName(var);

NSString?*key?=?[NSString?stringWithUTF8String:name];

id?value?=?[aDecoder?decodeObjectForKey:key];

[self?setValue:value?forKey:key];

}

}

returnself;

}

@end

完整的自動(dòng)歸檔代碼在這里 :https://github.com/daiweiping/RuntimeLearn

5. 字典轉(zhuǎn)模型

最開始博主是這樣用字典給 Model 賦值的:

-(instancetype)initWithDictionary:(NSDictionary?*)dict{

if(self?=?[superinit])?{

self.age?=?dict[@"age"];

self.name?=?dict[@"name"];

}

returnself;

}

可想而知,遇到的問題跟歸檔時(shí)候一樣(后來使用MJExtension),這里我們稍微來學(xué)習(xí)一下其中原理,字典轉(zhuǎn)模型的時(shí)候:

1.根據(jù)字典的 key 生成 setter 方法

2.使用 objc_msgSend 調(diào)用 setter 方法為 Model 的屬性賦值(或者 KVC)

模型轉(zhuǎn)字典的時(shí)候:

1.調(diào)用 class_copyPropertyList 方法獲取當(dāng)前 Model 的所有屬性

2.調(diào)用 property_getName 獲取屬性名稱

3.根據(jù)屬性名稱生成 getter 方法

4.使用 objc_msgSend 調(diào)用 getter 方法獲取屬性值(或者 KVC)

代碼如下:

#import?"NSObject+KeyValues.h"

#import?#import?@implementation?NSObject?(KeyValues)

//字典轉(zhuǎn)模型

+(id)objectWithKeyValues:(NSDictionary?*)aDictionary{

id?objc?=?[[self?alloc]?init];

for(NSString?*keyinaDictionary.allKeys)?{

id?value?=?aDictionary[key];

/*判斷當(dāng)前屬性是不是Model*/

objc_property_t?property?=?class_getProperty(self,?key.UTF8String);

unsigned?int?outCount?=?0;

objc_property_attribute_t?*attributeList?=?property_copyAttributeList(property,?&outCount);

objc_property_attribute_t?attribute?=?attributeList[0];

NSString?*typeString?=?[NSString?stringWithUTF8String:attribute.value];

if([typeString?isEqualToString:@"@\"TestModel\""])?{

value?=?[self?objectWithKeyValues:value];

}

/**********************/

//生成setter方法,并用objc_msgSend調(diào)用

NSString?*methodName?=?[NSString?stringWithFormat:@"set%@%@:",[key?substringToIndex:1].uppercaseString,[key?substringFromIndex:1]];

SEL?setter?=?sel_registerName(methodName.UTF8String);

if([objc?respondsToSelector:setter])?{

((void?(*)?(id,SEL,id))?objc_msgSend)?(objc,setter,value);

}

}

returnobjc;

}

//模型轉(zhuǎn)字典

-(NSDictionary?*)keyValuesWithObject{

unsigned?int?outCount?=?0;

objc_property_t?*propertyList?=?class_copyPropertyList([self?class],?&outCount);

NSMutableDictionary?*dict?=?[NSMutableDictionary?dictionary];

for(int?i?=?0;?i?<?outCount;?i?++)?{

objc_property_t?property?=?propertyList[i];

//生成getter方法,并用objc_msgSend調(diào)用

const?char?*propertyName?=?property_getName(property);

SEL?getter?=?sel_registerName(propertyName);

if([self?respondsToSelector:getter])?{

id?value?=?((id?(*)?(id,SEL))?objc_msgSend)?(self,getter);

/*判斷當(dāng)前屬性是不是Model*/

if([value?isKindOfClass:[self?class]]?&&?value)?{

value?=?[value?keyValuesWithObject];

}

/**********************/

if(value)?{

NSString?*key?=?[NSString?stringWithUTF8String:propertyName];

[dict?setObject:value?forKey:key];

}

}

}

returndict;

}

@end

完整代碼在這里 ?https://github.com/daiweiping/RuntimeLearn

三、利用runtime的動(dòng)態(tài)交換方法實(shí)現(xiàn),我們可以做什么?

1. 方法簡單的交換

創(chuàng)建一個(gè)Person類,類中實(shí)現(xiàn)以下兩個(gè)類方法,并在.h 文件中聲明

+ (void)run {NSLog(@"跑");

}

+ (void)study {NSLog(@"學(xué)習(xí)");

}

控制器中調(diào)用,則先打印跑,后打印學(xué)習(xí)

[Person run];

[Person study];

下面通過runtime 實(shí)現(xiàn)方法交換,類方法用class_getClassMethod,對(duì)象方法用class_getInstanceMethod

// 獲取兩個(gè)類的類方法

Methodm1=class_getClassMethod([Personclass], @selector(run));

Methodm2=class_getClassMethod([Personclass], @selector(study));

// 開始交換方法實(shí)現(xiàn)method_exchangeImplementations(m1, m2);// 交換后,先打印學(xué)習(xí),再打印跑!

[Person run];

[Person study];

2. 攔截系統(tǒng)方法(Swizzle 黑魔法),也可以說成對(duì)系統(tǒng)的方法進(jìn)行替換

由于某種原因,我們要改變這個(gè)方法的實(shí)現(xiàn),但是又不能去動(dòng)它的源代碼(系統(tǒng)的方法或者一些開源庫出現(xiàn)問題的時(shí)候),這個(gè)時(shí)候runtime就派上用場了。

需求:比如iOS6 升級(jí) iOS7 后需要版本適配,根據(jù)不同系統(tǒng)使用不同樣式圖片(擬物化和扁平化),如何通過不去手動(dòng)一個(gè)個(gè)修改每個(gè)UIImage的imageNamed:方法就可以實(shí)現(xiàn)為該方法中加入版本判斷語句?

步驟:

1、為UIImage建一個(gè)分類(UIImage+Category)

2、在分類中實(shí)現(xiàn)一個(gè)自定義方法,方法中寫要在系統(tǒng)方法中加入的語句,比如版本判斷

+ (UIImage*)xh_imageNamed:(NSString*)name {doubleversion = [[UIDevice currentDevice].systemVersiondoubleValue];if(version >=7.0) {// 如果系統(tǒng)版本是7.0以上,使用另外一套文件名結(jié)尾是‘_os7’的扁平化圖片name = [name stringByAppendingString:@"_os7"];

}return[UIImagexh_imageNamed:name];

}

3、分類中重寫UIImage的load方法,實(shí)現(xiàn)方法的交換(只要能讓其執(zhí)行一次方法交換語句,load再合適不過了)

+ (void)load {// 獲取兩個(gè)類的類方法Method m1 = class_getClassMethod([UIImageclass],@selector(imageNamed:));

Method m2 = class_getClassMethod([UIImageclass],@selector(xh_imageNamed:));// 開始交換方法實(shí)現(xiàn)method_exchangeImplementations(m1, m2);

}

注意:自定義方法中最后一定要再調(diào)用一下系統(tǒng)的方法,讓其有加載圖片的功能,但是由于方法交換,系統(tǒng)的方法名已經(jīng)變成了我們自定義的方法名(有點(diǎn)繞,就是用我們的名字能調(diào)用系統(tǒng)的方法,用系統(tǒng)的名字能調(diào)用我們的方法),這就實(shí)現(xiàn)了系統(tǒng)方法的攔截!

利用以上思路,我們還可以給 NSObject 添加分類,統(tǒng)計(jì)創(chuàng)建了多少個(gè)對(duì)象,給控制器添加分類,統(tǒng)計(jì)有創(chuàng)建了多少個(gè)控制器,特別是公司需求總變的時(shí)候,在一些原有控件或模塊上添加一個(gè)功能,建議使用該方法!

交換原理:

交換之前:


交換之后:


3. 運(yùn)行時(shí)實(shí)現(xiàn)多繼承的效果

既然方法我們可以攔截,可以交換,那么實(shí)現(xiàn)多繼承的效果就留給讀者自己思考了(避免篇幅太長,后續(xù)在博客中再來探討這個(gè)問題)

四、動(dòng)態(tài)添加方法

開發(fā)使用場景:如果一個(gè)類方法非常多,加載類到內(nèi)存的時(shí)候也比較耗費(fèi)資源,需要給每個(gè)方法生成映射表,可以使用動(dòng)態(tài)給某個(gè)類,添加方法解決。

經(jīng)典面試題:有沒有使用performSelector,其實(shí)主要想問你有沒有動(dòng)態(tài)添加過方法。

簡單使用:

@implementationViewController- (void)viewDidLoad {

[superviewDidLoad];// Do any additional setup after loading the view, typically from a nib.Person *p = [[Person alloc] init];// 默認(rèn)person,沒有實(shí)現(xiàn)eat方法,可以通過performSelector調(diào)用,但是會(huì)報(bào)錯(cuò)。// 動(dòng)態(tài)添加方法就不會(huì)報(bào)錯(cuò)[p performSelector:@selector(eat)];

}@end@implementationPerson// void(*)()// 默認(rèn)方法都有兩個(gè)隱式參數(shù),voideat(idself,SEL sel)

{NSLog(@"%@ %@",self,NSStringFromSelector(sel));

}// 當(dāng)一個(gè)對(duì)象調(diào)用未實(shí)現(xiàn)的方法,會(huì)調(diào)用這個(gè)方法處理,并且會(huì)把對(duì)應(yīng)的方法列表傳過來.// 剛好可以用來判斷,未實(shí)現(xiàn)的方法是不是我們想要?jiǎng)討B(tài)添加的方法+ (BOOL)resolveInstanceMethod:(SEL)sel

{if(sel ==@selector(eat)) {// 動(dòng)態(tài)添加eat方法// 第一個(gè)參數(shù):給哪個(gè)類添加方法// 第二個(gè)參數(shù):添加方法的方法編號(hào)// 第三個(gè)參數(shù):添加方法的函數(shù)實(shí)現(xiàn)(函數(shù)地址)// 第四個(gè)參數(shù):函數(shù)的類型,(返回值+參數(shù)類型) v:void @:對(duì)象->self :表示SEL->_cmdclass_addMethod(self,@selector(eat), eat,"v@:");

}return[superresolveInstanceMethod:sel];

}@end

五、利用運(yùn)行時(shí)set和get這兩個(gè)API,可以讓類別可以添加屬性

步驟:

1、創(chuàng)建一個(gè)類別,比如給任何一個(gè)對(duì)象都添加一個(gè)name屬性,就是NSObject添加分類(NSObject+Category)

2、先在.h 中@property 聲明出get 和 set 方法,方便點(diǎn)語法調(diào)用

? ? ?@property(nonatomic,copy)NSString *name;

3、在.m 中重寫set 和 get 方法,內(nèi)部利用runtime 給屬性賦值和取值

charnameKey;

- (void)setName:(NSString*)name {// 將某個(gè)值跟某個(gè)對(duì)象關(guān)聯(lián)起來,將某個(gè)值存儲(chǔ)到某個(gè)對(duì)象中objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);

}

- (NSString*)name {returnobjc_getAssociatedObject(self, &nameKey);

}

六、萬能界面跳轉(zhuǎn)(使用了runtime的N多個(gè)方法)

(特別是根據(jù)推送內(nèi)容跳不同界面) http://www.lxweimin.com/writer#/notebooks/16974385/notes/20278179

?http://blog.csdn.net/coyote1994/article/details/52472670

七、插件開發(fā)

插件入門

XCode 有個(gè)很坑爹的地方,就是它并不官方支持插件開發(fā),官方?jīng)]有文檔,XCode 也沒有開源,但由于 XCode 是 Objective-C 寫的,OC 動(dòng)態(tài)性太強(qiáng)大,導(dǎo)致在這么封閉的情況下民間還是可以做出各種插件,其核心開發(fā)方式就是:

dump 出 Xcode 所有頭文件,知道 Xcode 里有哪些類和接口。

通過頭文件方法名猜測方法的作用,swizzle 這些方法,插入自己的代碼實(shí)現(xiàn)插件邏輯。

通過 NSNotificationCenter 監(jiān)聽各種事件的發(fā)生。

更詳細(xì)的開發(fā)教程網(wǎng)上有不少文章,有興趣的自行搜索吧。

八、Jspath 熱更新 也是使用運(yùn)行時(shí),jspatch 基本上算是黑科技,在線修復(fù)版本bug,微信都使用了這個(gè)技術(shù),詳情百度“JSPatch”

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

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,751評(píng)論 0 9
  • Objective-C中有兩個(gè)NSObject,一個(gè)是NSObject類,另一個(gè)是NSObject協(xié)議。而其中NS...
    ScaryMonsterLyn閱讀 769評(píng)論 0 2
  • 我們常常會(huì)聽說 Objective-C 是一門動(dòng)態(tài)語言,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,225評(píng)論 0 7
  • 對(duì)于從事 iOS 開發(fā)人員來說,所有的人都會(huì)答出【runtime 是運(yùn)行時(shí)】什么情況下用runtime?大部分人能...
    夢夜繁星閱讀 3,730評(píng)論 7 64
  • 6月底開始的一場旅行,至今仍未結(jié)束。 美國的公路旅行雖然辛苦,但收獲了久違的內(nèi)心的放松,這是我記憶中美國的味道吧。...
    Union街閱讀 176評(píng)論 0 0