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 (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  的抽象类已经介绍完毕,在后面的使用中 直接使用这些抽象类来进行编码的
时间: 2024-10-11 03:26:59

YYModel 源码解读(二)之YYClassInfo.h (2)的相关文章

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

本篇文章主要介绍 _YYModelPropertyMeta 前边的内容 首先先解释一下前边的辅助函数和枚举变量,在写一个功能的时候,这些辅助的东西可能不是一开始就能想出来的,应该是在后续的编码过程中 逐步添加的. #define force_inline __inline__ __attribute__((always_inline)) 这行代码用到了C语言的内联函数 内联函数: 是用inline修饰的函数,内联函数在代码层次看和普通的函数结构一样,却不具备函数的性质,内联函数不是在调用时发生控

(转)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 源码解读之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 源码解读 总结

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

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 源码解读(二)之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