利用Runtime動(dòng)態(tài)綁定Model屬性

利用Runtime動(dòng)態(tài)綁定Model屬性

大家如果在開發(fā)中使用過從網(wǎng)絡(luò)獲取JSON數(shù)據(jù),那么一定對(duì)model.value = [dictionary objectForKey:@"key"]很熟悉豁鲤,相信大多數(shù)剛開始學(xué)習(xí)iOS網(wǎng)絡(luò)開發(fā)的人都是使用類似以上這句代碼將解析為NSDictionary對(duì)象的JSON數(shù)據(jù)綁定到Model上的秽誊。可是如果程序中有很多Model或者M(jìn)odel中有很多屬性琳骡,這么做就會(huì)加大很多工作量锅论,那么有沒有什么簡單的方法解決這個(gè)問題呢?答案就是Runtime技術(shù)楣号!

準(zhǔn)備工作

首先最易,建立一個(gè)Model類,我把它命名為KCModel竖席,它是應(yīng)用中所有Model的父類耘纱,應(yīng)用不能直接使用該類,定義一個(gè)協(xié)議(面向接口編程)毕荐,Model實(shí)現(xiàn)該協(xié)議束析,我命名為KCModelAutoBinding,協(xié)議聲明的方法有:

+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary;
+ (NSDictionary *)dictionaryKeyPathByPropertyKey;
- (void)autoBindingWithDictionary:(NSDictionary *)dictionary;

注意其中有兩個(gè)個(gè)類方法憎亚,說明如下:

第一個(gè)不說了员寇;
+ dictionaryKeyPathByPropertyKey 屬性映射的值在 dictionary 中的位置弄慰,比如 myName 屬性映射 dictionary[@"name"] ,則返回 @{@"myName" : @"name"} 蝶锋,而如果是多層關(guān)系陆爽,比如映射 dictionary[@"data"][@"name"] ,則返回 @{@"myName" : @"data.name"}扳缕;
- autoBindingWithDictionary:dictionary 綁定到Model慌闭。

獲取Model所有屬性

在Runtime中有個(gè)函數(shù)可以獲取某個(gè)類的所有屬性:

class_copyPropertyList(Class cls, unsigned int *outCount)

這是一個(gè)C語言函數(shù),返回的值是objc_property_t的指針(代表一個(gè)數(shù)組)躯舔。
需要注意的是這個(gè)函數(shù)只能獲取到當(dāng)前類的屬性驴剔,而不能獲取到父類的屬性,我們可以使用遞歸的方法獲取到包含父類在內(nèi)的所有屬性粥庄。

以上我們獲得到了objc_property_t的數(shù)組丧失,每個(gè)objc_property_t都代表一個(gè)屬性,我們可以使用以下方法得到屬性名:

property_getName(objc_property_t property)

要想得到更多的信息則需要它了:

property_getAttributes(objc_property_t property)

這個(gè)函數(shù)返回了一段char數(shù)組字符串給我們惜互,有屬性的各種信息布讹,但我們現(xiàn)在只需要一個(gè)信息,那就是屬性的類型训堆。
來看Apple的Runtime指南:

You can use the property_getAttributes function to discover the name, the @encode type string of a property, and other attributes of the property.

The string starts with a T followed by the @encode type and a comma, and finishes with a V followed by the name of the backing instance variable.

也就是說描验,返回的字符串是以T開頭,后面跟屬性類型等各種信息坑鱼,信息之間用,隔開挠乳。通過這些我們就可以得到屬性的類型了。
我們可以新建一個(gè)類來解析并儲(chǔ)存屬性的這些信息姑躲,我把它命名為KCModelProperty。在KCModel中盟蚣,我將所有屬性信息用一個(gè)key為屬性名黍析,valueKCModelProperty對(duì)象的NSDictionary儲(chǔ)存,方便使用屎开。

獲取屬性映射的值

方法很簡單阐枣,將屬性名作為key得到屬性映射的值在 dictionary 中的位置keyPath,不要問我怎么獲得奄抽,這就是之前提到的類方法dictionaryKeyPathByPropertyKey的作用蔼两。

注意:如果屬性是自定義類型,只需要滿足實(shí)現(xiàn)了之前定義的KCModelAutoBinding協(xié)議逞度,那么就可以通過遞歸的方式綁定該屬性额划。

使用KVC賦值

以上我們得到了dictionary所在keyPath位置的值,那么怎么把它賦值給屬性呢档泽?答案是

Class NSClassFromString(NSString *aClassName);

我們通過這個(gè)方法可以得到屬性的類俊戳,然后就可以開始賦值了揖赴。
注意:類分為兩種,一種是系統(tǒng)定義好的類抑胎,另一種是自定義的類——其他Model對(duì)象燥滑。因?yàn)槎鄶?shù)情況下通過解析JSON得到的NSDictionary對(duì)象(如使用AFNetworking)里儲(chǔ)存的都是系統(tǒng)的類,如:NSInteger阿逃、NSArray等铭拧,所以如果是第一種類,只要與dictionary中的值類型一樣就可以直接用它來賦值了恃锉,但是第二種類就需要使用其他方法賦值了搀菩,方法就是最前面提到的類方法modelWithDictionary:,通過這個(gè)方法得到其他Model對(duì)象淡喜,再進(jìn)行賦值秕磷。
賦值方法就是Key-Value Coding技術(shù)的setValue:forKey:

</br>
大功告成炼团。

思路說起來很簡單澎嚣,實(shí)際動(dòng)手又是另外一回事。
</br>

附上我的代碼:

//KCModel.h

#import <Foundation/Foundation.h>

//快速定義與類相同名稱的協(xié)議(數(shù)組元素類型標(biāo)記)
#define KC_ARRAY_TYPE(VAL) \
@protocol VAL <NSObject> \
@end

@protocol KCModelAutoBinding <NSObject>

+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary;
+ (NSArray *)modelsWithArray:(NSArray *)array;
- (void)autoBindingWithDictionary:(NSDictionary *)dictionary;

@end

@interface KCModel : NSObject <KCModelAutoBinding>

+ (NSDictionary *)dictionaryKeyPathByPropertyKey;

@end
//KCModel.m

static id KCTransformNormalValueForClass(id val, NSString *className) {
    id ret = val;
    
    Class valClass = [val class];
    Class cls = nil;
    if (className.length > 0) {
        cls = NSClassFromString(className);
    }
    
    if (!cls || !valClass) {
        ret = nil;
    } else if (![cls isSubclassOfClass:[val class]] && ![valClass isSubclassOfClass:cls]) {
        ret = nil;
    }
    
    return ret;
}

@implementation KCModel

#pragma mark -- KCItemAutoBinding
+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary
{
    id<KCModelAutoBinding> model = [[self class] new];
    [model autoBindingWithDictionary:dictionary];
    
    return model;
}

+ (NSArray *)modelsWithArray:(NSArray *)array
{
    NSMutableArray *models = @[].mutableCopy;
    for (NSDictionary *dict in array) {
        [models addObject:[self modelWithDictionary:dict]];
    }
    
    return [NSArray arrayWithArray:models];
}

- (void)autoBindingWithDictionary:(NSDictionary *)dictionary
{
    NSDictionary *properties = [self.class propertyInfos];
    NSDictionary *dictionaryKeyPathByPropertyKey = [self.class dictionaryKeyPathByPropertyKey];
    
    for (KCModelProperty *property in [properties allValues]) {
        KCModelPropertyType propertyType = property.propertyType;
        NSString *propertyName = property.propertyName;
        NSString *propertyClassName = property.propertyClassName;
        NSString *propertyKeyPath = propertyName;
        
        //獲取屬性映射的dictionary內(nèi)容位置
        if ([dictionaryKeyPathByPropertyKey objectForKey:propertyName]) {
            propertyKeyPath = [dictionaryKeyPathByPropertyKey objectForKey:propertyName];
        }
        
        id value = [dictionary kc_valueForKeyPath:propertyKeyPath]; //從dictionary中得到映射的值
        
        if (value == nil || value == [NSNull null]) {
            continue;
        }
        
        Class propertyClass = nil;
        if (propertyClassName.length > 0) {  //非系統(tǒng)自帶對(duì)象
            propertyClass = NSClassFromString(propertyClassName);
        }
        
        //轉(zhuǎn)換value
        switch (propertyType) {
            //基本數(shù)字類型
            case KCModelPropertyTypeInt:
            case KCModelPropertyTypeFloat:
            case KCModelPropertyTypeDouble:
            case KCModelPropertyTypeBool:
            case KCModelPropertyTypeNumber:{
                if ([value isKindOfClass:[NSString class]]) {
                    NSNumberFormatter *numberFormatter = [NSNumberFormatter new];
                    [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
                    value = [numberFormatter numberFromString:value];
                }else{
                    value = KCTransformNormalValueForClass(value, NSStringFromClass([NSNumber class]));
                }
            }
                break;
            case KCModelPropertyTypeChar:{
                if ([value isKindOfClass:[NSString class]]) {
                    char firstCharacter = [value characterAtIndex:0];
                    value = [NSNumber numberWithChar:firstCharacter];
                } else {
                    value = KCTransformNormalValueForClass(value, NSStringFromClass([NSNumber class]));
                }
            }
                break;
            case KCModelPropertyTypeString:{
                if ([value isKindOfClass:[NSNumber class]]) {
                    value = [value stringValue];
                } else {
                    value = KCTransformNormalValueForClass(value, NSStringFromClass([NSString class]));
                }
            }
                break;
            case KCModelPropertyTypeData:{
                value = KCTransformNormalValueForClass(value, NSStringFromClass([NSData class]));
            }
                break;
            case KCModelPropertyTypeDate:{
                value = KCTransformNormalValueForClass(value, NSStringFromClass([NSDate class]));
            }
                break;
            case KCModelPropertyTypeAny:
                break;
            case KCModelPropertyTypeDictionary:{
                value = KCTransformNormalValueForClass(value, NSStringFromClass([NSDictionary class]));
            }
                break;
            case KCModelPropertyTypeMutableDictionary:{
                value = KCTransformNormalValueForClass(value, NSStringFromClass([NSDictionary class]));
                value = [value mutableCopy];
            }
                break;
            case KCModelPropertyTypeArray:{
                if (propertyClass && [propertyClass isSubclassOfClass:[KCModel class]]) {  //儲(chǔ)存KCItem子類對(duì)象的數(shù)組
                    value = [propertyClass itemsWithArray:value];
                }else{
                    value = KCTransformNormalValueForClass(value, NSStringFromClass([NSArray class]));
                }
            }
                break;
            case KCModelPropertyTypeMutableArray:{
                value = KCTransformNormalValueForClass(value, NSStringFromClass([NSArray class]));
                value = [value mutableCopy];
            }
                break;
            case KCModelPropertyTypeObject:
            case KCModelPropertyTypeModel:{
                if (propertyClass) {
                    if ([propertyClass conformsToProtocol:@protocol(KCModelAutoBinding)]     //屬性為實(shí)現(xiàn)了KCModelAutoBinding協(xié)議的對(duì)象
                        && [value isKindOfClass:[NSDictionary class]]) {
                        NSDictionary *oldValue = value;
                        value = [[propertyClass alloc] init];
                        [value autoBindingWithDictionary:oldValue];
                    }else{
                        value = KCTransformNormalValueForClass(value, propertyClassName);
                    }
                }
            }
                break;
        }
        
        //KVC
        if (value && value != [NSNull null]) {
            [self setValue:value forKey:propertyName];
        }
    }
}

#pragma mark -- Class method
+ (NSDictionary *)propertyInfos
{
    //獲取緩存數(shù)據(jù)
    NSDictionary *cachedInfos = objc_getAssociatedObject(self, _cmd);
    if (cachedInfos != nil) {
        return cachedInfos;
    }
    
    NSMutableDictionary *ret = [NSMutableDictionary dictionary];
    
    unsigned int propertyCount;
    objc_property_t *properties = class_copyPropertyList(self, &propertyCount); //獲取自身的所有屬性(c語言瘟芝,*properties代表數(shù)組)
    Class superClass = class_getSuperclass(self);
    
    //獲取父類的所有屬性
    if (superClass && ![NSStringFromClass(superClass) isEqualToString:@"KCModel"]) {
        NSDictionary *superProperties = [superClass propertyInfos];  //遞歸
        [ret addEntriesFromDictionary:superProperties];
    }
    
    for (int i = 0; i < propertyCount; i++) {
        objc_property_t property = properties[i];   //獲取第i個(gè)屬性
        const char *propertyCharName = property_getName(property);  //獲取當(dāng)前屬性的名稱
        NSString *propertyName = @(propertyCharName);
        
        KCModelProperty *propertyInfo = [[KCModelProperty alloc] initWithPropertyName:propertyName objcProperty:property];
        [ret setValue:propertyInfo forKey:propertyName];
    }
    
    free(properties);
    
    //設(shè)置緩存數(shù)據(jù)
    objc_setAssociatedObject(self, @selector(propertyInfos), ret, OBJC_ASSOCIATION_COPY);
    
    return ret;
}

+ (NSDictionary *)dictionaryKeyPathByPropertyKey
{
    return [NSDictionary dictionaryWithObjects:[self propertyNames] forKeys:[self propertyNames]];
}

+ (NSArray *)propertyNames
{
    NSDictionary *ret = [self propertyInfos];
    return [ret allKeys];
}

@end
//KCModelProperty.h

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

typedef NS_ENUM(NSInteger, KCModelPropertyType) {
    KCModelPropertyTypeInt = 0,
    KCModelPropertyTypeFloat,
    KCModelPropertyTypeDouble,
    KCModelPropertyTypeBool,
    KCModelPropertyTypeChar,
    
    KCModelPropertyTypeString,
    KCModelPropertyTypeNumber,
    KCModelPropertyTypeData,
    KCModelPropertyTypeDate,
    KCModelPropertyTypeAny,
    
    KCModelPropertyTypeArray,
    KCModelPropertyTypeMutableArray,
    KCModelPropertyTypeDictionary,
    KCModelPropertyTypeMutableDictionary,
    KCModelPropertyTypeObject,
    KCModelPropertyTypeModel
};

@interface KCModelProperty : NSObject

@property (nonatomic, strong, readonly) NSString*   propertyClassName;
@property (nonatomic, strong, readonly) NSString*   propertyName;
@property (nonatomic, assign, readonly) KCModelPropertyType propertyType;

- (instancetype)initWithPropertyName:(NSString *)propertyName objcProperty:(objc_property_t)objcProperty;

@end
//KCModelProperty.m

#import "KCModelProperty.h"
#import "KCModel.h"

@implementation KCModelProperty

- (instancetype)initWithPropertyName:(NSString *)propertyName objcProperty:(objc_property_t)objcProperty
{
    if (self = [super init]) {
        _propertyName = propertyName;
        
        /*********************************************
         Apple "Objective-C Runtime Programming Guide":
            You can use the property_getAttributes function to discover the name, 
            the @encode type string of a property, and other attributes of the property.
            The string starts with a T followed by the @encode type and a comma, and finishes 
            with a V followed by the name of the backing instance variable.
        *********************************************/
        const char *attr = property_getAttributes(objcProperty);
        NSString *propertyAttributes = @(attr); //使用","隔開的屬性描述字符串
        propertyAttributes = [propertyAttributes substringFromIndex:1]; //移除"T"
        
        NSArray *attributes = [propertyAttributes componentsSeparatedByString:@","]; //屬性描述數(shù)組
        
        NSString *typeAttr = attributes[0];  //屬性類型名稱
        const char *typeCharAttr = [typeAttr UTF8String];
        
        NSString *encodeCodeStr = [typeAttr substringToIndex:1];  //屬性類型
        const char *encodeCode = [encodeCodeStr UTF8String];
        const char typeEncoding = *encodeCode;
        
        //判斷類型
        switch (typeEncoding) {
            case 'i': // int
            case 's': // short
            case 'l': // long
            case 'q': // long long
            case 'I': // unsigned int
            case 'S': // unsigned short
            case 'L': // unsigned long
            case 'Q': // unsigned long long
                _propertyType = KCModelPropertyTypeInt;
                break;
            case 'f': // float
                _propertyType = KCModelPropertyTypeFloat;
                break;
            case 'd': // double
                _propertyType = KCModelPropertyTypeDouble;
                break;
            case 'B': // BOOL
                _propertyType = KCModelPropertyTypeBool;
                break;
            case 'c': // char
            case 'C': // unsigned char
                _propertyType = KCModelPropertyTypeChar;
                break;
            case '@':{ //object
                
                
                static const char arrayPrefix[] = "@\"NSArray<";  //NSArray,且遵循某個(gè)協(xié)議
                static const int arrayPrefixLen = sizeof(arrayPrefix) - 1;
                
                if (typeCharAttr[1] == '\0') {
                    // string is "@"
                    _propertyType = KCModelPropertyTypeAny;
                } else if (strncmp(typeCharAttr, arrayPrefix, arrayPrefixLen) == 0) {
                    /*******************
                        因?yàn)橹挥蠳SArray遵循某個(gè)協(xié)議才能被property_getAttributes()函數(shù)識(shí)別出來易桃,
                        以此為標(biāo)記表示這個(gè)數(shù)組存儲(chǔ)著以協(xié)議名為類名的Model對(duì)象
                     *******************/
                    _propertyType = KCModelPropertyTypeArray;
                    NSString *className = [[NSString alloc] initWithBytes:typeCharAttr + arrayPrefixLen
                                                                   length:strlen(typeCharAttr + arrayPrefixLen) - 2
                                                                 encoding:NSUTF8StringEncoding];
                    
                    Class propertyClass = NSClassFromString(className);
                    if (propertyClass) {
                        _propertyClassName = NSStringFromClass(propertyClass);
                    }
                } else if (strcmp(typeCharAttr, "@\"NSString\"") == 0) {
                    _propertyType = KCModelPropertyTypeString;
                } else if (strcmp(typeCharAttr, "@\"NSNumber\"") == 0) {
                    _propertyType = KCModelPropertyTypeNumber;
                } else if (strcmp(typeCharAttr, "@\"NSDate\"") == 0) {
                    _propertyType = KCModelPropertyTypeDate;
                } else if (strcmp(typeCharAttr, "@\"NSData\"") == 0) {
                    _propertyType = KCModelPropertyTypeData;
                } else if (strcmp(typeCharAttr, "@\"NSDictionary\"") == 0) {
                    _propertyType = KCModelPropertyTypeDictionary;
                } else if (strcmp(typeCharAttr, "@\"NSArray\"") == 0) {
                    _propertyType = KCModelPropertyTypeArray;
                } else if (strcmp(typeCharAttr, "@\"NSMutableArray\"") == 0){
                    _propertyType = KCModelPropertyTypeMutableArray;
                } else if (strcmp(typeCharAttr, "@\"NSMutableDictionary\"") == 0){
                    _propertyType = KCModelPropertyTypeMutableDictionary;
                }else {
                    _propertyType = KCModelPropertyTypeObject;
                    
                    Class propertyClass = nil;
                    if (typeAttr.length >= 3) {
                        NSString* className = [typeAttr substringWithRange:NSMakeRange(2, typeAttr.length-3)];
                        propertyClass = NSClassFromString(className);
                    }
                    
                    if (propertyClass) {
                        if ([propertyClass isSubclassOfClass:[KCModel class]]) {
                            _propertyType = KCModelPropertyTypeModel;
                        }
                        _propertyClassName = NSStringFromClass(propertyClass);
                    }
                    
                }
            }
                break;
            default:
                break;
        }
    }
    return self;
}

@end
//NSDictionary+KCModel.h

#import <Foundation/Foundation.h>

@interface NSDictionary (KCModel)

- (id)kc_valueForKeyPath:(NSString *)keyPath;

@end
//NSDictionary+KCModel.m

@implementation NSDictionary (KCModel)

- (id)kc_valueForKeyPath:(NSString *)keyPath
{
    NSArray *components = [keyPath componentsSeparatedByString:@"."];
    
    id ret = self;
    for (NSString *component in components) {
        if (ret == nil || ret == [NSNull null] || ![ret isKindOfClass:[NSDictionary class]]) {
            break;
        }
        ret = ret[component];
    }
    return ret;
}

@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市锌俱,隨后出現(xiàn)的幾起案子晤郑,更是在濱河造成了極大的恐慌,老刑警劉巖贸宏,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件造寝,死亡現(xiàn)場離奇詭異,居然都是意外死亡吭练,警方通過查閱死者的電腦和手機(jī)诫龙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鲫咽,“玉大人签赃,你說我怎么就攤上這事》质” “怎么了锦聊?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長箩绍。 經(jīng)常有香客問我孔庭,道長,這世上最難降的妖魔是什么伶选? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任史飞,我火速辦了婚禮尖昏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘构资。我一直安慰自己抽诉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布吐绵。 她就那樣靜靜地躺著迹淌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪己单。 梳的紋絲不亂的頭發(fā)上唉窃,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天,我揣著相機(jī)與錄音纹笼,去河邊找鬼纹份。 笑死,一個(gè)胖子當(dāng)著我的面吹牛廷痘,可吹牛的內(nèi)容都是我干的蔓涧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼笋额,長吁一口氣:“原來是場噩夢啊……” “哼元暴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起兄猩,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤茉盏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后枢冤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸠姨,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年淹真,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了享怀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡趟咆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出梅屉,到底是詐尸還是另有隱情值纱,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布坯汤,位于F島的核電站虐唠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏惰聂。R本人自食惡果不足惜疆偿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一咱筛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧杆故,春花似錦迅箩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至撤蟆,卻和暖如春奕塑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背家肯。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國打工龄砰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讨衣。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓换棚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親值依。 傳聞我的和親對(duì)象是個(gè)殘疾皇子圃泡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,709評(píng)論 0 9
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理愿险,服務(wù)發(fā)現(xiàn)颇蜡,斷路器,智...
    卡卡羅2017閱讀 134,651評(píng)論 18 139
  • 概述 ? iOS源碼解析—YYModel(YYClassInfo)分析了如何根據(jù)OC的Class對(duì)象構(gòu)建...
    egoCogito_panf閱讀 11,568評(píng)論 4 32
  • 無論我們的感受是什么辆亏,都能告訴我們很多關(guān)于自己的事情风秤。感覺沒有對(duì)錯(cuò)之分,它們只是感受扮叨。感受是一種能量缤弦,而這種能量會(huì)...
    正面管教講師張素敏閱讀 329評(píng)論 0 0
  • 傻氣的人碍沐,喜歡給心,也許會(huì)被人騙衷蜓,卻未必能得到別人的累提。 小六那天晚上打電話給我。電話那頭的她磁浇,很難形容斋陪,像是失去了...
    蚊子小姐_閱讀 365評(píng)論 0 2