YYModel源碼分析

前言

YYModel 是一個iOS JSON模型轉(zhuǎn)化庫及老,和其他一些同類型庫相比慢味,具有比較好的性能優(yōu)勢骄呼。本文會對YYModel的源碼進(jìn)行分析,具體用法作者ibireme在github中有提及攘残。YYModel的目錄結(jié)構(gòu)很簡單,只有兩個類, NSObject+YYModelYYClassInfo对竣。YYClassInfo主要對根類NSObject 的 Ivar , Method, Property以及Class本身進(jìn)行了封裝难衰,NSObject+YYModel 是 NSObject的分類,擴(kuò)展了一些JSON模型轉(zhuǎn)化的方法丛忆。下圖一張YYmodel的整體結(jié)構(gòu)圖:

YYModel 結(jié)構(gòu)圖.png

YYClassInfo

要點(diǎn)

YYClassIvarInfo : 對 Class的Ivar進(jìn)行了封裝
YYClassMethodInfo : 對 Class的Method進(jìn)行了封裝
YYClassPropertyInfo : 對 Class的Property進(jìn)行了封裝
YYClassInfo : 對Class進(jìn)行了封裝祠汇,包含了YYClassIvarInfo,YYClassMethodInfo熄诡,YYClassPropertyInfo

變量類型編碼

說到變量可很,可能我們寫代碼中最常見的就是變量了。當(dāng)我們自定義一個類時凰浮,首先總是會申明一些屬性我抠,而且每個屬性都會帶有一些修飾詞。比如是否原子性袜茧,內(nèi)存管理原則菜拓,只讀性。這些都可以通過這個屬性的property_getAttributes 方法獲取笛厦,蘋果為所有的類型纳鼎,包括屬性類型都有編碼,具體可以查看蘋果官方文檔:類型編碼, 蘋果官方文檔:屬性類型裳凸。 下面是一個簡單例子:

  1. 申明屬性
@interface Cat : NSObject
@end

@interface Person : NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) NSInteger age;
@property (nonatomic,strong) Cat *cat;
- (NSDictionary *)getProperties;
@end

2.通過runtime 來獲取所有屬性

#import "Person.h"
#import <objc/runtime.h>
@implementation Person
- (NSDictionary *) getProperties{
    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    unsigned int count = 0;
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (NSUInteger  i = 0; i < count; i++) {
        const char *name = property_getName(propertyList[i]);
        const char *attribute = property_getAttributes(propertyList[i]);
        NSString *nameStr = [NSString stringWithUTF8String:name];
        NSString *attributeStr = [NSString stringWithUTF8String:attribute];
        dic[nameStr] = attributeStr;
    }
    return [dic copy];
}

@end

3.打印結(jié)果

2017-01-03 10:54:34.690 Properties[16941:881462] {
    age = "Tq,N,V_age";
    cat = "T@\"Cat\",&,N,V_cat";
    name = "T@\"NSString\",C,N,V_name";
}

我們可以看到屬性的所有類型編碼信息贱鄙,其中第一個代表是這個變量的類型,以T開頭姨谷,最后一個代表的是變量的名字逗宁,一般用V_屬性名表示,中間的部分就是我們聲明的修飾符梦湘。比如age的類型是 Tq瞎颗,而在官方文檔中q 代表了A long long,64bit下NSInteger的取值范圍就是long == long long 件甥,N代表了非原子性,變量名是_age言缤。其他的@代表了OC類型 id ,cat類型即是T@"Cat"嚼蚀,&代表了 這個變量是retain (ARC下strong相當(dāng)于retain),C 代表了copy

YYEncodingType

根據(jù)類型編碼自定義了類型枚舉管挟,包含了三個部分

YYEncodingTypeMask : 0~8位的值轿曙,變量的數(shù)據(jù)類型
YYEncodingTypeQualifierMask : 8~16位的值,變量的方法類型
YYEncodingTypePropertyMask: 16~24位的值僻孝,變量的屬性類型

這里把枚舉值分成三個部分导帝,通過 枚舉值 & 對應(yīng) Mask 取出對應(yīng)的變量類型,區(qū)分不同類型部分穿铆。YYEncodingGetType 是根據(jù)變量的數(shù)據(jù)類型編碼值獲取自定義YYEncodingType

Var Method Property

YYClassIvarInfo:用于存取變量的信息

/**
 Instance variable information.
 */
@interface YYClassIvarInfo : NSObject
@property (nonatomic, assign, readonly) Ivar ivar;              ///< ivar opaque struct
@property (nonatomic, strong, readonly) NSString *name;         ///< Ivar's name
@property (nonatomic, assign, readonly) ptrdiff_t offset;       ///< Ivar's offset
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding
@property (nonatomic, assign, readonly) YYEncodingType type;    ///< Ivar's type

/**
 Creates and returns an ivar info object.
 
 @param ivar ivar opaque struct
 @return A new object, or nil if an error occurs.
 */
- (instancetype)initWithIvar:(Ivar)ivar;
@end

Ivar是表示實(shí)例變量的類型您单,其實(shí)際是一個指向objc_ivar結(jié)構(gòu)體的指針,其定義如下:

struct objc_ivar {  
    charchar *ivar_name                              OBJC2_UNAVAILABLE;  
    charchar *ivar_type                              OBJC2_UNAVAILABLE;  
    int ivar_offset                                  OBJC2_UNAVAILABLE;  
#ifdef __LP64__  
    int space                                        OBJC2_UNAVAILABLE;  
#endif  
}  

運(yùn)用runtime,name通過的ivar_getName獲取荞雏,offset 通過ivar_getOffset獲取虐秦,typeEncoding 通過 ivar_getTypeEncoding 獲取,type 通過自定義方法 YYEncodingGetType獲取凤优。其中offset是變量的基地址偏移量悦陋,可以通過它來直接訪問變量數(shù)據(jù),下面是例子:

    Person *p = [[Person alloc]init];
//    NSLog(@"%@",[p getProperties]);
    p.age = 20;
    Ivar age_ivar = class_getInstanceVariable([Person class], "_age");
    long *age_pointer = (long *)((__bridge void  *)(p) + ivar_getOffset(age_ivar));
    NSLog(@"age ivar offset = %td", ivar_getOffset(age_ivar));
    *age_pointer = 10;
    NSLog(@"%@", p);
- (NSString *)description {
    NSLog(@"current pointer = %p", self);
    NSLog(@"age pointer = %p", &_age);
    return [NSString stringWithFormat:@"age = %zi", _age];
}

打印結(jié)果:

2017-01-03 14:41:14.175 Properties[20934:1554728] age ivar offset = 16
2017-01-03 14:41:14.176 Properties[20934:1554728] current pointer = 0x600000075140
2017-01-03 14:41:14.176 Properties[20934:1554728] age pointer = 0x600000075150
2017-01-03 14:41:14.177 Properties[20934:1554728] age = 10

YYClassMethodInfo:用于存取方法的信息

/**
 Method information.
 */
@interface YYClassMethodInfo : NSObject
@property (nonatomic, assign, readonly) Method method;                  ///< method opaque struct
@property (nonatomic, strong, readonly) NSString *name;                 ///< method name
@property (nonatomic, assign, readonly) SEL sel;                        ///< method's selector
@property (nonatomic, assign, readonly) IMP imp;                        ///< method's implementation
@property (nonatomic, strong, readonly) NSString *typeEncoding;         ///< method's parameter and return types
@property (nonatomic, strong, readonly) NSString *returnTypeEncoding;   ///< return value's type
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *argumentTypeEncodings; ///< array of arguments' type

/**
 Creates and returns a method info object.
 
 @param method method opaque struct
 @return A new object, or nil if an error occurs.
 */
- (instancetype)initWithMethod:(Method)method;
@end

Method 的信息同 Ivar 一樣,通過runtime的 method相關(guān)方法獲取筑辨,其他的一些信息同Ivar俺驶,主要來說一下 SELImp

SEL: 方法ID,C字符串

typedef struct objc_selector *SEL;

/// Defines a method
struct objc_method_description {
    SEL name;               /**< The name of the method */
    char *types;            /**< The types of the method arguments */
};

IMP:方法函數(shù)指針

OC是動態(tài)語言棍辕,方法調(diào)用(也叫做消息發(fā)送)是在運(yùn)行時動態(tài)綁定的暮现,而非編譯時。如何做到正確的調(diào)用指定的方法呢楚昭?這里就需要用到SEL和IMP栖袋。編譯器會將消息發(fā)送轉(zhuǎn)換成對objc_msgSend(void /* id self, SEL op, ... */ )方法的調(diào)用 。objc_msgSend方法根據(jù)對象的isa指針找到對象的類抚太,通過在類中的調(diào)度表(dispatch table)中查找SEL 獲得 IMP,精確執(zhí)行指定方法栋荸。

YYClassPropertyInfo: 用于存取屬性的信息

/**
 Property information.
 */
@interface YYClassPropertyInfo : NSObject
@property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct
@property (nonatomic, strong, readonly) NSString *name;           ///< property's name
@property (nonatomic, assign, readonly) YYEncodingType type;      ///< property's type
@property (nonatomic, strong, readonly) NSString *typeEncoding;   ///< property's encoding value
@property (nonatomic, strong, readonly) NSString *ivarName;       ///< property's ivar name
@property (nullable, nonatomic, assign, readonly) Class cls;      ///< may be nil
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *protocols; ///< may nil
@property (nonatomic, assign, readonly) SEL getter;               ///< getter (nonnull)
@property (nonatomic, assign, readonly) SEL setter;               ///< setter (nonnull)

/**
 Creates and returns a property info object.
 
 @param property property opaque struct
 @return A new object, or nil if an error occurs.
 */
- (instancetype)initWithProperty:(objc_property_t)property;
@end

YYClassInfo: 對Class的封裝,包含了上面三部分的信息

/**
 Class information for a class.
 */
@interface YYClassInfo : NSObject
@property (nonatomic, assign, readonly) Class cls; ///< class object
@property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object
@property (nullable, nonatomic, assign, readonly) Class metaCls;  ///< class's meta class object
@property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class
@property (nonatomic, strong, readonly) NSString *name; ///< class name
@property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassIvarInfo *> *ivarInfos; ///< ivars
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos; ///< methods
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *propertyInfos; ///< properties

元類

其中有metaCls代表了元類凭舶。那什么是元類呢?下面是一張經(jīng)典的類結(jié)構(gòu)圖

isa.png

在OC中爱沟,每個實(shí)例對象都有一個isa指針帅霜,它指向了對象的class。而這個class也同樣有一個isa指針呼伸,它就是指向了它的元類身冀,其實(shí)類也是一個對象钝尸,所以對象之于類的關(guān)系,就相當(dāng)于類(類對象)之于其元類(類對象的類)的關(guān)系搂根。那元類有什么用呢珍促?我們都知道在OC中調(diào)用方法有實(shí)例方法和類方法。我們調(diào)用實(shí)例方法剩愧,就是通過isa指針找到指定的class猪叙,查找存儲在class中的方法列表執(zhí)行方法,所以元類的作用就是調(diào)用類方法時仁卷,通過查找保存在元類中的類方法執(zhí)行方法的作用穴翩。那為什么不把所有方法都保存在類中,可能這樣更加高效也節(jié)省資源吧锦积,具體可以自己查找資料芒帕。

在YYClassInfo中,有一個_update方法丰介,用來更新類中存儲的信息背蟆。

初始化方法

+ (instancetype)classInfoWithClass:(Class)cls {
    if (!cls) return nil;
    static CFMutableDictionaryRef classCache;
    static CFMutableDictionaryRef metaCache;
    static dispatch_once_t onceToken;
    static dispatch_semaphore_t lock;
    dispatch_once(&onceToken, ^{
        classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        lock = dispatch_semaphore_create(1);
    });
    dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
    YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
    if (info && info->_needUpdate) {
        [info _update];
    }
    dispatch_semaphore_signal(lock);
    if (!info) {
        info = [[YYClassInfo alloc] initWithClass:cls];
        if (info) {
            dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
            CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
            dispatch_semaphore_signal(lock);
        }
    }
    return info;
}

要點(diǎn):

  • CFDictionaryCreateMutable 和 CFDictionarySetValue 不是線程安全的,所以需要創(chuàng)建鎖哮幢,采用 dispatch_semaphore 通過控制信號量實(shí)現(xiàn)了鎖

  • 設(shè)置緩存带膀,如果在緩存中存在class,則直接獲取到對應(yīng)的ivar家浇,method本砰,property,否者創(chuàng)建YYClassInfo實(shí)例對象

NSObject+YYModel:

NSObject+YYModel是YYModel的核心類钢悲,主要部分:

強(qiáng)制內(nèi)聯(lián)C函數(shù):功能函數(shù)
私有類_YYModelPropertyMeta : 管理Model屬性的數(shù)據(jù), 類型, 映射的key点额,keyPath
私有類 _YYModelMeta :管理Model 數(shù)據(jù),類型,存儲 映射key,keypath,_YYModelPropertyMeta
NSObject NSArray NSDictionary (YYModel) : 幾個分類莺琳,YYModel主體功能實(shí)現(xiàn)
YYModel 協(xié)議:擴(kuò)展功能實(shí)現(xiàn)

私有類_YYModelPropertyMeta

_YYModelPropertyMeta是對上面的 YYClassPropertyInfo 的進(jìn)一步封裝还棱。

內(nèi)部實(shí)例變量

/// A property info in object model.
// model property的進(jìn)一步分裝
@interface _YYModelPropertyMeta : NSObject {
    @package
    NSString *_name;              //屬性名
    YYEncodingType _type;         //屬性的編碼類型
    YYEncodingNSType _nsType;     //屬性的Foundation類型
    BOOL _isCNumber;              //是否c語言的數(shù)字
    Class _cls;                   //屬性的class
    Class _genericCls;    //屬性內(nèi)包含的類class
    SEL _getter;             //屬性 getter方法
    SEL _setter;             //屬性 setter方法
    BOOL _isKVCCompatible;     //是否可以使用KVC
    BOOL _isStructAvailableForKeyedArchiver;   //是否struct并且可以歸檔
    BOOL _hasCustomClassFromDictionary;   //是否包含本本地的class轉(zhuǎn)換
    /*
     property->key:     
   _mappedToKey:key   _mappedToKeyPath:nil   _mappedToKeyArray:nil
     property->keyPath:  
   _mappedToKey:keyPath _mappedToKeyPath:keyPath(array)  _mappedToKeyArray:nil
     property->keys:   
   _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath        _mappedToKeyArray:keys(array)
     */
    NSString *_mappedToKey;     //屬性名映射的 key 
    NSArray *_mappedToKeyPath;  //屬性名映射的 keyPath 
    NSArray *_mappedToKeyArray;  //屬性名的映射的key keyPath 數(shù)組 
    YYClassPropertyInfo *_info;  //屬性的YYClassPropertyInfo info
    _YYModelPropertyMeta *_next;  
    //多個屬性名映射到同一個key 時,指向下一個屬性名的YYModelPropertyMeta 指針
}
@end

主要是最后幾個變量惭等。其中_mappedToKey _mappedToKeyPath _mappedToKeyArray 是屬性映射的key珍手,keyPath ,key (keypath) 數(shù)組_mappedToKeyArray 中可以是key和keyPath辞做,實(shí)際取其中第一個映射到的值

一般一個屬性名對應(yīng)一個key值琳要,如果多個屬性名對應(yīng)同一個key,這里就需要next發(fā)揮作用了秤茅。比如

{
    @"name1" : @"name",
    @"name2" : @"name",
    @"name3" : @"name",
}

首先name1最先得到映射稚补,對mapKey進(jìn)行賦值,取得json中的name字段進(jìn)行賦值一系列操作框喳,此時next指針為nil
name2接著進(jìn)行映射课幕,對mapKey進(jìn)行賦值厦坛,接著取得原來json key對應(yīng)的屬性描述對象,將name2的next指針乍惊,指向name1杜秸。
name3接著進(jìn)行映射,對mapKey進(jìn)行賦值润绎,接著取得原來json key對應(yīng)的屬性描述對象撬碟,將name3的next指針,指向name2凡橱。

代碼中的實(shí)現(xiàn)

    propertyMeta->_next = mapper[mappedToKey] ?: nil;
    mapper[mappedToKey] = propertyMeta;

初始化方法

@implementation _YYModelPropertyMeta
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
    //創(chuàng)建并且根據(jù)propertyInfo 進(jìn)行變量賦值
    _YYModelPropertyMeta *meta = [self new];
    meta->_name = propertyInfo.name;
    meta->_type = propertyInfo.type;
    meta->_info = propertyInfo;

    // 屬性為容器類型的時候小作, 映射類型賦值
    meta->_genericCls = generic;

    if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) { 
         // 是否Foundation 類型
        meta->_nsType = YYClassGetNSType(propertyInfo.cls);
    } else { 
        // 是否C數(shù)據(jù)類型
        meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);
    }

    // 屬性為結(jié)構(gòu)體類型
    if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) {
        /*
         It seems that NSKeyedUnarchiver cannot decode NSValue except these structs:
         */
        static NSSet *types = nil;
        static dispatch_once_t onceToken;
        // 單例 創(chuàng)建C結(jié)構(gòu)體類型映射
        dispatch_once(&onceToken, ^{
            NSMutableSet *set = [NSMutableSet new];
            // 32 bit
            [set addObject:@"{CGSize=ff}"];
            [set addObject:@"{CGPoint=ff}"];
            [set addObject:@"{CGRect={CGPoint=ff}{CGSize=ff}}"];
            [set addObject:@"{CGAffineTransform=ffffff}"];
            [set addObject:@"{UIEdgeInsets=ffff}"];
            [set addObject:@"{UIOffset=ff}"];
            // 64 bit
            [set addObject:@"{CGSize=dd}"];
            [set addObject:@"{CGPoint=dd}"];
            [set addObject:@"{CGRect={CGPoint=dd}{CGSize=dd}}"];
            [set addObject:@"{CGAffineTransform=dddddd}"];
            [set addObject:@"{UIEdgeInsets=dddd}"];
            [set addObject:@"{UIOffset=dd}"];
            types = set;
        });

        // 只有上面結(jié)構(gòu)體才能被歸檔
        if ([types containsObject:propertyInfo.typeEncoding]) {
            meta->_isStructAvailableForKeyedArchiver = YES;
        }
    }
    // 設(shè)置class類型
    meta->_cls = propertyInfo.cls;

    // 如果是容器類型
    if (generic) {
        // 從容器class 中讀取
        meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)];
    } else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) {
        // 從class類型中讀取
        meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
    }

    // 設(shè)置 getter 和 setter 方法
    if (propertyInfo.getter) {
        if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) {
            meta->_getter = propertyInfo.getter;
        }
    }
    if (propertyInfo.setter) {
        if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) {
            meta->_setter = propertyInfo.setter;
        }
    }

    /**
     *  只有實(shí)現(xiàn)了getter和setter方法 才能實(shí)現(xiàn)歸檔
     */
    if (meta->_getter && meta->_setter) {
        /*
        類型是否支持 KVC
         */
        switch (meta->_type & YYEncodingTypeMask) {
            case YYEncodingTypeBool:
            case YYEncodingTypeInt8:
            case YYEncodingTypeUInt8:
            case YYEncodingTypeInt16:
            case YYEncodingTypeUInt16:
            case YYEncodingTypeInt32:
            case YYEncodingTypeUInt32:
            case YYEncodingTypeInt64:
            case YYEncodingTypeUInt64:
            case YYEncodingTypeFloat:
            case YYEncodingTypeDouble:
            case YYEncodingTypeObject:
            case YYEncodingTypeClass:
            case YYEncodingTypeBlock:
            case YYEncodingTypeStruct:
            case YYEncodingTypeUnion: {
                meta->_isKVCCompatible = YES;
            } break;
            default: break;
        }
    }
    return meta;
}
@end

私有類_YYModelMeta

_YYModelMeta 是對 YYClassInfo 的再次封裝

內(nèi)部變量


@interface _YYModelMeta : NSObject {
    @package
    YYClassInfo *_classInfo;

    // key: 映射的 json key ,keyPath  value: _YYModelPropertyMeta 
    NSDictionary *_mapper;

    // model所有屬性的PropertyMetas
    NSArray *_allPropertyMetas;

    //model所有映射son keyPath 屬性 的PropertyMetas
    NSArray *_keyPathPropertyMetas;

    // model所有映射多個key 屬性 的PropertyMetas
    NSArray *_multiKeysPropertyMetas;
    /// 需要映射的屬性總個數(shù)
    NSUInteger _keyMappedCount;

    /// Model對應(yīng)的Foundation 類型
    YYEncodingNSType _nsType;

    // 事否實(shí)現(xiàn)了自定義的映射關(guān)系表 
    BOOL _hasCustomWillTransformFromDictionary;
    BOOL _hasCustomTransformFromDictionary;
    BOOL _hasCustomTransformToDictionary;
    BOOL _hasCustomClassFromDictionary;
}
@end

初始化

/// Returns the cached model class meta
+ (instancetype)metaWithClass:(Class)cls {
    if (!cls) return nil;
    static CFMutableDictionaryRef cache;
    static dispatch_once_t onceToken;
    static dispatch_semaphore_t lock;
    dispatch_once(&onceToken, ^{
        cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        lock = dispatch_semaphore_create(1);
    });
    dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
    _YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
    dispatch_semaphore_signal(lock);
    if (!meta || meta->_classInfo.needUpdate) {
        meta = [[_YYModelMeta alloc] initWithClass:cls];
        if (meta) {
            dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
            CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta));
            dispatch_semaphore_signal(lock);
        }
    }
    return meta;
}

首先從緩存中加載,沒有在根據(jù)傳入cls 創(chuàng)建meta稼钩,并做緩存處理顾稀, dispatch_semaphore 確保線程安全

@implementation _YYModelMeta


- (instancetype)initWithClass:(Class)cls {

    YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
    if (!classInfo) return nil;
    self = [super init];

    // 黑名單 會忽略返回?cái)?shù)組里的屬性
    NSSet *blacklist = nil;
    if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {
        NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist];
        if (properties) {
            blacklist = [NSSet setWithArray:properties];
        }
    }

    // 白名單 只考慮返回?cái)?shù)組內(nèi)的屬性 
    NSSet *whitelist = nil;
    if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {
        NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist];
        if (properties) {
            whitelist = [NSSet setWithArray:properties];
        }
    }

    // 獲取容器屬性中的映射關(guān)系字典
    NSDictionary *genericMapper = nil;
   // 判斷類中是否實(shí)現(xiàn)了對應(yīng)的modelContainerPropertyGenericClass方法
    if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {

        genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];
      // 存儲key和對應(yīng)的class到字典中
        if (genericMapper) {
            NSMutableDictionary *tmp = [NSMutableDictionary new];
            [genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
                if (![key isKindOfClass:[NSString class]]) return;
                Class meta = object_getClass(obj);
                if (!meta) return;
                if (class_isMetaClass(meta)) {
                    tmp[key] = obj;
                } else if ([obj isKindOfClass:[NSString class]]) {
                    Class cls = NSClassFromString(obj);
                    if (cls) {
                        tmp[key] = cls;
                    }
                }
            }];
            genericMapper = tmp;
        }
    }

    // 存儲所有屬性的PropertyMeta對象

    NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
    YYClassInfo *curClassInfo = classInfo;
    while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)
        // 遍歷當(dāng)前ClassInfo 中的所有PropertyInfo, 將它們封裝成PropertyMeta
        for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {
            // 檢查是否合法和黑白名單篩選
            if (!propertyInfo.name) continue;
            if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;
            if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue;

            // 通過propetyInfo來創(chuàng)建PropertyMeta 對象
            _YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
                                                                    propertyInfo:propertyInfo
                                                                         generic:genericMapper[propertyInfo.name]];
            // meta name非空
            if (!meta || !meta->_name) continue;
            // 需要實(shí)現(xiàn)get方法和set方法
            if (!meta->_getter || !meta->_setter) continue;
            // 字典中已有該字段的meta 避免重復(fù)操作
            if (allPropertyMetas[meta->_name]) continue;
            allPropertyMetas[meta->_name] = meta;
        }
        // 遍歷父類的property
        curClassInfo = curClassInfo.superClassInfo;
    }
  
    if (allPropertyMetas.count)
  _allPropertyMetas = allPropertyMetas.allValues.copy;

    // 創(chuàng)建 key :propertyMeta 映射關(guān)系字典
    NSMutableDictionary *mapper = [NSMutableDictionary new];
    NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
    NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];

    // 是否實(shí)現(xiàn)自定義的映射表
    if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
        
        NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];

        // 遍歷自定義的字典
        [customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
           
            _YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
            if (!propertyMeta) return;
            // 由于用戶自定義映射坝撑,把原來映射的數(shù)據(jù)刪除
            [allPropertyMetas removeObjectForKey:propertyName];

            if ([mappedToKey isKindOfClass:[NSString class]]) { 
               // key字段非空
                if (mappedToKey.length == 0) return;
                // 保存映射的key
                propertyMeta->_mappedToKey = mappedToKey;

                // 如果是keyPath的情況處理
                NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."];

                if (keyPath.count > 1) {  
                    propertyMeta->_mappedToKeyPath = keyPath;
                    [keyPathPropertyMetas addObject:propertyMeta];
                }

                // 多個屬性映射同一個key的時候静秆,用next存儲前一個 json Key映射的meta
                propertyMeta->_next = mapper[mappedToKey] ?: nil;
                // 保存新的meta對象
                mapper[mappedToKey] = propertyMeta;

            } else if ([mappedToKey isKindOfClass:[NSArray class]]) { 

         // 一個屬性映射多個json Key

                NSMutableArray *mappedToKeyArray = [NSMutableArray new];
                for (NSString *oneKey in ((NSArray *)mappedToKey)) {
                    if (![oneKey isKindOfClass:[NSString class]]) continue;
                    if (oneKey.length == 0) continue;

                    NSArray *keyPath = [oneKey componentsSeparatedByString:@"."];
                    if (keyPath.count > 1) {
                        [mappedToKeyArray addObject:keyPath];
                    } else {
                        [mappedToKeyArray addObject:oneKey];
                    }

                    if (!propertyMeta->_mappedToKey) {
                        propertyMeta->_mappedToKey = oneKey;
                        propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil;
                    }
                }
                if (!propertyMeta->_mappedToKey) return;

                propertyMeta->_mappedToKeyArray = mappedToKeyArray;
                [multiKeysPropertyMetas addObject:propertyMeta];

                propertyMeta->_next = mapper[mappedToKey] ?: nil;
                mapper[mappedToKey] = propertyMeta;
            }
        }];
    }

    // 沒有自定義映射規(guī)則的屬性處理

    [allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
        // 直接讓mappedKey等于屬性名
        propertyMeta->_mappedToKey = name;
        propertyMeta->_next = mapper[name] ?: nil;
        mapper[name] = propertyMeta;
    }];

    // 變量存儲
    if (mapper.count) _mapper = mapper;
    if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
    if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;

    _classInfo = classInfo;
    _keyMappedCount = _allPropertyMetas.count;
    _nsType = YYClassGetNSType(cls);
    _hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
    _hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
    _hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
    _hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
    return self;
}

JSON 轉(zhuǎn) Model

+ (instancetype)yy_modelWithJSON:(id)son {
    NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
    return [self yy_modelWithDictionary:dic];
}

傳入的json可以是 NSDictionary, NSString , NSData_yy_dictionaryWithJSON 統(tǒng)一轉(zhuǎn)化成字典

+ (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary {
    //字典合法性校驗(yàn)
    if (!dictionary || dictionary == (id)kCFNull) return nil;
    if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
    
    Class cls = [self class];
   //創(chuàng)建 model 元數(shù)據(jù)
    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
   //自定義字典
    if (modelMeta->_hasCustomClassFromDictionary) {
        cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
    }
    // 根據(jù)dictionary 進(jìn)行 model 屬性賦值
    NSObject *one = [cls new];
    if ([one yy_modelSetWithDictionary:dictionary]) return one;
    return nil;
}

結(jié)構(gòu)體 ModelSetContext 存儲 modelMeta 巡李,model抚笔, dic 作為 CFDictionaryApplyFunctionCFArrayApplyFunction 的context 參數(shù),傳遞數(shù)據(jù)

- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {

   // 合法性檢驗(yàn)
    if (!dic || dic == (id)kCFNull) return NO;
    if (![dic isKindOfClass:[NSDictionary class]]) return NO;
    

    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
    if (modelMeta->_keyMappedCount == 0) return NO;
    
    if (modelMeta->_hasCustomWillTransformFromDictionary) {
        dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic];
        if (![dic isKindOfClass:[NSDictionary class]]) return NO;
    }
    
    ModelSetContext context = {0};
    context.modelMeta = (__bridge void *)(modelMeta);
    context.model = (__bridge void *)(self);
    context.dictionary = (__bridge void *)(dic);
   
    if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
        //映射的key Count >= dic Count
        CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
        if (modelMeta->_keyPathPropertyMetas) {
            CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
                                 CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
                                 ModelSetWithPropertyMetaArrayFunction,
                                 &context);
        }
        if (modelMeta->_multiKeysPropertyMetas) {
            CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
                                 CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
                                 ModelSetWithPropertyMetaArrayFunction,
                                 &context);
        }
    } else {
         //映射的key Count < dic Count  
        CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
                             CFRangeMake(0, modelMeta->_keyMappedCount),
                             ModelSetWithPropertyMetaArrayFunction,
                             &context);
    }
    
    if (modelMeta->_hasCustomTransformFromDictionary) {
        return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
    }
    return YES;
}

調(diào)用CoreFoundation 的 CFDictionaryApplyFunction 和 CFArrayApplyFunction 回調(diào)自定義的 Apply function
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context)static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context)

根據(jù)獲得的value 侨拦,model 殊橙,_propertyMeta 最后統(tǒng)一調(diào)用 下面方法,運(yùn)用runtime的 objc_msgSend 設(shè)置model屬性

static void ModelSetValueForProperty(__unsafe_unretained id model,
                                     __unsafe_unretained id value,
                                     __unsafe_unretained _YYModelPropertyMeta *meta)

自定義的CFDictionaryApplyFunction 的回調(diào)方法狱从,CoreFoundation中的原回調(diào)函數(shù)
typedef void (*CFDictionaryApplierFunction)(const void *key, const void *value, void *context);

/**
 Apply function for dictionary, to set the key-value pair to model.
 
 @param _key     should not be nil, NSString.
 @param _value   should not be nil.
 @param _context _context.modelMeta and _context.model should not be nil.
 */
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
    ModelSetContext *context = _context;
    __unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);
    __unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];
    __unsafe_unretained id model = (__bridge id)(context->model);
    while (propertyMeta) {
        if (propertyMeta->_setter) {
            ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
        }
        propertyMeta = propertyMeta->_next;
    };
}

自定義的CFArrayApplyFunction 的回調(diào)方法膨蛮,CoreFoundation中的原回調(diào)函數(shù)
typedef void (*CFArrayApplierFunction)(const void *value, void *context);

/**
 Apply function for model property meta, to set dictionary to model.
 
 @param _propertyMeta should not be nil, _YYModelPropertyMeta.
 @param _context      _context.model and _context.dictionary should not be nil.
 */
static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {
    ModelSetContext *context = _context;
    __unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary);
    __unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta);
    if (!propertyMeta->_setter) return;
    id value = nil;
    
    if (propertyMeta->_mappedToKeyArray) {
            //從dic中獲取映射多個key的值,返回映射到的第一個值
        value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
    } else if (propertyMeta->_mappedToKeyPath) {
            //從dic中獲取映射keypath的值
        value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);
    } else {
            //從dic中獲取映射key的值
        value = [dictionary objectForKey:propertyMeta->_mappedToKey];
    }
    
    if (value) {
            //Model 屬性賦值
        __unsafe_unretained id model = (__bridge id)(context->model);
        ModelSetValueForProperty(model, value, propertyMeta);
    }
}

最終實(shí)現(xiàn)model 屬性賦值。 該方法比較長季研,首先對 meta的屬性類型進(jìn)行判斷敞葛,主要分為三類,

  • C基本數(shù)據(jù)類型
  • Foundation 類型
  • 其他類型与涡,如 id惹谐, Class ,block驼卖,SEL等等
    根據(jù)類型獲取對應(yīng)value氨肌,

最后都調(diào)用 ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)value) 進(jìn)行model 屬性賦值

static void ModelSetValueForProperty(__unsafe_unretained id model,
                                     __unsafe_unretained id value,
                                     __unsafe_unretained _YYModelPropertyMeta *meta)

Model 轉(zhuǎn) JSON

其中有效的JSON Object 只能是以下類型:
NSArray,NSDictionary酌畜,NSString儒飒,NSNumber,NSNull檩奠。

1.如果是NSDictionary,NSSet,NSArray 類型桩了,遞歸調(diào)用此方法獲得JSON Object
2.如果是NSURL,NSAttributedString 埠戳,NSDate井誉, NSData,做簡單相應(yīng)返回
3.根據(jù)Model類型創(chuàng)建modelMeta整胃,取實(shí)例變量_mapper 獲取所有屬性名和`propertyMeta,再根據(jù)propertyMeta的 類型_type 獲得相應(yīng)的 value, 根據(jù)有無_mappedToKeyPath再進(jìn)一步處理颗圣,賦值,最后 判斷有無自定義_hasCustomTransformToDictionary屁使,返回最終轉(zhuǎn)化結(jié)果

主要方法:

static id ModelToJSONObjectRecursive(NSObject *model) {
  
    if (!model || model == (id)kCFNull) return model;
    if ([model isKindOfClass:[NSString class]]) return model;
    if ([model isKindOfClass:[NSNumber class]]) return model;
    if ([model isKindOfClass:[NSDictionary class]]) {
        if ([NSJSONSerialization isValidJSONObject:model]) return model;
        NSMutableDictionary *newDic = [NSMutableDictionary new];
        [((NSDictionary *)model) enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
            NSString *stringKey = [key isKindOfClass:[NSString class]] ? key : key.description;
            if (!stringKey) return;
            id jsonObj = ModelToJSONObjectRecursive(obj);
            if (!jsonObj) jsonObj = (id)kCFNull;
            newDic[stringKey] = jsonObj;
        }];
        return newDic;
    }
    if ([model isKindOfClass:[NSSet class]]) {
        NSArray *array = ((NSSet *)model).allObjects;
        if ([NSJSONSerialization isValidJSONObject:array]) return array;
        NSMutableArray *newArray = [NSMutableArray new];
        for (id obj in array) {
            if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
                [newArray addObject:obj];
            } else {
                id jsonObj = ModelToJSONObjectRecursive(obj);
                if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
            }
        }
        return newArray;
    }
    if ([model isKindOfClass:[NSArray class]]) {
        if ([NSJSONSerialization isValidJSONObject:model]) return model;
        NSMutableArray *newArray = [NSMutableArray new];
        for (id obj in (NSArray *)model) {
            if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
                [newArray addObject:obj];
            } else {
                id jsonObj = ModelToJSONObjectRecursive(obj);
                if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
            }
        }
        return newArray;
    }
    if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;
    if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;
    if ([model isKindOfClass:[NSDate class]]) return [YYISODateFormatter() stringFromDate:(id)model];
    if ([model isKindOfClass:[NSData class]]) return nil;
    
    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]];
    if (!modelMeta || modelMeta->_keyMappedCount == 0) return nil;
    NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:64];
    __unsafe_unretained NSMutableDictionary *dic = result; // avoid retain and release in block
    [modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
        if (!propertyMeta->_getter) return;
        
        id value = nil;
        if (propertyMeta->_isCNumber) {
            value = ModelCreateNumberFromProperty(model, propertyMeta);
        } else if (propertyMeta->_nsType) {
            id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
            value = ModelToJSONObjectRecursive(v);
        } else {
            switch (propertyMeta->_type & YYEncodingTypeMask) {
                case YYEncodingTypeObject: {
                    id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
                    value = ModelToJSONObjectRecursive(v);
                    if (value == (id)kCFNull) value = nil;
                } break;
                case YYEncodingTypeClass: {
                    Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
                    value = v ? NSStringFromClass(v) : nil;
                } break;
                case YYEncodingTypeSEL: {
                    SEL v = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
                    value = v ? NSStringFromSelector(v) : nil;
                } break;
                default: break;
            }
        }
        if (!value) return;
        
        if (propertyMeta->_mappedToKeyPath) {
            NSMutableDictionary *superDic = dic;
            NSMutableDictionary *subDic = nil;
            for (NSUInteger i = 0, max = propertyMeta->_mappedToKeyPath.count; i < max; i++) {
                NSString *key = propertyMeta->_mappedToKeyPath[i];
                if (i + 1 == max) { // end
                    if (!superDic[key]) superDic[key] = value;
                    break;
                }
                
                subDic = superDic[key];
                if (subDic) {
                    if ([subDic isKindOfClass:[NSDictionary class]]) {
                        subDic = subDic.mutableCopy;
                        superDic[key] = subDic;
                    } else {
                        break;
                    }
                } else {
                    subDic = [NSMutableDictionary new];
                    superDic[key] = subDic;
                }
                superDic = subDic;
                subDic = nil;
            }
        } else {
            if (!dic[propertyMeta->_mappedToKey]) {
                dic[propertyMeta->_mappedToKey] = value;
            }
        }
    }];
    
    if (modelMeta->_hasCustomTransformToDictionary) {
        BOOL suc = [((id<YYModel>)model) modelCustomTransformToDictionary:dic];
        if (!suc) return nil;
    }
    return result;
}

參考資料

鄭欽洪_:YYModel 源碼歷險記

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末在岂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蛮寂,更是在濱河造成了極大的恐慌蔽午,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酬蹋,死亡現(xiàn)場離奇詭異及老,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)范抓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門骄恶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人匕垫,你說我怎么就攤上這事僧鲁。” “怎么了象泵?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵寞秃,是天一觀的道長。 經(jīng)常有香客問我单芜,道長蜕该,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任洲鸠,我火速辦了婚禮堂淡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扒腕。我一直安慰自己绢淀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布瘾腰。 她就那樣靜靜地躺著皆的,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蹋盆。 梳的紋絲不亂的頭發(fā)上费薄,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天硝全,我揣著相機(jī)與錄音,去河邊找鬼楞抡。 笑死伟众,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的召廷。 我是一名探鬼主播凳厢,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼竞慢!你這毒婦竟也來了先紫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤筹煮,失蹤者是張志新(化名)和其女友劉穎遮精,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寺谤,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仑鸥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了变屁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片眼俊。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖粟关,靈堂內(nèi)的尸體忽然破棺而出疮胖,到底是詐尸還是另有隱情,我是刑警寧澤闷板,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布澎灸,位于F島的核電站,受9級特大地震影響遮晚,放射性物質(zhì)發(fā)生泄漏性昭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一县遣、第九天 我趴在偏房一處隱蔽的房頂上張望糜颠。 院中可真熱鬧,春花似錦萧求、人聲如沸其兴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽元旬。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間匀归,已是汗流浹背坑资。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留穆端,地道東北人盐茎。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像徙赢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子探越,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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