YYModel 源码解读(二)之NSObject+YYModel.h (1)

本篇文章主要介绍 _YYModelPropertyMeta 前边的内容

首先先解释一下前边的辅助函数和枚举变量,在写一个功能的时候,这些辅助的东西可能不是一开始就能想出来的,应该是在后续的编码过程中 逐步添加的。

#define force_inline __inline__ __attribute__((always_inline))

这行代码用到了C语言的内联函数

内联函数: 是用inline修饰的函数,内联函数在代码层次看和普通的函数结构一样,却不具备函数的性质,内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入到调用处,和宏很类似,但是有一下不同点

我们先定义一个宏

#define TABLE_COMP(x) ((x)>0?(x):0)

那么我们为什么要使用宏呢?而不是直接调用函数呢?

应为函数的调用需要把当前程序的执行循序切换到函数在内存中的地址,将函数内的程序执行完毕后,再次跳回之前调用函数的地方,这就需要记录函数调用前和调用后的状态,因此函数调用就产生了时间和空间上的开销,会影响效率,而宏只是在预编译的地方把代码展开,不需要时间和空间上的开销,效率比函数高

但是宏也有不尽如人意的地方

1. 宏不能方位对象的私有成员

2. 宏的定义很容易产生二义性

举例说明:

#define TABLE_MULTI(x) (x*x)

TABLE_MULTI(10)  ------ >  结果是 100  这个是我们想要的结果

TABLE_MULTI(10+10)  --------> 结果是 120  这个不是我们想要的结果  实际上 10 + 10 * 10 + 10 --> 120

为了避免这些错误,我们在使用宏的时候,应该尽量写成这样

#define TABLE_MULTI(x) ((x)*(x))

但仍然存在问题,

TABLE_MULTI(a++)

当a = 4 的时候,展开后是(4++)*(4++) == 16 并不是我们想要的结果 5*5 = 25

事实上,我们完全可以使用内联函数取代宏,他们之间的区别在于: 宏是由预处理器进行替代的,而内联函数则是通过编译器控制实现的,内联函数是真正的函数,只有在需要用到的时候才会像宏那样的展开,所以取消了函数的参数压栈,减少了调用的开销,我们可以像调用函数一样调用内联函数,而不必担心产生于处理宏一样的问题。

好了,内联函数相信大家应该已经明白怎么使用了,接下来,我们看源码

/// Foundation Class Type
typedef NS_ENUM (NSUInteger, YYEncodingNSType) {
    YYEncodingTypeNSUnknown = 0,
    YYEncodingTypeNSString,
    YYEncodingTypeNSMutableString,
    YYEncodingTypeNSValue,
    YYEncodingTypeNSNumber,
    YYEncodingTypeNSDecimalNumber,   ///  科学计数值 当值很大的时候使用,比如: 310000000 ---> 3.1 * 10的8次方
    YYEncodingTypeNSData,
    YYEncodingTypeNSMutableData,
    YYEncodingTypeNSDate,
    YYEncodingTypeNSURL,
    YYEncodingTypeNSArray,
    YYEncodingTypeNSMutableArray,
    YYEncodingTypeNSDictionary,
    YYEncodingTypeNSMutableDictionary,
    YYEncodingTypeNSSet,
    YYEncodingTypeNSMutableSet,
};

这个是Foundation Class 的类型封装,基本上都是我们熟悉的 NSDecimaNumber 是科学计数值的类型,往往 定义了一个枚举后,应该提供一个方法来进行必要的转换

/// Get the Foundation class type from property info.
static force_inline YYEncodingNSType YYClassGetNSType(Class cls) {
    if (!cls) return YYEncodingTypeNSUnknown;
    if ([cls isSubclassOfClass:[NSMutableString class]]) return YYEncodingTypeNSMutableString;
    if ([cls isSubclassOfClass:[NSString class]]) return YYEncodingTypeNSString;
    if ([cls isSubclassOfClass:[NSDecimalNumber class]]) return YYEncodingTypeNSDecimalNumber;
    if ([cls isSubclassOfClass:[NSNumber class]]) return YYEncodingTypeNSNumber;
    if ([cls isSubclassOfClass:[NSValue class]]) return YYEncodingTypeNSValue;
    if ([cls isSubclassOfClass:[NSMutableData class]]) return YYEncodingTypeNSMutableData;
    if ([cls isSubclassOfClass:[NSData class]]) return YYEncodingTypeNSData;
    if ([cls isSubclassOfClass:[NSDate class]]) return YYEncodingTypeNSDate;
    if ([cls isSubclassOfClass:[NSURL class]]) return YYEncodingTypeNSURL;
    if ([cls isSubclassOfClass:[NSMutableArray class]]) return YYEncodingTypeNSMutableArray;
    if ([cls isSubclassOfClass:[NSArray class]]) return YYEncodingTypeNSArray;
    if ([cls isSubclassOfClass:[NSMutableDictionary class]]) return YYEncodingTypeNSMutableDictionary;
    if ([cls isSubclassOfClass:[NSDictionary class]]) return YYEncodingTypeNSDictionary;
    if ([cls isSubclassOfClass:[NSMutableSet class]]) return YYEncodingTypeNSMutableSet;
    if ([cls isSubclassOfClass:[NSSet class]]) return YYEncodingTypeNSSet;
    return YYEncodingTypeNSUnknown;
}

判断是否是一个C 的类型

/// Whether the type is c number.
static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type) {
    switch (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 YYEncodingTypeLongDouble: return YES;
        default: return NO;
    }
}

接下来的方法是 在id 类型中 解析 NSNumber , 这里用到了NSCharacterSet 这个类,下边对这个类做一些简单介绍

这个类配合NSScanner  做字符串的过滤操作

NSCharacterSet的详细使用说明

 1 /// Parse a number value from ‘id‘.
 2 static force_inline NSNumber *YYNSNumberCreateFromID(__unsafe_unretained id value) {
 3     static NSCharacterSet *dot;
 4     static NSDictionary *dic;
 5     static dispatch_once_t onceToken;
 6     dispatch_once(&onceToken, ^{
 7         dot = [NSCharacterSet characterSetWithRange:NSMakeRange(‘.‘, 1)];
 8         dic = @{@"TRUE" :   @(YES),
 9                 @"True" :   @(YES),
10                 @"true" :   @(YES),
11                 @"FALSE" :  @(NO),
12                 @"False" :  @(NO),
13                 @"false" :  @(NO),
14                 @"YES" :    @(YES),
15                 @"Yes" :    @(YES),
16                 @"yes" :    @(YES),
17                 @"NO" :     @(NO),
18                 @"No" :     @(NO),
19                 @"no" :     @(NO),
20                 @"NIL" :    (id)kCFNull,
21                 @"Nil" :    (id)kCFNull,
22                 @"nil" :    (id)kCFNull,
23                 @"NULL" :   (id)kCFNull,
24                 @"Null" :   (id)kCFNull,
25                 @"null" :   (id)kCFNull,
26                 @"(NULL)" : (id)kCFNull,
27                 @"(Null)" : (id)kCFNull,
28                 @"(null)" : (id)kCFNull,
29                 @"<NULL>" : (id)kCFNull,
30                 @"<Null>" : (id)kCFNull,
31                 @"<null>" : (id)kCFNull};
32     });
33
34     // 判空
35     if (!value || value == (id)kCFNull) return nil;
36     // 如果是NSNumber 直接返回
37     if ([value isKindOfClass:[NSNumber class]]) return value;
38     // 如果是字符串
39     if ([value isKindOfClass:[NSString class]]) {
40
41         // 取出字典中映射的值
42         NSNumber *num = dic[value];
43         if (num) {
44             if (num == (id)kCFNull) return nil;
45             return num;
46         }
47
48         // 字符串中包含字符‘.’
49         if ([(NSString *)value rangeOfCharacterFromSet:dot].location != NSNotFound) {
50             const char *cstring = ((NSString *)value).UTF8String;
51             if (!cstring) return nil;
52
53             // 把字符串转换成浮点数
54             double num = atof(cstring);
55             if (isnan(num) || isinf(num)) return nil;
56             return @(num);
57         } else {
58             const char *cstring = ((NSString *)value).UTF8String;
59             if (!cstring) return nil;
60
61             // 返回字符串 的 长长整型
62             return @(atoll(cstring));
63         }
64     }
65
66     return nil;
67 }

上边代码的主要功能就是把id 类型 转换成NSNumber 类型,其中有两个函数

atof()    ----  将字符串转换成浮点类型
atoll()   ----   将字符串转换成长长整型

下边这个方法就是把字符串转换成日期的方法

使用Block来保存 可支持的字符串和转换结果,,然后根据字符串的长度进行转换

  1 /// Parse string to date.
  2 static force_inline NSDate *YYNSDateFromString(__unsafe_unretained NSString *string) {
  3     typedef NSDate* (^YYNSDateParseBlock)(NSString *string);
  4     #define kParserNum 34
  5     static YYNSDateParseBlock blocks[kParserNum + 1] = {0};
  6     static dispatch_once_t onceToken;
  7     dispatch_once(&onceToken, ^{
  8         {
  9             /*
 10              2014-01-20  // Google
 11              */
 12             NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
 13             formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
 14             formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
 15             formatter.dateFormat = @"yyyy-MM-dd";
 16             blocks[10] = ^(NSString *string) { return [formatter dateFromString:string]; };
 17         }
 18
 19         {
 20             /*
 21              2014-01-20 12:24:48
 22              2014-01-20T12:24:48   // Google
 23              2014-01-20 12:24:48.000
 24              2014-01-20T12:24:48.000
 25              */
 26             NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init];
 27             formatter1.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
 28             formatter1.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
 29             formatter1.dateFormat = @"yyyy-MM-dd‘T‘HH:mm:ss";
 30
 31             NSDateFormatter *formatter2 = [[NSDateFormatter alloc] init];
 32             formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
 33             formatter2.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
 34             formatter2.dateFormat = @"yyyy-MM-dd HH:mm:ss";
 35
 36             NSDateFormatter *formatter3 = [[NSDateFormatter alloc] init];
 37             formatter3.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
 38             formatter3.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
 39             formatter3.dateFormat = @"yyyy-MM-dd‘T‘HH:mm:ss.SSS";
 40
 41             NSDateFormatter *formatter4 = [[NSDateFormatter alloc] init];
 42             formatter4.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
 43             formatter4.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
 44             formatter4.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
 45
 46             blocks[19] = ^(NSString *string) {
 47                 if ([string characterAtIndex:10] == ‘T‘) {
 48                     return [formatter1 dateFromString:string];
 49                 } else {
 50                     return [formatter2 dateFromString:string];
 51                 }
 52             };
 53
 54             blocks[23] = ^(NSString *string) {
 55                 if ([string characterAtIndex:10] == ‘T‘) {
 56                     return [formatter3 dateFromString:string];
 57                 } else {
 58                     return [formatter4 dateFromString:string];
 59                 }
 60             };
 61         }
 62
 63         {
 64             /*
 65              2014-01-20T12:24:48Z        // Github, Apple
 66              2014-01-20T12:24:48+0800    // Facebook
 67              2014-01-20T12:24:48+12:00   // Google
 68              2014-01-20T12:24:48.000Z
 69              2014-01-20T12:24:48.000+0800
 70              2014-01-20T12:24:48.000+12:00
 71              */
 72             NSDateFormatter *formatter = [NSDateFormatter new];
 73             formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
 74             formatter.dateFormat = @"yyyy-MM-dd‘T‘HH:mm:ssZ";
 75
 76             NSDateFormatter *formatter2 = [NSDateFormatter new];
 77             formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
 78             formatter2.dateFormat = @"yyyy-MM-dd‘T‘HH:mm:ss.SSSZ";
 79
 80             blocks[20] = ^(NSString *string) { return [formatter dateFromString:string]; };
 81             blocks[24] = ^(NSString *string) { return [formatter dateFromString:string]?: [formatter2 dateFromString:string]; };
 82             blocks[25] = ^(NSString *string) { return [formatter dateFromString:string]; };
 83             blocks[28] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
 84             blocks[29] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
 85         }
 86
 87         {
 88             /*
 89              Fri Sep 04 00:12:21 +0800 2015 // Weibo, Twitter
 90              Fri Sep 04 00:12:21.000 +0800 2015
 91              */
 92             NSDateFormatter *formatter = [NSDateFormatter new];
 93             formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
 94             formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy";
 95
 96             NSDateFormatter *formatter2 = [NSDateFormatter new];
 97             formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
 98             formatter2.dateFormat = @"EEE MMM dd HH:mm:ss.SSS Z yyyy";
 99
100             blocks[30] = ^(NSString *string) { return [formatter dateFromString:string]; };
101             blocks[34] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
102         }
103     });
104     if (!string) return nil;
105     if (string.length > kParserNum) return nil;
106     YYNSDateParseBlock parser = blocks[string.length];
107     if (!parser) return nil;
108     return parser(string);
109     #undef kParserNum
110 }

获得NSBlock 这个类 ,

 1 /// Get the ‘NSBlock‘ class.
 2 static force_inline Class YYNSBlockClass() {
 3     static Class cls;
 4     static dispatch_once_t onceToken;
 5     dispatch_once(&onceToken, ^{
 6         void (^block)(void) = ^{};
 7         cls = ((NSObject *)block).class;
 8         while (class_getSuperclass(cls) != [NSObject class]) {
 9             NSLog(@"cls = %@",class_getSuperclass(cls));
10             cls = class_getSuperclass(cls);
11         }
12     });
13     return cls; // current is "NSBlock"
14 }

加入了打印我们可以看出 block 的父类的关系是

block -------> NSGlobalBlock ---------> NSBlock

获取标准的时间的格式

 1 /**
 2  Get the ISO date formatter.
 3
 4  ISO8601 format example:
 5  2010-07-09T16:13:30+12:00
 6  2011-01-11T11:11:11+0000
 7  2011-01-26T19:06:43Z
 8
 9  length: 20/24/25
10  */
11 static force_inline NSDateFormatter *YYISODateFormatter() {
12     static NSDateFormatter *formatter = nil;
13     static dispatch_once_t onceToken;
14     dispatch_once(&onceToken, ^{
15         formatter = [[NSDateFormatter alloc] init];
16         formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
17         formatter.dateFormat = @"yyyy-MM-dd‘T‘HH:mm:ssZ";
18     });
19     return formatter;
20 }

根据字典和key 或者keypath 获取 value

@{
     @"name" : @"machao",
     @"user" : @{
                 @"uid":@{
                             @"name":@"abc",
                             @"addrs":@"beijing",
                         },
                 @"pic" : @"http://7ke.com",
                },
 };使用下边的方法的思路是获取这样的一个值

[NSDictionary valueForKeyPath:@"user.uid.name"]

数组keys 装着的是@[@"user",@"uid",@"name"];


/// Get the value with key paths from dictionary
/// The dic should be NSDictionary, and the keyPath should not be nil.
static force_inline id YYValueForKeyPath(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *keyPaths) {
    id value = nil;
    for (NSUInteger i = 0, max = keyPaths.count; i < max; i++) {
        value = dic[keyPaths[i]];
        if (i + 1 < max) {
            if ([value isKindOfClass:[NSDictionary class]]) {
                dic = value;
            } else {
                return nil;
            }
        }
    }
    return value;
}

/// Get the value with multi key (or key path) from dictionary
/// The dic should be NSDictionary
static force_inline id YYValueForMultiKeys(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *multiKeys) {
    id value = nil;
    for (NSString *key in multiKeys) {
        if ([key isKindOfClass:[NSString class]]) {
            value = dic[key];
            if (value) break;
        } else {
            value = YYValueForKeyPath(dic, (NSArray *)key);
            if (value) break;
        }
    }
    return value;
}

----------- 分隔线 -------------

接下来 分析_YYModelPropertyMeta 这个类 ,这个类在本文件中是一个私有类

有一个@package 需要注意一下,

@protected 该类和所有子类中的方法可以直接访问这样的变量。
@private 该类中的方法可以访问,子类不可以访问。
@public   可以被所有的类访问
@package 本包内使用,跨包不可以

 1 /// A property info in object model.
 2 @interface _YYModelPropertyMeta : NSObject {
 3     @package
 4     NSString *_name;             ///< property‘s name
 5     YYEncodingType _type;        ///< property‘s type
 6     YYEncodingNSType _nsType;    ///< property‘s Foundation type
 7     BOOL _isCNumber;             ///< is c number type
 8     Class _cls;                  ///< property‘s class, or nil
 9     Class _genericCls;           ///< container‘s generic class, or nil if threr‘s no generic class
10     SEL _getter;                 ///< getter, or nil if the instances cannot respond
11     SEL _setter;                 ///< setter, or nil if the instances cannot respond
12     BOOL _isKVCCompatible;       ///< YES if it can access with key-value coding
13     BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver
14     BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary:
15
16     /*
17      property->key:       _mappedToKey:key     _mappedToKeyPath:nil            _mappedToKeyArray:nil
18      property->keyPath:   _mappedToKey:keyPath _mappedToKeyPath:keyPath(array) _mappedToKeyArray:nil
19      property->keys:      _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath    _mappedToKeyArray:keys(array)
20      */
21     NSString *_mappedToKey;      ///< the key mapped to
22     NSArray *_mappedToKeyPath;   ///< the key path mapped to (nil if the name is not key path)
23     NSArray *_mappedToKeyArray;  ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys)
24     YYClassPropertyInfo *_info;  ///< property‘s info
25     _YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.
26 }
27 @end

这个私有的类中包含了 一条@property 的全部信息,我们可以了解到一条@property 其实是包含很多信息的

1. _name    ------> 名称

2. _type  -------> @encode()编码 转换后的类型 详见 YYModel源码解读 (二) 之 YYClassInfo.h(1)

3. _nsType ------> 自定义的Foundation 类型

4. _isCNumber -------- > 类型是否是c类型

5.  _cls   -------->  property 的类型

6.  _genericCls  ---------> 这个是容器的类型, 比如: NSArray  NSSet  NSDictionary

7.  _getter  -------- >  getter 方法 (可能为空)

8.  _setter   ---------> setter 方法(可能为空)

9.

时间: 2024-08-28 11:59:57

YYModel 源码解读(二)之NSObject+YYModel.h (1)的相关文章

(转)go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin

转自:http://www.baiyuxiong.com/?p=886 ----------------------------------------------------------------------- 上一篇go语言nsq源码解读-基本介绍  介绍了最基本的nsq环境搭建及使用.在最后使用时,我们用到了几个命令:nsqlookupd.nsqd.nsqadmin.curl及 nsq_to_file,并看到用curl命令写入的几个”hello world”被nsq_to_file命令保

YYModel 源码解读(二)之YYClassInfo.h (3)

前边3篇介绍了YYClassinfo 文件的组成单元,算是功能的分割,按照业务的设计思想来说,方向应该是相反的 由此引申出我们在设计api的思想其实和项目管理是很类似的----- 一些题外话 1.目的 回到代码,首先应该明确写这个类的目的是什么? 按照正常逻辑,我们需要一个类来获取我们所需要的所有和此类相关的信息 包括(类名,父类,成员变量,方法,属性...) 2.技术调研 调研我们所需要的结果是否能够通过技术手段实现 3.目标分隔,也就是任务分解 需要把整体目标分解成小目标,在本代码中则分割成

YYModel 源码解读 总结

在使用swfit写代码的过程中,使用了下oc写的字典转模型,发现有些属性转不成功,就萌生了阅读源码的想法. 其实一直都知道Runtime机制,但并没有系统的学习,可能是因为平时的使用比较少,无意间在github上看到了YYModel ,粗略的看了下源码,发现有大量的c方面的知识,就产生了很大的兴趣,因为c总是性能的代名词吗?于是就有了这几篇文章,其实主要的目的还是对平时所学知识的一个总结. 毕竟,人类的记忆总是容易忘记的. 不知道作者在写这个框架时的思想是怎么样一个过程?但字里行间都流露出对代码

YYModel 源码解读之YYModel.h (一)

#if __has_include(<YYModel/YYModel.h>) FOUNDATION_EXPORT double YYModelVersionNumber; FOUNDATION_EXPORT const unsigned char YYModelVersionString[]; #import <YYModel/NSObject+YYModel.h> #import <YYModel/YYClassInfo.h> #else #import "

YYModel 源码解读(二)之NSObject+YYModel.h (4)

接下来我们继续向下看 typedef struct { void *modelMeta; ///< _YYModelMeta void *model; ///< id (self) void *dictionary; ///< NSDictionary (json) } ModelSetContext; 这是一个c的结构体,在c中 void * 相当于 oc 中的 id 类型 那么 为什么使用c的结构体呢,最主要的使用场景就是我们需要同时使用多个参数的情况下,可以使用c的结构体 /**

YYModel 源码解读(二)之NSObject+YYModel.h (5)

好了,之前的博文中详细的解释了一些辅助的类和辅助的函数,接下来就是使用它们来实现酷炫功能的时候,正所谓磨刀不误砍柴工啊 我们先把总的功能罗列出来 1. json转字典              + (NSDictionary *)_yy_dictionaryWithJSON:(id)json 2. json转模型              + (instancetype)yy_modelWithJSON:(id)json 3. 字典转模型              + (instancetype

YYModel 源码解读(二)之NSObject+YYModel.h (3)

本篇主要介绍的是 在真正转之前的几个辅助函数 /** Get number from property. @discussion Caller should hold strong reference to the parameters before this function returns. @param model Should not be nil. @param meta Should not be nil, meta.isCNumber should be YES, meta.get

YYModel 源码解读(二)之YYClassInfo.h (1)

1 NS_ASSUME_NONNULL_BEGIN 2 NS_ASSUME_NONNULL_END 为了兼容Swift 中的 ? 和 ! oc 在6.3引入了两个新的类型注释:__nullable和__nonnull , 在字面上很好理解 可能为空, 不为空, 在上面代码中间则表示 默认的所有的属性都不能为空,这样我们在敲码的过程中只需要手写__nullable的类型就可以了  1 /** 2 Type encoding's type. 3 */ 4 typedef NS_OPTIONS(NSU

YYModel 源码解读(二)之YYClassInfo.h (2)

1 /** 2 Instance variable information. 3 */ 4 @interface YYClassIvarInfo : NSObject 5 @property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct 6 @property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name 7 @property (