YYModel 閱讀總結(jié)

簡介(摘至官網(wǎng)

特性

  • 高性能: 模型轉(zhuǎn)換性能接近手寫解析代碼。
  • 自動(dòng)類型轉(zhuǎn)換: 對象類型可以自動(dòng)轉(zhuǎn)換,詳情見下方表格。
  • 類型安全: 轉(zhuǎn)換過程中,所有的數(shù)據(jù)類型都會被檢測一遍,以保證類型安全,避免崩潰問題。
  • 無侵入性: 模型無需繼承自其他基類。
  • 輕量: 該框架只有 5 個(gè)文件 (包括.h文件)。
  • 文檔和單元測試: 文檔覆蓋率100%, 代碼覆蓋率99.6%。

基本使用

簡單的 Model 與 JSON 相互轉(zhuǎn)換

// JSON:
{
    "uid":123456,
    "name":"Harry",
    "created":"1965-07-31T00:00:00+0000"
}

// Model:
@interface User : NSObject
@property UInt64 uid;
@property NSString *name;
@property NSDate *created;
@end
@implementation User
@end

    
// 將 JSON (NSData,NSString,NSDictionary) 轉(zhuǎn)換為 Model:
User *user = [User yy_modelWithJSON:json];
    
// 將 Model 轉(zhuǎn)換為 JSON 對象:
NSDictionary *json = [user yy_modelToJSONObject];

當(dāng) JSON/Dictionary 中的對象類型與 Model 屬性不一致時(shí),YYModel 將會進(jìn)行如下自動(dòng)轉(zhuǎn)換。自動(dòng)轉(zhuǎn)換不支持的值將會被忽略,以避免各種潛在的崩潰問題。

Model 屬性名和 JSON 中的 Key 不相同

// JSON:
{
    "n":"Harry Pottery",
    "p": 256,
    "ext" : {
        "desc" : "A book written by J.K.Rowing."
    },
    "ID" : 100010
}

// Model:
@interface Book : NSObject
@property NSString *name;
@property NSInteger page;
@property NSString *desc;
@property NSString *bookID;
@end
@implementation Book
//返回一個(gè) Dict,將 Model 屬性名對映射到 JSON 的 Key。
+ (NSDictionary *)modelCustomPropertyMapper {
    return @{@"name" : @"n",
             @"page" : @"p",
             @"desc" : @"ext.desc",
             @"bookID" : @[@"id",@"ID",@"book_id"]};
}
@end

你可以把一個(gè)或一組 json key (key path) 映射到一個(gè)或多個(gè)屬性。如果一個(gè)屬性沒有映射關(guān)系,那默認(rèn)會使用相同屬性名作為映射。

在 json->model 的過程中:如果一個(gè)屬性對應(yīng)了多個(gè) json key,那么轉(zhuǎn)換過程會按順序查找,并使用第一個(gè)不為空的值。

在 model->json 的過程中:如果一個(gè)屬性對應(yīng)了多個(gè) json key (key path),那么轉(zhuǎn)換過程僅會處理第一個(gè) json key (key path);如果多個(gè)屬性對應(yīng)了同一個(gè) json key,則轉(zhuǎn)換過過程會使用其中任意一個(gè)不為空的值。

Model 包含其他 Model

// JSON
{
    "author":{
        "name":"J.K.Rowling",
        "birthday":"1965-07-31T00:00:00+0000"
    },
    "name":"Harry Potter",
    "pages":256
}

// Model: 什么都不用做,轉(zhuǎn)換會自動(dòng)完成
@interface Author : NSObject
@property NSString *name;
@property NSDate *birthday;
@end
@implementation Author
@end
    
@interface Book : NSObject
@property NSString *name;
@property NSUInteger pages;
@property Author *author; //Book 包含 Author 屬性
@end
@implementation Book
@end

容器類屬性

@class Shadow, Border, Attachment;

@interface Attributes
@property NSString *name;
@property NSArray *shadows; //Array<Shadow>
@property NSSet *borders; //Set<Border>
@property NSMutableDictionary *attachments; //Dict<NSString,Attachment>
@end

@implementation Attributes
// 返回容器類中的所需要存放的數(shù)據(jù)類型 (以 Class 或 Class Name 的形式)。
+ (NSDictionary *)modelContainerPropertyGenericClass {
    return @{@"shadows" : [Shadow class],
             @"borders" : Border.class,
             @"attachments" : @"Attachment" };
}
@end

黑名單與白名單

@interface User
@property NSString *name;
@property NSUInteger age;
@end
    
@implementation Attributes
// 如果實(shí)現(xiàn)了該方法,則處理過程中會忽略該列表內(nèi)的所有屬性
+ (NSArray *)modelPropertyBlacklist {
    return @[@"test1", @"test2"];
}
// 如果實(shí)現(xiàn)了該方法,則處理過程中不會處理該列表外的屬性。
+ (NSArray *)modelPropertyWhitelist {
    return @[@"name"];
}
@end

數(shù)據(jù)校驗(yàn)與自定義轉(zhuǎn)換

// JSON:
{
    "name":"Harry",
    "timestamp" : 1445534567
}
    
// Model:
@interface User
@property NSString *name;
@property NSDate *createdAt;
@end

@implementation User
// 當(dāng) JSON 轉(zhuǎn)為 Model 完成后,該方法會被調(diào)用。
// 你可以在這里對數(shù)據(jù)進(jìn)行校驗(yàn),如果校驗(yàn)不通過,可以返回 NO,則該 Model 會被忽略。
// 你也可以在這里做一些自動(dòng)轉(zhuǎn)換不能完成的工作。
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic {
    NSNumber *timestamp = dic[@"timestamp"];
    if (![timestamp isKindOfClass:[NSNumber class]]) return NO;
    _createdAt = [NSDate dateWithTimeIntervalSince1970:timestamp.floatValue];
    return YES;
}
    
// 當(dāng) Model 轉(zhuǎn)為 JSON 完成后,該方法會被調(diào)用。
// 你可以在這里對數(shù)據(jù)進(jìn)行校驗(yàn),如果校驗(yàn)不通過,可以返回 NO,則該 Model 會被忽略。
// 你也可以在這里做一些自動(dòng)轉(zhuǎn)換不能完成的工作。
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic {
    if (!_createdAt) return NO;
    dic[@"timestamp"] = @(n.timeIntervalSince1970);
    return YES;
}
@end

底層原理

YYModel 類結(jié)構(gòu)

1305302-20180914153149933-1445616001.jpg

除掉YYModel.h之外,只剩下了YYClassInfo和NSObject+YYModel兩個(gè)模塊啦

  1. YYClassInfo功能主要是將Runtime層級中的一些結(jié)構(gòu)體封裝到NSObject中調(diào)用


    1305302-20180915103000006-951413839.jpg
  2. NSObject+YYModel功能是提供調(diào)用的接口以及實(shí)現(xiàn)具體的模型轉(zhuǎn)換邏輯

前面已經(jīng)講到Y(jié)YClassInfo主要功能是將Runtime層級的結(jié)構(gòu)體封裝到NSObject層級以便調(diào)用。下面是YYClassInfo與Runtime層級對比:


1305302-20180915103817595-987812236.png

詳解

YYClassIvarInfo

YYClassIvarInfo && objc_ivar
// 類的屬性描述
@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
// 成員變量數(shù)據(jù)類型
@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.
 */
// 初始化一個(gè)類成員變量描述
- (instancetype)initWithIvar:(Ivar)ivar;
@end

緊接著我們看一下Runtime的objc_ivar表示變量的結(jié)構(gòu)體

struct objc_ivar {
    char * _Nullable ivar_name OBJC2_UNAVAILABLE; // 變量名稱
    char * _Nullable ivar_type OBJC2_UNAVAILABLE; // 變量類型
    int ivar_offset OBJC2_UNAVAILABLE; // 變量偏移量
#ifdef __LP64__ // 如果已定義 __LP64__ 則表示正在構(gòu)建 64 位目標(biāo)
    int space OBJC2_UNAVAILABLE; // 變量空間
#endif
}

注:日常開發(fā)中,NSString類型的屬性會用copy修飾,看上面YYClassIvarInfo中typeEncoding和name是用strong修飾。這是因?yàn)槠鋬?nèi)部先是通過Runtime方法拿到const char * 之后通過 stringWithUTF8String 方法之后轉(zhuǎn)為 NSString 的。所以 NSString 這類屬性在確定其不會在初始化之后出現(xiàn)被修改的情況下,使用 strong來修飾 做一次單純的強(qiáng)引用在性能上是比 copy 要高的。

YYClassMethodInfo && objc_method

下面是YYClassMethodInfo

/**
 Method information.
 */
// 類方法的描述
@interface YYClassMethodInfo : NSObject
// 方法,實(shí)質(zhì)上就是一個(gè)結(jié)構(gòu)體
@property (nonatomic, assign, readonly) Method method;                  ///< method opaque struct
// 方法名
@property (nonatomic, strong, readonly) NSString *name;                 ///< method name
// 方法的選擇子,實(shí)質(zhì)上是可以跟 name 進(jìn)行轉(zhuǎn)換的
@property (nonatomic, assign, readonly) SEL sel;                        ///< method's selector
// 函數(shù)的實(shí)現(xiàn)
@property (nonatomic, assign, readonly) IMP imp;                        ///< method's implementation
// 參數(shù)的編碼
@property (nonatomic, strong, readonly) NSString *typeEncoding;         ///< method's parameter and return types
// 返回值的編碼
@property (nonatomic, strong, readonly) NSString *returnTypeEncoding;   ///< return value's type
// 所有的參數(shù)編碼
@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.
 */
// 初始化一個(gè)函數(shù)描述
- (instancetype)initWithMethod:(Method)method;
@end

YYClassMethodInfo則是對Rutime里面的objc_method的封裝,緊接著我們看Runtime的objc_method結(jié)構(gòu)體

struct objc_method {
    SEL _Nonnull method_name OBJC2_UNAVAILABLE; // 方法名稱
    char * _Nullable method_types OBJC2_UNAVAILABLE; // 方法類型
    IMP _Nonnull method_imp OBJC2_UNAVAILABLE; // 方法實(shí)現(xiàn)(函數(shù)指針)
}
YYClassPropertyInfo && property_t

YYClassPropertyInfo是對Runtime中property_t的封裝

/**
 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
// 編碼類型 由 typeEncoding 轉(zhuǎn)換而來
@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
// 隸屬的 class
@property (nullable, nonatomic, assign, readonly) Class cls;      ///< may be nil
// 遵守的協(xié)議
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *protocols; ///< may nil
// get 方法
@property (nonatomic, assign, readonly) SEL getter;               ///< getter (nonnull)
// set 方法
@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

然后來看一下Runtime的property_t結(jié)構(gòu)體

struct property_t {
    const char *name; // 名稱
    const char *attributes; // 修飾
};
YYClassInfo && objc_class

YYClassInfo封裝了Runtime的objc_class,下面看一下YYClassInfo

YYClassInfo

/**
 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
// 這個(gè)類是否是元類
@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

/**
 If the class is changed (for example: you add a method to this class with
 'class_addMethod()'), you should call this method to refresh the class info cache.
 
 After called this method, `needUpdate` will returns `YES`, and you should call 
 'classInfoWithClass' or 'classInfoWithClassName' to get the updated class info.
 */
//
// 更新
- (void)setNeedUpdate;

/**
 If this method returns `YES`, you should stop using this instance and call
 `classInfoWithClass` or `classInfoWithClassName` to get the updated class info.
 
 @return Whether this class info need update.
 */
// 是否更新
- (BOOL)needUpdate;

/**
 Get the class info of a specified Class.
 
 @discussion This method will cache the class info and super-class info
 at the first access to the Class. This method is thread-safe.
 
 @param cls A class.
 @return A class info, or nil if an error occurs.
 */
// 通過 class 初始化一個(gè)類描述對象
+ (nullable instancetype)classInfoWithClass:(Class)cls;

/**
 Get the class info of a specified Class.
 
 @discussion This method will cache the class info and super-class info
 at the first access to the Class. This method is thread-safe.
 
 @param className A class name.
 @return A class info, or nil if an error occurs.
 */
// 通過 類名 初始化一個(gè)描述對象
+ (nullable instancetype)classInfoWithClassName:(NSString *)className;

@end

objc_class

// objc.h
typedef struct objc_class *Class;
 
// runtime.h
struct objc_class {
    Class _Nonnull isa OBJC_ISA_AVAILABILITY; // isa 指針
 
#if !__OBJC2__
    Class _Nullable super_class OBJC2_UNAVAILABLE; // 父類(超類)指針
    const char * _Nonnull name OBJC2_UNAVAILABLE; // 類名
    long version OBJC2_UNAVAILABLE; // 版本
    long info OBJC2_UNAVAILABLE; // 信息
    long instance_size OBJC2_UNAVAILABLE; // 初始尺寸
    struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE; // 變量列表
    struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE; // 方法列表
    struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE; // 緩存
    struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; // 協(xié)議列表
#endif
 
} OBJC2_UNAVAILABLE;

注解:下面是Runtime關(guān)于class的知識,下圖是 《iOS 進(jìn)階》對oc對象模型的解釋


1305302-20180915150609417-1293696256.jpg

下面是對應(yīng)的講解。


1305302-20180915150813320-283185146.png
YYClassInfo 的初始化
+ (instancetype)classInfoWithClass:(Class)cls {
    // 如果父類不存在,則結(jié)束調(diào)用
    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);
    // 從元類緩存中查找類描述對象,如果是元類,如果是元類,就從元類緩存中找;如果cls 不是元類,從類緩存中找
    YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
    // 默認(rèn)為NO 第一次加載 info 的時(shí)候會進(jìn)入判斷條件
    if (info && info->_needUpdate) {
        [info _update];
    }
    dispatch_semaphore_signal(lock);
    // 如果沒有查找到
    if (!info) {
        // 實(shí)例化
        info = [[YYClassInfo alloc] initWithClass:cls];
        if (info) {
            dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
            // 根據(jù)是否是元類屬性區(qū)分存儲
            CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
            dispatch_semaphore_signal(lock);
        }
    }
    return info;
}
下面總結(jié)一下初始化主要步驟:
  1. 首先創(chuàng)建單例緩存,類緩存和元類緩存
  2. 使用dispatch_semaphore 保證緩存線程安全
  3. 初始化操作之前首先緩存中查找是否已經(jīng)向緩存中注冊過的當(dāng)前要初始化的YYClassInfo
  4. 如果查找緩存對象,需要判斷對象是否需要更新以及其他相關(guān)操作
  5. 如果沒有找到緩存對象,就開始初始化
  6. 初始化成功之后,向緩存中注冊YYClassInfo實(shí)例

NSObject+YYModel

1305302-20180915153501929-876137726.jpg

NSObject+YYModel在YYModel主要任務(wù)是利用YYClassInfo層級封裝的類來執(zhí)行JSON模型之間的轉(zhuǎn)換邏輯。下面是NSObject+YYModel講述的主要內(nèi)容:

  1. 類型編碼的解析
  2. 數(shù)據(jù)結(jié)構(gòu)的定義
  3. 遞歸模型的轉(zhuǎn)換
  4. 接口相關(guān)的代碼
數(shù)據(jù)結(jié)構(gòu)的定義

NSObject+YYModel重新定義了兩個(gè)類,來使用 YYClassInfo 中的封裝。


1305302-20180915161006355-1364949982.png

_YYModelPropertyMeta

/// A property info in object model.
// 對象模型的屬性信息
@interface _YYModelPropertyMeta : NSObject {
    @package
    // 屬性的名稱
    NSString *_name;             ///< property's name
    // 屬性的類型
    YYEncodingType _type;        ///< property's type
    // 屬性的基礎(chǔ)類型
    YYEncodingNSType _nsType;    ///< property's Foundation type
    // 是否是數(shù)字類型
    BOOL _isCNumber;             ///< is c number type
    // 類
    Class _cls;                  ///< property's class, or nil
    // 泛型類
    Class _genericCls;           ///< container's generic class, or nil if threr's no generic class
    // get 方法
    SEL _getter;                 ///< getter, or nil if the instances cannot respond
    // set 方法
    SEL _setter;                 ///< setter, or nil if the instances cannot respond
    // KVO 兼容屬性
    BOOL _isKVCCompatible;       ///< YES if it can access with key-value coding
    // 如果結(jié)構(gòu)可以用鍵控歸檔器/非歸檔器編碼,則可以
    BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver
    BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary:
    
    /*
     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)
     */
    // 映射 key
    NSString *_mappedToKey;      ///< the key mapped to
    // 映射 keyPath,如果沒有映射到 keyPath 則返回 nil
    NSArray *_mappedToKeyPath;   ///< the key path mapped to (nil if the name is not key path)
    // key 或者 keyPath 的數(shù)組,如果沒有映射多個(gè)鍵的話則返回 nil
    NSArray *_mappedToKeyArray;  ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys)
    // 屬性信息,詳見上文 YYClassPropertyInfo && property_t 章節(jié)
    YYClassPropertyInfo *_info;  ///< property's info
    // 如果有多個(gè)屬性映射到同一個(gè) key 則指向下一個(gè)模型屬性元
    _YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.
}
@end

_YYModelMeta

// 對象模型的類信息
@interface _YYModelMeta : NSObject {
    @package
    // 類描述
    YYClassInfo *_classInfo;
    /// Key:mapped key and key path, Value:_YYModelPropertyMeta.
    // 屬性映射器,包括key和keypath
    NSDictionary *_mapper;
    /// Array<_YYModelPropertyMeta>, all property meta of this model.
    // 類屬性
    NSArray *_allPropertyMetas;
    /// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
    // 類中的所欲keypath 屬性
    NSArray *_keyPathPropertyMetas;
    /// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
    // 所有key
    NSArray *_multiKeysPropertyMetas;
    /// The number of mapped key (and key path), same to _mapper.count.
    // key 的個(gè)數(shù)
    NSUInteger _keyMappedCount;
    /// Model class type.
    // 類型
    YYEncodingNSType _nsType;
    
    BOOL _hasCustomWillTransformFromDictionary;
    BOOL _hasCustomTransformFromDictionary;
    BOOL _hasCustomTransformToDictionary;
    BOOL _hasCustomClassFromDictionary;
}
@end
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,685評論 1 32
  • 概述 ? iOS源碼解析—YYModel(YYClassInfo)分析了如何根據(jù)OC的Class對象構(gòu)建...
    egoCogito_panf閱讀 11,835評論 4 32
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,098評論 0 9
  • 上面一篇文章沒有分析完yymodel 。 接著上篇接著分析 static void ModelSetValueFo...
    充滿活力的早晨閱讀 1,307評論 0 0
  • YYKit出現(xiàn)了很長時(shí)間了,一直想要詳細(xì)解析一下它的源碼,都是各種緣由推遲了。 最近稍微閑了一點(diǎn),決定先從最簡單的...
    咖啡綠茶1991閱讀 567評論 0 0

友情鏈接更多精彩內(nèi)容