您的位置:首页 > 移动开发 > Objective-C

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

2016-06-12 17:37 573 查看
本篇文章主要介绍 _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的详细使用说明

/// Parse a number value from 'id'.
static force_inline NSNumber *YYNSNumberCreateFromID(__unsafe_unretained id value) {
static NSCharacterSet *dot;
static NSDictionary *dic;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dot = [NSCharacterSet characterSetWithRange:NSMakeRange('.', 1)];
dic = @{@"TRUE" :   @(YES),
@"True" :   @(YES),
@"true" :   @(YES),
@"FALSE" :  @(NO),
@"False" :  @(NO),
@"false" :  @(NO),
@"YES" :    @(YES),
@"Yes" :    @(YES),
@"yes" :    @(YES),
@"NO" :     @(NO),
@"No" :     @(NO),
@"no" :     @(NO),
@"NIL" :    (id)kCFNull,
@"Nil" :    (id)kCFNull,
@"nil" :    (id)kCFNull,
@"NULL" :   (id)kCFNull,
@"Null" :   (id)kCFNull,
@"null" :   (id)kCFNull,
@"(NULL)" : (id)kCFNull,
@"(Null)" : (id)kCFNull,
@"(null)" : (id)kCFNull,
@"<NULL>" : (id)kCFNull,
@"<Null>" : (id)kCFNull,
@"<null>" : (id)kCFNull};
});

// 判空
if (!value || value == (id)kCFNull) return nil;
// 如果是NSNumber 直接返回
if ([value isKindOfClass:[NSNumber class]]) return value;
// 如果是字符串
if ([value isKindOfClass:[NSString class]]) {

// 取出字典中映射的值
NSNumber *num = dic[value];
if (num) {
if (num == (id)kCFNull) return nil;
return num;
}

// 字符串中包含字符‘.’
if ([(NSString *)value rangeOfCharacterFromSet:dot].location != NSNotFound) {
const char *cstring = ((NSString *)value).UTF8String;
if (!cstring) return nil;

// 把字符串转换成浮点数
double num = atof(cstring);
if (isnan(num) || isinf(num)) return nil;
return @(num);
} else {
const char *cstring = ((NSString *)value).UTF8String;
if (!cstring) return nil;

// 返回字符串 的 长长整型
return @(atoll(cstring));
}
}

return nil;
}


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

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


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

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

/// Parse string to date.
static force_inline NSDate *YYNSDateFromString(__unsafe_unretained NSString *string) {
typedef NSDate* (^YYNSDateParseBlock)(NSString *string);
#define kParserNum 34
static YYNSDateParseBlock blocks[kParserNum + 1] = {0};
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
{
/*
2014-01-20  // Google
*/
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter.dateFormat = @"yyyy-MM-dd";
blocks[10] = ^(NSString *string) { return [formatter dateFromString:string]; };
}

{
/*
2014-01-20 12:24:48
2014-01-20T12:24:48   // Google
2014-01-20 12:24:48.000
2014-01-20T12:24:48.000
*/
NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init];
formatter1.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter1.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter1.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss";

NSDateFormatter *formatter2 = [[NSDateFormatter alloc] init];
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter2.dateFormat = @"yyyy-MM-dd HH:mm:ss";

NSDateFormatter *formatter3 = [[NSDateFormatter alloc] init];
formatter3.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter3.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter3.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS";

NSDateFormatter *formatter4 = [[NSDateFormatter alloc] init];
formatter4.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter4.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter4.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";

blocks[19] = ^(NSString *string) {
if ([string characterAtIndex:10] == 'T') {
return [formatter1 dateFromString:string];
} else {
return [formatter2 dateFromString:string];
}
};

blocks[23] = ^(NSString *string) {
if ([string characterAtIndex:10] == 'T') {
return [formatter3 dateFromString:string];
} else {
return [formatter4 dateFromString:string];
}
};
}

{
/*
2014-01-20T12:24:48Z        // Github, Apple
2014-01-20T12:24:48+0800    // Facebook
2014-01-20T12:24:48+12:00   // Google
2014-01-20T12:24:48.000Z
2014-01-20T12:24:48.000+0800
2014-01-20T12:24:48.000+12:00
*/
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";

NSDateFormatter *formatter2 = [NSDateFormatter new];
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZ";

blocks[20] = ^(NSString *string) { return [formatter dateFromString:string]; };
blocks[24] = ^(NSString *string) { return [formatter dateFromString:string]?: [formatter2 dateFromString:string]; };
blocks[25] = ^(NSString *string) { return [formatter dateFromString:string]; };
blocks[28] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
blocks[29] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
}

{
/*
Fri Sep 04 00:12:21 +0800 2015 // Weibo, Twitter
Fri Sep 04 00:12:21.000 +0800 2015
*/
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy";

NSDateFormatter *formatter2 = [NSDateFormatter new];
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.dateFormat = @"EEE MMM dd HH:mm:ss.SSS Z yyyy";

blocks[30] = ^(NSString *string) { return [formatter dateFromString:string]; };
blocks[34] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
}
});
if (!string) return nil;
if (string.length > kParserNum) return nil;
YYNSDateParseBlock parser = blocks[string.length];
if (!parser) return nil;
return parser(string);
#undef kParserNum
}


获得NSBlock 这个类 ,

/// Get the 'NSBlock' class.
static force_inline Class YYNSBlockClass() {
static Class cls;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
void (^block)(void) = ^{};
cls = ((NSObject *)block).class;
while (class_getSuperclass(cls) != [NSObject class]) {
NSLog(@"cls = %@",class_getSuperclass(cls));
cls = class_getSuperclass(cls);
}
});
return cls; // current is "NSBlock"
}


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

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

获取标准的时间的格式

/**
Get the ISO date formatter.

ISO8601 format example:
2010-07-09T16:13:30+12:00
2011-01-11T11:11:11+0000
2011-01-26T19:06:43Z

length: 20/24/25
*/
static force_inline NSDateFormatter *YYISODateFormatter() {
static NSDateFormatter *formatter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [[NSDateFormatter alloc] init];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
});
return formatter;
}


根据字典和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 本包内使用,跨包不可以

/// A property info in object model.
@interface _YYModelPropertyMeta : NSObject {
@package
NSString *_name;             ///< property's name
YYEncodingType _type;        ///< property's type
YYEncodingNSType _nsType;    ///< property's Foundation type
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
SEL _getter;                 ///< getter, or nil if the instances cannot respond
SEL _setter;                 ///< setter, or nil if the instances cannot respond
BOOL _isKVCCompatible;       ///< YES if it can access with key-value coding
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)
*/
NSString *_mappedToKey;      ///< the key mapped to
NSArray *_mappedToKeyPath;   ///< the key path mapped to (nil if the name is not key path)
NSArray *_mappedToKeyArray;  ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys)
YYClassPropertyInfo *_info;  ///< property's info
_YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.
}
@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. _isKVCCompatible ---------- > 能否被归档,属性需要实现setter 和 getter 方法

10. _isStructAvailableForKeyedArchiver ------- > 结构体类型是否支持归档

11. _hasCustomClassFromDictionary ---------> 是否实现了 +modelCustomClassForDictionary 方法 自定义了返回类型

12. _mappedToKey --------->

接下来我们看看实现,有些信息是我们在这个参数中可以直接获取的,直接赋值就好,其他的需要判断的就判断,值得注意的是,因为本类是支持归档的 所以要知道哪些是支持归档的,哪些是不支持归档的,

支持归档的结构体:

// 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}"];


不支持归档的:

long double
pointer (such as SEL/CoreFoundation object)


 + (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {

// 初始化信息
_YYModelPropertyMeta *meta = [self new];
meta->_name = propertyInfo.name;
meta->_type = propertyInfo.type;
meta->_info = propertyInfo;
meta->_genericCls = generic;

// 如果是Foundation 类型
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) {
meta->_nsType = YYClassGetNSType(propertyInfo.cls);
}
// 否则判断是不是C 类型
else {
meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);
}

// 结构体类型
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) {
/*
It seems that NSKeyedUnarchiver cannot decode NSValue except these structs:
*/
static NSSet *types = nil;
static dispatch_once_t onceToken;
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;
});

// 由此看来,ios的归档大概只支持上边的几种结构体类型
if ([types containsObject:propertyInfo.typeEncoding]) {
meta->_isStructAvailableForKeyedArchiver = YES;
}
}

meta->_cls = propertyInfo.cls;

// 判断是否实现了modelCustomClassForDictionary方法
if (generic) {
meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)];
} else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) {
meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
}

// 判断property 所属的类能否相应自身的getter 方法
if (propertyInfo.getter) {
if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) {
meta->_getter = propertyInfo.getter;
}
}
// 判断property 所属的类能否相应自身的setter 方法
if (propertyInfo.setter) {
if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) {
meta->_setter = propertyInfo.setter;
}
}

// 判断能够被归档,
if (meta->_getter && meta->_setter) {
/*
KVC invalid type:
long double pointer (such as SEL/CoreFoundation object)
*/
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: