使用NSSecureCoding協(xié)議進(jìn)行對(duì)象編解碼(轉(zhuǎn))

http://blog.jobbole.com/67655/
NSCoding是把數(shù)據(jù)存儲(chǔ)在iOS和Mac

OS上的一種極其簡(jiǎn)單和方便的方式耗美,它把模型對(duì)象直接轉(zhuǎn)變成一個(gè)文件商架,然后再把這個(gè)文件重新加載到內(nèi)存里,并不需要任何文件解析和序列化的邏輯弃鸦。如果要把對(duì)象保存到一個(gè)數(shù)據(jù)文件中(假設(shè)這個(gè)對(duì)象實(shí)現(xiàn)了NSCoding協(xié)議),那么你可以像下面這樣做:

C++

Foo *someFoo = [[Foo alloc] init];

[NSKeyedArchiver archiveRootObject:someFoo toFile:someFile];

1

2Foo*someFoo=[[Fooalloc]init];

[NSKeyedArchiverarchiveRootObject:someFootoFile:someFile];

稍后再加載它:

C++

Foo *someFoo = [NSKeyedUnarchiver unarchiveObjectWithFile:someFile];

1

Foo*someFoo=[NSKeyedUnarchiverunarchiveObjectWithFile:someFile];

這樣做對(duì)于編譯進(jìn)APP里的資源來說是可以的(例如nib文件颜说,它在底層使用了NSCoding)髓梅,但是使用NSCoding來讀寫用戶數(shù)據(jù)文件的問題在于酝锅,把全部的類編碼到一個(gè)文件里,也就間接地給了這個(gè)文件訪問你APP里面實(shí)例類的權(quán)限场绿。

雖然你不能在一個(gè)NSCoded文件里(至少在iOS中的)存儲(chǔ)可執(zhí)行代碼澎粟,但是一名黑客可以使用特制地文件騙過你的APP進(jìn)入到實(shí)例化類中啸盏,這是你從沒打算做的,或者是你想要在另一個(gè)不同的上下文時(shí)才做的骑祟。盡管以這種方式造成實(shí)際性的破壞很難回懦,但是無疑會(huì)導(dǎo)致用戶的APP崩潰掉或者數(shù)據(jù)丟失气笙。

在iOS6中,蘋果引入了一個(gè)新的協(xié)議怯晕,是基于NSCoding的潜圃,叫做NSSecureCoding。NSSecureCoding和NSCoding是一樣的舟茶,除了在解碼時(shí)要同時(shí)指定key和要解碼的對(duì)象的類秉犹,如果要求的類和從文件中解碼出的對(duì)象的類不匹配,NSCoder會(huì)拋出異常稚晚,告訴你數(shù)據(jù)已經(jīng)被篡改了。

大部分支持NSCoding的系統(tǒng)對(duì)象都已經(jīng)升級(jí)到支持NSSecureCoding了型诚,所以能安全地寫有關(guān)歸檔的代碼客燕,你可以確保正在加載的數(shù)據(jù)文件是安全的。實(shí)現(xiàn)的方式如下:

C++

// Set up NSKeyedUnarchiver to use secure coding

NSData *data = [NSData dataWithContentsOfFile:someFile];

NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

[unarchiver setRequiresSecureCoding:YES];

// Decode object

Foo *someFoo = [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];

1

2

3

4

5

6

7// Set up NSKeyedUnarchiver to use secure coding

NSData*data=[NSDatadataWithContentsOfFile:someFile];

NSKeyedUnarchiver*unarchiver=[[NSKeyedUnarchiveralloc]initForReadingWithData:data];

[unarchiversetRequiresSecureCoding:YES];

// Decode object

Foo*someFoo=[unarchiverdecodeObjectForKey:NSKeyedArchiveRootObjectKey];

注意一下狰贯,如果要讓編寫歸檔的代碼是安全的也搓,那么存儲(chǔ)在文件中的每一個(gè)對(duì)象都要實(shí)現(xiàn)NSSecureCoding協(xié)議,否則會(huì)有異常拋出涵紊。如果要告訴框架自定義的類支持NSSecureCoding協(xié)議傍妒,那么你必須在initWithCoder:

method方法中實(shí)現(xiàn)新的解碼邏輯,并且supportsSecureCodin方法要返回YES摸柄。encodeWithCoder:方法沒有變化颤练,因?yàn)榕c安全相關(guān)的事是圍繞加載進(jìn)行的,而不是保存:

C++

@interface Foo : NSObject

@property (nonatomic, strong) NSNumber *property1;

@property (nonatomic, copy) NSArray *property2;

@property (nonatomic, copy) NSString *property3;

@end

@implementation Foo

+ (BOOL)supportsSecureCoding

{

return YES;

}

- (id)initWithCoder:(NSCoder *)coder

{

if ((self = [super init]))

{

// Decode the property values by key, specifying the expected class

_property1 = [coder decodeObjectOfClass:[NSNumber class] forKey:@"property1"];

_property2 = [coder decodeObjectOfClass:[NSArray class] forKey:@"property2"];

_property3 = [coder decodeObjectOfClass:[NSString class] forKey:@"property3"];

}

return self;

}

- (void)encodeWithCoder:(NSCoder *)coder

{

// Encode our ivars using string keys as normal

[coder encodeObject:_property1 forKey:@"property1"];

[coder encodeObject:_property2 forKey:@"property2"];

[coder encodeObject:_property3 forKey:@"property3"];

}

@end

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57@interfaceFoo:NSObject

@property(nonatomic,strong)NSNumber*property1;

@property(nonatomic,copy)NSArray*property2;

@property(nonatomic,copy)NSString*property3;

@end

@implementationFoo

+(BOOL)supportsSecureCoding

{

returnYES;

}

-(id)initWithCoder:(NSCoder*)coder

{

if((self=[superinit]))

{

// Decode the property values by key, specifying the expected class

_property1=[coderdecodeObjectOfClass:[NSNumberclass]forKey:@"property1"];

_property2=[coderdecodeObjectOfClass:[NSArrayclass]forKey:@"property2"];

_property3=[coderdecodeObjectOfClass:[NSStringclass]forKey:@"property3"];

}

returnself;

}

-(void)encodeWithCoder:(NSCoder*)coder

{

// Encode our ivars using string keys as normal

[coderencodeObject:_property1forKey:@"property1"];

[coderencodeObject:_property2forKey:@"property2"];

[coderencodeObject:_property3forKey:@"property3"];

}

@end

幾周前驱负,我寫了一篇關(guān)于如何自動(dòng)實(shí)現(xiàn)NSCoding的文章嗦玖,它利用反射機(jī)制確定運(yùn)行時(shí)類的屬性。

這是一種給所有的模型對(duì)象添加NSCoding支持的很好的方式跃脊,在initWithCoder:/encodeWithCoder:

方法中宇挫,你不再需要寫重復(fù)的并且容易出錯(cuò)的代碼了。但是我們使用的方法沒有支持NSSecureCoding酪术,因?yàn)槲覀儾淮蛩阍趯?duì)象被加載時(shí)校驗(yàn)其類型器瘪。

那么怎么改善這個(gè)自動(dòng)NSCoding系統(tǒng),使其以正確的方式支持NSSecureCoding呢绘雁?

回想一下橡疼,最開始的實(shí)現(xiàn)原理是利用class_copyPropertyList() 和 property_getName()這樣兩個(gè)運(yùn)行時(shí)方法,產(chǎn)生屬性名稱列表咧七,我們?cè)侔阉鼈冊(cè)跀?shù)組中排序:

C++

// Import the Objective-C runtime headers

#import

- (NSArray *)propertyNames

{

// Get the list of properties

unsigned int propertyCount;

objc_property_t *properties = class_copyPropertyList([self class],

&propertyCount);

NSMutableArray *array = [NSMutableArray arrayWithCapacity:propertyCount];

for (int i = 0; i < propertyCount; i++)

{

// Get property name

objc_property_t property = properties[i];

const char *propertyName = property_getName(property);

NSString *key = @(propertyName);

// Add to array

[array addObject:key];

}

// Remember to free the list because ARC doesn't do that for us

free(properties);

return array;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43// Import the Objective-C runtime headers

#import

-(NSArray*)propertyNames

{

// Get the list of properties

unsignedintpropertyCount;

objc_property_t*properties=class_copyPropertyList([selfclass],

&propertyCount);

NSMutableArray*array=[NSMutableArrayarrayWithCapacity:propertyCount];

for(inti=0;i

{

// Get property name

objc_property_tproperty=properties[i];

constchar*propertyName=property_getName(property);

NSString*key=@(propertyName);

// Add to array

[arrayaddObject:key];

}

// Remember to free the list because ARC doesn't do that for us

free(properties);

returnarray;

}

使用KVC(鍵-值編碼)衰齐,我們能夠利用名稱設(shè)置和獲取一個(gè)對(duì)象的所有屬性,并且在一個(gè)NSCoder對(duì)象中對(duì)這些屬性進(jìn)行編碼/解碼继阻。

為了要實(shí)現(xiàn)NSSecureCoding耻涛,我們要遵循同樣的原則废酷,但是不僅僅是獲取屬性名,還需要獲取它們的類型抹缕。幸運(yùn)地是澈蟆,Objective C運(yùn)行時(shí)存儲(chǔ)了類的屬性類型的詳細(xì)信息,所以可以很容易和名字一起取到這些數(shù)據(jù)卓研。

一個(gè)類的屬性可以是基本數(shù)據(jù)類型(例如整型趴俘、布爾類型和結(jié)構(gòu)體),或者對(duì)象(例如字符串奏赘、數(shù)組等等)寥闪。KVC中的valueForKey: and

setValue:forKey:方法實(shí)現(xiàn)了對(duì)基本類型的自動(dòng)“裝箱”,也就是說它們會(huì)把整型磨淌、布爾型和結(jié)構(gòu)體各自轉(zhuǎn)變成NSNumber和NSValue對(duì)象疲憋。這使事情變得簡(jiǎn)單了很多,因?yàn)槲覀冎灰幚硌b箱過的類型(對(duì)象)即可梁只,所以我們可以聲明屬性類型為類缚柳,而不用為不同的屬性類型調(diào)用不同的解碼方法。

盡管運(yùn)行時(shí)方法沒有提供已裝箱的類名搪锣,但是它們提供了類型編碼—一種特殊格式化的C風(fēng)格的字符串秋忙,它包含了類型信息(與@encode(var);返回的形式一樣)。因?yàn)闆]有方法自動(dòng)獲取到基本類型對(duì)應(yīng)的裝箱過的類构舟,所以我們需要解析這個(gè)字符串灰追,然后指定其合適的類型。

類型編碼字符串形式的文檔說明在這里狗超。

第一個(gè)字母代表了基本類型监嗜。Objective

C使用一個(gè)唯一的字母表示每一個(gè)支持的基本類型,例如’i’表示integer抡谐,’f’表示float裁奇,’d’表示double,等等麦撵。對(duì)象用’@’表示(緊跟著的是類名)刽肠,還有其他一些不常見的類型,例如’:’表示selectors免胃,’#’表示類音五。

結(jié)構(gòu)體和聯(lián)合體表示為大括號(hào)里面的表達(dá)式。只有幾種類型是KVC機(jī)制所支持的羔沙,但是支持的那些類通常被裝箱為NSValue對(duì)象躺涝,所以可用一種方式處理以’{’開頭的任何值。

如果根據(jù)字符串的首字母來轉(zhuǎn)換扼雏,那么我們可以處理所有已知的類型:

C++

Class propertyClass = nil;

char *typeEncoding = property_copyAttributeValue(property, "T");

switch (typeEncoding[0])

{

case 'c': // Numeric types

case 'i':

case 's':

case 'l':

case 'q':

case 'C':

case 'I':

case 'S':

case 'L':

case 'Q':

case 'f':

case 'd':

case 'B':

{

propertyClass = [NSNumber class];

break;

}

case '*': // C-String

{

propertyClass = [NSString class];

break;

}

case '@': // Object

{

//TODO: get class name

break;

}

case '{': // Struct

{

propertyClass = [NSValue class];

break;

}

case '[': // C-Array

case '(': // Enum

case '#': // Class

case ':': // Selector

case '^': // Pointer

case 'b': // Bitfield

case '?': // Unknown type

default:

{

propertyClass = nil; // Not supported by KVC

break;

}

}

free(typeEncoding);

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99ClasspropertyClass=nil;

char*typeEncoding=property_copyAttributeValue(property,"T");

switch(typeEncoding[0])

{

case'c':// Numeric types

case'i':

case's':

case'l':

case'q':

case'C':

case'I':

case'S':

case'L':

case'Q':

case'f':

case'd':

case'B':

{

propertyClass=[NSNumberclass];

break;

}

case'*':// C-String

{

propertyClass=[NSStringclass];

break;

}

case'@':// Object

{

//TODO: get class name

break;

}

case'{':// Struct

{

propertyClass=[NSValueclass];

break;

}

case'[':// C-Array

case'(':// Enum

case'#':// Class

case':':// Selector

case'^':// Pointer

case'b':// Bitfield

case'?':// Unknown type

default:

{

propertyClass=nil;// Not supported by KVC

break;

}

}

free(typeEncoding);

如果要處理’@’類型坚嗜,則需要提去出類名夯膀。類名可能包括協(xié)議(實(shí)際上我們并不需要用到),所以劃分字符串拿準(zhǔn)確的類名苍蔬,然后使用NSClassFromString得到類:

C++

case '@':

{

// The objcType for classes will always be at least 3 characters long

if (strlen(typeEncoding) >= 3)

{

// Copy the class name as a C-String

char *cName = strndup(typeEncoding + 2, strlen(typeEncoding) - 3);

// Convert to an NSString for easier manipulation

NSString *name = @(cName);

// Strip out and protocols from the end of the class name

NSRange range = [name rangeOfString:@"<"];

if (range.location != NSNotFound)

{

name = [name substringToIndex:range.location];

}

// Get class from name, or default to NSObject if no name is found

propertyClass = NSClassFromString(name) ?: [NSObject class];

free(cName);

}

break;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40case'@':

{

// The objcType for classes will always be at least 3 characters long

if(strlen(typeEncoding)>=3)

{

// Copy the class name as a C-String

char*cName=strndup(typeEncoding+2,strlen(typeEncoding)-3);

// Convert to an NSString for easier manipulation

NSString*name=@(cName);

// Strip out and protocols from the end of the class name

NSRangerange=[namerangeOfString:@"<"];

if(range.location!=NSNotFound)

{

name=[namesubstringToIndex:range.location];

}

// Get class from name, or default to NSObject if no name is found

propertyClass=NSClassFromString(name)?:[NSObjectclass];

free(cName);

}

break;

}

最后诱建,把上面的解析過程和前面實(shí)現(xiàn)的propertyNames方法結(jié)合起來,創(chuàng)建一個(gè)方法返回屬性類的字典碟绑,屬性名稱作為字典的鍵俺猿。下面是完成的實(shí)現(xiàn)過程:

- (NSDictionary *)propertyClassesByName

{

// Check for a cached value (we use _cmd as the cache key,

// which represents @selector(propertyNames))

NSMutableDictionary *dictionary = objc_getAssociatedObject([self class], _cmd);

if (dictionary)

{

return dictionary;

}

// Loop through our superclasses until we hit NSObject

dictionary = [NSMutableDictionary dictionary];

Class subclass = [self class];

while (subclass != [NSObject class])

{

unsigned int propertyCount;

objc_property_t *properties = class_copyPropertyList(subclass,

&propertyCount);

for (int i = 0; i < propertyCount; i++)

{

// Get property name

objc_property_t property = properties[i];

const char *propertyName = property_getName(property);

NSString *key = @(propertyName);

// Check if there is a backing ivar

char *ivar = property_copyAttributeValue(property, "V");

if (ivar)

{

// Check if ivar has KVC-compliant name

NSString *ivarName = @(ivar);

if ([ivarName isEqualToString:key] ||

[ivarName isEqualToString:[@"_" stringByAppendingString:key]])

{

// Get type

Class propertyClass = nil;

char *typeEncoding = property_copyAttributeValue(property, "T");

switch (typeEncoding[0])

{

case 'c': // Numeric types

case 'i':

case 's':

case 'l':

case 'q':

case 'C':

case 'I':

case 'S':

case 'L':

case 'Q':

case 'f':

case 'd':

case 'B':

{

propertyClass = [NSNumber class];

break;

}

case '*': // C-String

{

propertyClass = [NSString class];

break;

}

case '@': // Object

{

//TODO: get class name

break;

}

case '{': // Struct

{

propertyClass = [NSValue class];

break;

}

case '[': // C-Array

case '(': // Enum

case '#': // Class

case ':': // Selector

case '^': // Pointer

case 'b': // Bitfield

case '?': // Unknown type

default:

{

propertyClass = nil; // Not supported by KVC

break;

}

}

free(typeEncoding);

// If known type, add to dictionary

if (propertyClass) dictionary[propertyName] = propertyClass;

}

free(ivar);

}

}

free(properties);

subclass = [subclass superclass];

}

// Cache and return dictionary

objc_setAssociatedObject([self class], _cmd, dictionary,

OBJC_ASSOCIATION_RETAIN_NONATOMIC);

return dictionary;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193-(NSDictionary *)propertyClassesByName

{

// Check for a cached value (we use _cmd as the cache key,

// which represents @selector(propertyNames))

NSMutableDictionary *dictionary=objc_getAssociatedObject([selfclass],_cmd);

if(dictionary)

{

returndictionary;

}

// Loop through our superclasses until we hit NSObject

dictionary=[NSMutableDictionarydictionary];

Classsubclass=[selfclass];

while(subclass!=[NSObjectclass])

{

unsignedintpropertyCount;

objc_property_t *properties=class_copyPropertyList(subclass,

&propertyCount);

for(inti=0;i<propertyCount;i++)

{

// Get property name

objc_property_tproperty=properties[i];

constchar*propertyName=property_getName(property);

NSString *key=@(propertyName);

// Check if there is a backing ivar

char*ivar=property_copyAttributeValue(property,"V");

if(ivar)

{

// Check if ivar has KVC-compliant name

NSString *ivarName=@(ivar);

if([ivarNameisEqualToString:key]||

[ivarNameisEqualToString:[@"_"stringByAppendingString:key]])

{

// Get type

ClasspropertyClass=nil;

char*typeEncoding=property_copyAttributeValue(property,"T");

switch(typeEncoding[0])

{

case'c': // Numeric types

case'i':

case's':

case'l':

case'q':

case'C':

case'I':

case'S':

case'L':

case'Q':

case'f':

case'd':

case'B':

{

propertyClass=[NSNumberclass];

break;

}

case'*': // C-String

{

propertyClass=[NSStringclass];

break;

}

case'@': // Object

{

//TODO: get class name

break;

}

case'{': // Struct

{

propertyClass=[NSValueclass];

break;

}

case'[': // C-Array

case'(': // Enum

case'#': // Class

case':': // Selector

case'^': // Pointer

case'b': // Bitfield

case'?': // Unknown type

default:

{

propertyClass=nil;// Not supported by KVC

break;

}

}

free(typeEncoding);

// If known type, add to dictionary

if(propertyClass)dictionary[propertyName]=propertyClass;

}

free(ivar);

}

}

free(properties);

subclass=[subclasssuperclass];

}

// Cache and return dictionary

objc_setAssociatedObject([selfclass],_cmd,dictionary,

OBJC_ASSOCIATION_RETAIN_NONATOMIC);

returndictionary;

}

最難的部分已經(jīng)完成了。現(xiàn)在格仲,要實(shí)現(xiàn)NSSecureCoding押袍,只要將initWithCoder:方法中之前寫的自動(dòng)編碼實(shí)現(xiàn)的部分,改為在解析時(shí)考慮到屬性的類就可以了凯肋。此外伯病,還需讓supportsSecureCoding方法返回YES:

C++

+ (BOOL)supportsSecureCoding

{

return YES;

}

- (id)initWithCoder:(NSCoder *)coder

{

if ((self = [super init]))

{

// Decode the property values by key, specifying the expected class

[[self propertyClassesByName] enumerateKeysAndObjectsUsingBlock:(void (^)(NSString *key, Class propertyClass, BOOL *stop)) {

id object = [aDecoder decodeObjectOfClass:propertyClass forKey:key];

if (object) [self setValue:object forKey:key];

}];

}

return self;

}

- (void)encodeWithCoder:(NSCoder *)aCoder

{

for (NSString *key in [self propertyClassesByName])

{

id object = [self valueForKey:key];

if (object) [aCoder encodeObject:object forKey:key];

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47+(BOOL)supportsSecureCoding

{

returnYES;

}

-(id)initWithCoder:(NSCoder*)coder

{

if((self=[superinit]))

{

// Decode the property values by key, specifying the expected class

[[selfpropertyClassesByName]enumerateKeysAndObjectsUsingBlock:(void(^)(NSString*key,ClasspropertyClass,BOOL*stop)){

idobject=[aDecoderdecodeObjectOfClass:propertyClassforKey:key];

if(object)[selfsetValue:objectforKey:key];

}];

}

returnself;

}

-(void)encodeWithCoder:(NSCoder*)aCoder

{

for(NSString*keyin[selfpropertyClassesByName])

{

idobject=[selfvalueForKey:key];

if(object)[aCoderencodeObject:objectforKey:key];

}

}

這樣就得到了一個(gè)用于描述模型對(duì)象的簡(jiǎn)單的基類,并且它以正確的方式支持NSSecureCoding否过。此外,你可以使用我的AutoCoding擴(kuò)展惭蟋,它利用這種方法自動(dòng)給沒有實(shí)現(xiàn)NSCoding 和 NSSecureCoding協(xié)議的對(duì)象添加對(duì)它們的支持苗桂。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市告组,隨后出現(xiàn)的幾起案子煤伟,更是在濱河造成了極大的恐慌,老刑警劉巖木缝,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件便锨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡我碟,警方通過查閱死者的電腦和手機(jī)放案,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來矫俺,“玉大人吱殉,你說我怎么就攤上這事±逋校” “怎么了友雳?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铅匹。 經(jīng)常有香客問我押赊,道長,這世上最難降的妖魔是什么包斑? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任流礁,我火速辦了婚禮涕俗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘崇棠。我一直安慰自己咽袜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布枕稀。 她就那樣靜靜地躺著询刹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪萎坷。 梳的紋絲不亂的頭發(fā)上凹联,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音哆档,去河邊找鬼蔽挠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瓜浸,可吹牛的內(nèi)容都是我干的澳淑。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼插佛,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼杠巡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起雇寇,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤氢拥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后锨侯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嫩海,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年囚痴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叁怪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡深滚,死狀恐怖骂束,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情成箫,我是刑警寧澤展箱,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站蹬昌,受9級(jí)特大地震影響混驰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一栖榨、第九天 我趴在偏房一處隱蔽的房頂上張望昆汹。 院中可真熱鬧,春花似錦婴栽、人聲如沸满粗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽映皆。三九已至,卻和暖如春轰枝,著一層夾襖步出監(jiān)牢的瞬間捅彻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國打工鞍陨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留步淹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓诚撵,卻偏偏與公主長得像缭裆,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子寿烟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

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