YYModel学习笔记(一)

作者: GiantAxe77 | 来源:发表于2016-12-02 15:42 被阅读419次

    YYModel.h文件

    yymodel.h.png

    注意点:

    • 用#if __has_include( )判断是否包含某个类.
    • FOUNDATION_EXPORT 和 #define 作用是一样的, 前者表示如下:
    FOUNDATION_EXPORT.png

    这就说明FOUNDATION_EXPORT 和 extern 是一样的, 而且, FOUNDATION_EXPORT 比较的是指针地址,效率更高.
    用法如下:
    在.h文件中:

    FOUNDATION_EXPORT NSString * const kMyConstantString;  
    

    在.m文件中:

    NSString * const kMyConstantString = @"gw";
    
    • 判断项目中是否导入头文件,如果导入,则再去当前目录,没有的话,就导入下面的类.

    NSObject+YYModel.h

    • 内容太多就不列举图片了

    注意点:

    NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END

    • 打开NSObject+YYModel.h, 映入眼帘的就是NS_ASSUME_NONNULL_BEGIN, 最后还有一个NS_ASSUME_NONNULL_END.
      说明下: 如果需要每个属性或每个方法都去指定nonnull和nullable,是一件非常繁琐的事。苹果为了减轻我们的工作量,专门提供了两个宏:NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END。在这两个宏之间的代码,所有简单指针对象都被假定为nonnull,因此我们只需要去指定那些nullable的指针。

    nil Nil NULL NSNull kCFNull 的区别

    • nil 代表指向一个实例对象的空指针:
    NSString *msg = nil;
    
    • Nil表示指向一个类的空指针:
    Class class  = Nil;
    
    • NULL定义其他类型(基本类型、C类型)的空指针
    char *p = NULL;
    
    • NSNull 表示数组中的占位符,数组中的元素不能为nil, nil代表数组结束的标志, 所以为空的话可以用NSNull来占位

    • kCFNull : NSNull的单例
      CF_EXPORT 就是 extern


      kCFNull.png

    先介绍下辅助函数和枚举变量:

    force_inline

    #define force_inline __inline__ __attribute__((always_inline))
    

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

    • 内联函数: 是用inline修饰的函数,内联函数在代码层次看和普通的函数结构一样,却不具备函数的性质,内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入到调用处,和宏很类似,但是有一下不同点. 他们之间的区别在于: 宏是由预处理器进行替代的,而内联函数则是通过编译器控制实现的,内联函数是真正的函数,只有在需要用到的时候才会像宏那样的展开,所以取消了函数的参数压栈,减少了调用的开销,我们可以像调用函数一样调用内联函数,而不必担心产生于处理宏一样的问题.
    • __inlineinline等同。inline__inline通知编译器将该函数的内容拷贝一份放在调用函数的地方,这称之为内联。内联减少了函数调用的开销,但却增加了代码量. __attribute__((always_inline))的意思是强制内联,所有加了__attribute__((always_inline))的函数再被调用时不会被编译成函数调用而是直接扩展到调用函数体内.

    yy_modelWithJSON

    + (nullable instancetype)yy_modelWithJSON:(id)json;
    
    • 这个方法是这个类里的第一个方法,传入Dictionary, NSString或者NSData格式的返回一个新的实例变量.底层调用了_yy_dictionaryWithJSON方法,_yy_dictionaryWithJSON会把根据传入的json对象做判断,如果是字典就直接赋值,不是字典的根据NSData转换成字典赋值,在这里作者进行了容错处理,不是字典类型的都赋值为空.

    下面看.m源码

    Foundation Class Type 的封装

    Foundation Class Type.png Foundation Class封装.png
    YYEncodingTypeNSDecimalNumber是科学计数的类型.

    判断是否是一个C的类型

    c number

    从一个id类型里解析NSNumber

    
    /// 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, ^{
            // 这里的range对应的是字符编码的数字
            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;
        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);
                // 如果num不存在或无穷大 返回nil
                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;
    }
    

    将String转换为NSDate

    /// Parse string to date.
    static force_inline NSDate *YYNSDateFromString(__unsafe_unretained NSString *string) {
        typedef NSDate* (^YYNSDateParseBlock)(NSString *string);
        #define kParserNum 34
        // blocks是个数组
        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";
                // 转换后的结果保存在数组中, 10代表日期格式有多少位,就保存在数组中的哪一位
                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
    }
    

    Get the 'NSBlock' class

    /// 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]) {
                cls = class_getSuperclass(cls);
            }
        });
        return cls; // current is "NSBlock"
    }
    
    • 这里注意下: block有三种形式的: NSStackBlock, NSGlobalBlock, NSMallocBlock.
    • NSStackBlock表示在block内部引用外部变量的, NSGlobalBlock表示在block内部没有引用外部变量的, NSMallocBlock表示堆block,MRC下引用外部变量的需要手动copy到堆上,ARC下系统自动copy.
    • block在ARC下也是有NSStackBlock的block的,只是当赋值给strong对象时,系统会主动对其进行copy.

    获取标准的时间的格式

    /**
     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 paths 或者 key 获取字典中的value

    /// 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

    • 注意: _YYModelPropertyMeta这个类在本文件中是个私有类.
    • 补充说明:@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
    
    @implementation _YYModelPropertyMeta
    + (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
        
        // support pseudo generic class with protocol name
        // 泛型类不存在且有遵守的协议,支持带协议名的伪泛型类
        if (!generic && propertyInfo.protocols) {
            for (NSString *protocol in propertyInfo.protocols) {
                Class cls = objc_getClass(protocol.UTF8String);
                if (cls) {
                    generic = cls;
                    break;
                }
            }
        }
        
        // 这里用->不能用. 没有set/get方法
        _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;
            });
            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;
    }
    @end
    

    相关文章

      网友评论

        本文标题:YYModel学习笔记(一)

        本文链接:https://www.haomeiwen.com/subject/vaaapttx.html