YYModel 源码解读之YYClassInfo.h
Posted 马在路上
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了YYModel 源码解读之YYClassInfo.h相关的知识,希望对你有一定的参考价值。
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 (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar‘s offset 8 @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar‘s type encoding 9 @property (nonatomic, assign, readonly) YYEncodingType type; ///< Ivar‘s type 10 11 /** 12 Creates and returns an ivar info object. 13 14 @param ivar ivar opaque struct 15 @return A new object, or nil if an error occurs. 16 */ 17 - (instancetype)initWithIvar:(Ivar)ivar; 18 @end
上边代码通过创建Ivar(成员变量)的 抽象类, 返回我们需要的关于Ivar 的信息,
通过一个初始化方法创建,接下来我们看看该方法的具体实现
1 - (instancetype)initWithIvar:(Ivar)ivar { 2 // 初始化判空 如果为空 就返回nil 3 if (!ivar) return nil; 4 self = [super init]; 5 _ivar = ivar; 6 7 // 获取成员变量的名称 8 const char *name = ivar_getName(ivar); 9 if (name) { 10 11 // 把c的字符串转化成oc的字符串 12 _name = [NSString stringWithUTF8String:name]; 13 } 14 15 _offset = ivar_getOffset(ivar); 16 17 // 获取类型编码 18 const char *typeEncoding = ivar_getTypeEncoding(ivar); 19 if (typeEncoding) { 20 21 // 转为oc的字符穿 22 _typeEncoding = [NSString stringWithUTF8String:typeEncoding]; 23 24 // 转成枚举值 25 _type = YYEncodingGetType(typeEncoding); 26 27 } 28 return self; 29 }
ivar_getName
ivar_getTypeEncoding
ivar_getOffset
这三个方法都是运行时方法,分别用来获取 名称 , 类型编码 , 偏移量 尤其要之处的是
ivar_getOffset方法: 官方文档中的描述是这样的
1 Returns the offset of an instance variable. 2 3 Declaration 4 ptrdiff_t ivar_getOffset( Ivar ivar) 5 Discussion 6 For instance variables of type id or other object types, call object_getIvar and object_setIvar instead of using this offset to access the instance variable data directly.
ivar_getOffset函数,对于类型id或其它对象类型的实例变量,可以调用object_getIvar和object_setIvar来直接访问成员变量,而不使用偏移量。
接下来我们看看Method(方法)的 抽象类
1 /** 2 Method information. 3 */ 4 @interface YYClassMethodInfo : NSObject 5 @property (nonatomic, assign, readonly) Method method; ///< method opaque struct 6 @property (nonatomic, strong, readonly) NSString *name; ///< method name 7 @property (nonatomic, assign, readonly) SEL sel; ///< method‘s selector 8 @property (nonatomic, assign, readonly) IMP imp; ///< method‘s implementation 9 @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method‘s parameter and return types 10 @property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value‘s type 11 @property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *argumentTypeEncodings; ///< array of arguments‘ type 12 13 /** 14 Creates and returns a method info object. 15 16 @param method method opaque struct 17 @return A new object, or nil if an error occurs. 18 */ 19 - (instancetype)initWithMethod:(Method)method; 20 @end
这这段代码中 比较陌生的是Method 和 IMP
Method 是一个结构体:
struct objc_method { SEL method_name; char * method_types; IMP method_imp; };
1.方法名:方法名为此方法的方法签名,相同函数名和参数的方法名是一样的
2.方法类型: 描述方法的参数类型
3. 方法真实实现代码块的地址指针,可像C 一样直接调用
1 - (instancetype)initWithMethod:(Method)method { 2 if (!method) return nil; 3 self = [super init]; 4 _method = method; 5 6 // Method获取方法的名称 7 _sel = method_getName(method); 8 9 // 方法的实现地址 10 _imp = method_getImplementation(method); 11 12 // SEL 获取方法名 13 const char *name = sel_getName(_sel); 14 if (name) { 15 _name = [NSString stringWithUTF8String:name]; 16 } 17 18 // 获取类型 19 const char *typeEncoding = method_getTypeEncoding(method); 20 if (typeEncoding) { 21 _typeEncoding = [NSString stringWithUTF8String:typeEncoding]; 22 } 23 24 // 获取返回值类型 25 char *returnType = method_copyReturnType(method); 26 if (returnType) { 27 _returnTypeEncoding = [NSString stringWithUTF8String:returnType]; 28 29 // 但凡 通过copy retain alloc 系统方法得到的内存,必须使用relea() 或 free() 进行释放 30 free(returnType); 31 } 32 33 // 获取参数列表 34 unsigned int argumentCount = method_getNumberOfArguments(method); 35 if (argumentCount > 0) { 36 37 NSMutableArray *argumentTypes = [NSMutableArray new]; 38 39 for (unsigned int i = 0; i < argumentCount; i++) { 40 41 // 获取参数中的某一个参数 42 char *argumentType = method_copyArgumentType(method, i); 43 44 NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil; 45 [argumentTypes addObject:type ? type : @""]; 46 if (argumentType) free(argumentType); 47 } 48 _argumentTypeEncodings = argumentTypes; 49 } 50 return self; 51 }
上边的代码使用了运行时中 关于Method 的一些方法,再次不做介绍,但值得注意的是
但凡 通过copy retain alloc 系统方法得到的内存,必须使用relea() 或 free() 进行释放
1 /** 2 Property information. 3 */ 4 @interface YYClassPropertyInfo : NSObject 5 @property (nonatomic, assign, readonly) objc_property_t property; ///< property‘s opaque struct 6 @property (nonatomic, strong, readonly) NSString *name; ///< property‘s name 7 @property (nonatomic, assign, readonly) YYEncodingType type; ///< property‘s type 8 @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< property‘s encoding value 9 @property (nonatomic, strong, readonly) NSString *ivarName; ///< property‘s ivar name 10 @property (nullable, nonatomic, assign, readonly) Class cls; ///< may be nil 11 @property (nonatomic, assign, readonly) SEL getter; ///< getter (nonnull) 12 @property (nonatomic, assign, readonly) SEL setter; ///< setter (nonnull) 13 14 /** 15 Creates and returns a property info object. 16 17 @param property property opaque struct 18 @return A new object, or nil if an error occurs. 19 */ 20 - (instancetype)initWithProperty:(objc_property_t)property;
上边的类是对属性的抽象类,让我们通过下边的代码 了解下属性编码的知识
1 objc_property_t property = class_getProperty([YYWeiboStatus class], "user"); 2 3 unsigned int num; 4 objc_property_attribute_t *attr = property_copyAttributeList(property, &num); 5 for (unsigned int i = 0; i < num; i++) { 6 7 objc_property_attribute_t att = attr[i]; 8 fprintf(stdout, "name = %s , value = %s \n",att.name , att.value); 9 } 10 11 const char *chars = property_getAttributes(property); 12 fprintf(stdout, "%s \n",chars);
打印的输出结果为
1 name = T , value = @"YYWeiboUser" 2 name = & , value = 3 name = N , value = 4 name = V , value = _user 5 T@"YYWeiboUser",&,N,V_user
可以看出,比较重要的是属性的编码都是以T开头 标示属性的类型 以V开头 标示属性的变量名
1 - (instancetype)initWithProperty:(objc_property_t)property { 2 if (!property) return nil; 3 self = [self init]; 4 5 _property = property; 6 7 // 1. 获取属性名称 8 const char *name = property_getName(property); 9 if (name) { 10 _name = [NSString stringWithUTF8String:name]; 11 } 12 13 // 2.获取每一个属性的编码字符串 14 YYEncodingType type = 0; 15 unsigned int attrCount; 16 objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount); 17 18 // 3. 编译每一个属性的 objc_property_attribute_t 19 for (unsigned int i = 0; i < attrCount; i++) { 20 21 // 3.1 根据objc_property_attribute_t 中的name 做一些事 22 switch (attrs[i].name[0]) { 23 24 // T 代码属性的类型编码 25 case ‘T‘: { // Type encoding 26 if (attrs[i].value) { 27 _typeEncoding = [NSString stringWithUTF8String:attrs[i].value]; 28 type = YYEncodingGetType(attrs[i].value); 29 30 // 计算属性的实体类型 比如:@"User" 31 if ((type & YYEncodingTypeMask) == YYEncodingTypeObject) { 32 33 size_t len = strlen(attrs[i].value); // len = 7 34 if (len > 3) { 35 char name[len - 2]; // 新建一个长度 = len - 2 的name字符数组 长度为5 36 name[len - 3] = ‘\0‘; // 设置最后一个字符为\0 37 memcpy(name, attrs[i].value + 2, len - 3); // copy USer 到name 中, 38 39 // 获取name 的真实实体类型 40 _cls = objc_getClass(name); 41 } 42 } 43 } 44 } break; 45 case ‘V‘: { // Instance variable 46 if (attrs[i].value) { 47 _ivarName = [NSString stringWithUTF8String:attrs[i].value]; 48 } 49 } break; 50 case ‘R‘: { 51 type |= YYEncodingTypePropertyReadonly; 52 } break; 53 case ‘C‘: { 54 type |= YYEncodingTypePropertyCopy; 55 } break; 56 case ‘&‘: { 57 type |= YYEncodingTypePropertyRetain; 58 } break; 59 case ‘N‘: { 60 type |= YYEncodingTypePropertyNonatomic; 61 } break; 62 case ‘D‘: { 63 type |= YYEncodingTypePropertyDynamic; 64 } break; 65 case ‘W‘: { 66 type |= YYEncodingTypePropertyWeak; 67 } break; 68 case ‘G‘: { // getter 方法 69 type |= YYEncodingTypePropertyCustomGetter; 70 if (attrs[i].value) { 71 _getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]); 72 } 73 } break; 74 case ‘S‘: { // setter 方法 75 type |= YYEncodingTypePropertyCustomSetter; 76 if (attrs[i].value) { 77 _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]); 78 } 79 } // break; commented for code coverage in next line 80 default: break; 81 } 82 } 83 if (attrs) { 84 free(attrs); 85 attrs = NULL; 86 } 87 88 _type = type; 89 90 // 获取setter 和 getter 方法 91 if (_name.length) { 92 if (!_getter) { 93 _getter = NSSelectorFromString(_name); 94 } 95 if (!_setter) { 96 _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]); 97 } 98 } 99 return self; 100 }
最后让我来看看打印结果
1 objc_property_t property = class_getProperty([YYWeiboStatus class], "user"); 2 3 YYClassPropertyInfo *propertyInfo = [[YYClassPropertyInfo alloc] initWithProperty:property]; 4 5 NSLog(@"%@",propertyInfo.typeEncoding);
1 2016-05-23 16:00:12.134 ModelBenchmark[12626:245778] @"YYWeiboUser"
1 typedef struct example { 2 int* aPint; 3 double aDouble; 4 char *aString; 5 int anInt; 6 BOOL isMan; 7 struct example *next; 8 } Example;
1 @property (nonatomic, assign) Example example;
1 2016-05-23 16:04:59.493 ModelBenchmark[12684:250528] {example=^id*iB^{example}}
1 objc_property_t property = class_getProperty([YYWeiboStatus class], "statusID"); 2 3 YYClassPropertyInfo *propertyInfo = [[YYClassPropertyInfo alloc] initWithProperty:property]; 4 5 NSLog(@"%@",propertyInfo.typeEncoding);
1 2016-05-23 16:06:39.747 ModelBenchmark[12696:252148] Q
到此 关于 Ivar Method Property 的抽象类已经介绍完毕,在后面的使用中 直接使用这些抽象类来进行编码的
以上是关于YYModel 源码解读之YYClassInfo.h的主要内容,如果未能解决你的问题,请参考以下文章
YYModel 源码解读之NSObject+YYModel.h