1.成员变量 Ivar 在objc/runtime.h
中,Ivar的描述如下:
1 2 3 4 5 6 7 8 9 10 11 typedef struct objc_ivar *Ivar;struct objc_ivar {char *ivar_namechar *ivar_typeint ivar_offset#ifdef __LP64__ int space#endif }
Ivar,对象的成员变量,是一个指向objc_ivar
结构体的指针,包括名字与类型。
获取成员变量 常用操作函数有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // 获取所有成员变量 class_copyIvarList// 获取成员变量名 ivar_getName// 获取成员变量类型编码 ivar_getTypeEncoding// 获取指定名称的成员变量 class_getInstanceVariable// 获取某个对象成员变量的值 object_getIvar// 设置某个对象成员变量的值 object_setIvar
2.属性 属性的定义 1 2 typedef struct objc_property *objc_property_t ;
属性,一个指向objc_property
结构体,通常以@property
关键字来声明。
属性
与成员变量
是两个独立的结构体,但两者被紧紧关联在了一起,只不过在不同版本的编译器中稍有不同。
iOS5
以前,使用属性通常需要三步:
大括号声明成员变量;
@property 声明属性;
@synthesize 手动合成属性与成员变量;
#示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @interface People () { NSString *_name; NSString *nick; }@property (nonatomic ,copy ) NSString *name;@property (nonatomic , copy ) NSString *aliasName;@end @implementation People @synthesize name = _name;@synthesize aliasName = nick; - (void )test{ self .name = @"A" ; self .aliasName = @"B" ; NSLog (@"++self.name='%@',++self.aliasName='%@',++_name='%@',++nick='%@'" ,self .name,self .aliasName,_name,nick); }@end
1.示例中{ }
里声明成员变量时可以使用下划线_
开头,也可以不带下划线;
2.@property
的作用是自动生成属性的 setter\getter 方法的声明
,即
1 2 - (void)setName:(NSString*)aName;- (NSString*)name;
3.@synthesize
的作用是指定一个成员变量
,将其绑定到属性上。然后实现属性的 getter\setter 并在其中使用此成员变量存取值;
1 2 3 4 5 6 7 8 9 10 - (NSString *)name { return _name ; } - (void)setName :(NSString *)aName{ if (aName && _name && ![aName isEqualToString:_name ]) { return; } _name = aName; }
在iOS5以后
,苹果将编译器从 GCC 转为 LLVM,以 @property 声明的属性name
,编译器会默认为其生成一个以下划线开头的成员变量
,同时实现 setter/getter 方法并在其中使用"_name"
存取值,所以编译器帮我们省去了部分操作。 在.m文件中,我们可以直接使用_name
,也可以使用self.name
。其中,后者实际上是调用了属性name
的 getter\setter 函数。
ps: iOS5之后,@synthesize
仍可以继续使用。如果@synthesize
后为属性指定的这个成员变量并不存在,那么编译器会自动帮我们创建一个新的同类型的成员变量。例如@synthesize aliasName = NotExistIvar;
,我们的{ }
中并未声明此成员变量NotExistIvar
,编译器会自动帮我们创建~
获取属性 属性的常用操作函数:
1 2 3 4 5 6 7 8 9 10 11 // 获取所有属性 class_copyPropertyList// 获取属性名 property_getName// 获取属性特性描述字符串 property_getAttributes// 获取所有属性特性 property_copyAttributeList
其中,property_getAttributes
返回的是一个objc_property_attribute_t
结构体列表:
1 2 3 4 5 typedef struct { const char *name; const char *value; } objc_property_attribute_t ;
常用的值如下:
1 2 3 4 属性类型 name值:T value:NSString 等 编码类型 name值:C(copy )、&(strong \retain )、W(weak )、R(readonly )、空(assign ) 等 value:无 非/原子性 name值:空(atomic) N(Nonatomic) value:无 属性名称 name值:V value:自定义的名字
3.查询示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @interface IvarTool : NSObject { double ivar_defaultDouble;@package int ivar_packInt;@public float ivar_publicFloat;@protected NSString *ivar_protectedStr;@private NSDictionary *ivar_privateDic; }@property (nonatomic ,copy ) NSString *property_string; + (void )ivarList; + (void )propertyList;@end
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #import "IvarTool.h" #import <objc/runtime.h> @interface IvarTool () { int ivar_int;@public NSArray *ivar_array; }@property (nonatomic , strong ) NSDictionary *property_dic;@end @implementation IvarTool + (void )ivarList { unsigned int count = 0 ; Ivar *ivars = class_copyIvarList([IvarTool class ], &count); for (int i = 0 ; i<count; i++) { Ivar ivar = ivars[i]; const char *charName = ivar_getName(ivar); const char *charType = ivar_getTypeEncoding(ivar); NSLog (@"Name:%s,Type:%s" ,charName,charType); } free(ivars); } + (void )propertyList { unsigned int count = 0 ; objc_property_t *properties = class_copyPropertyList([IvarTool class ], &count); for (int i = 0 ; i<count; i++) { objc_property_t aProperty = properties[i]; const char *charName = property_getName(aProperty); const char *charType = property_getAttributes(aProperty); NSLog (@"Name:%s,Type:%s" ,charName,charType); } free(properties); }@end
调用[IvarTool ivarList]
,查看成员变量,输出日志如下:
1 2 3 4 5 6 7 8 9 Name :ivar_defaultDouble,Type :dName :ivar_packInt,Type :iName :ivar_publicFloat,Type :fName :ivar_protectedStr,Type :@"NSString"Name :ivar_privateDic,Type :@"NSDictionary"Name :ivar_int,Type :iName :ivar_array,Type :@"NSArray"Name :_property_string,Type :@"NSString"Name :_property_dic,Type :@"NSDictionary"
调用[IvarTool propertyList]
,查看属性列表,输出日志如下:
1 2 Na me:pr operty_dic,Type:T @"NSDictionary" ,&,N ,V_property_dicNa me:pr operty_string,Type:T @"NSString" ,C,N ,V_property_string
小结 :
编译器会自动为属性生成对应的以下划线开头的成员变量;
无论成员变量的访问权限如何,是在.h 还是 .m 中声明的,class_copyIvarList 都能获取的到;
无论属性定义在 .h 还是 .m 中,class_copyPropertyList 能获取全部属性;
class_copyIvarList 与 class_copyPropertyList 都只能获取到当前类中定义的成员变量或属性,获取不到其父类的中定义的;
4.访问权限 关于成员变量的访问权限,Apple官方 描述如下:
@public: 导入头文件的地方,都能直接访问(.m
文件中声明的成员变量除外);
@protected: 本类及其子类实例方法中可见;
@private: 只对本类可见,对其子类不可见;
@package: 比较特殊,访问范围介于public
和private
之间,只要处于同一个镜像或可执行文件中时相当于public
,在实现其的镜像或可执行文件之外时相当于private
,下面是Apple原文中的描述;
Using the modern runtime, an @package instance variable has @public scope inside the executable image that implements the class, but acts like @private outside. The @package scope for Objective-C instance variables is analogous to private_extern for C variables and functions. Any code outside the class implementation’s image that tries to use the instance variable gets a link error. This scope is most useful for instance variables in framework classes, where @private may be too restrictive but @protected or @public too permissive.
默认值:
.h
中默认为 @protected;
.m
中默认为 @private;
#示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { IvarTool *tool = [[IvarTool alloc] init]; tool -> ivar_defaultDouble; tool -> ivar_protectedStr; tool -> ivar_privateDic; tool -> ivar_int; tool -> ivar_array; tool.property_dic; tool -> ivar_packInt; tool -> ivar_publicFloat; tool.property_string; return YES; }
若想实现成员变量支持外部访问,需要在头文件的声明中给该成员变量添加@public
修饰符,同时在外部访问时使用->
。m文件内即使成员变量添加@public
修饰符,外部访问时仍会报错,因为它只对m文件内可见。
5.快速序列化 原理:利用runtime
提供的函数,遍历model
的属性,并对属性进行decode
与encode
。
#示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 //ModelSerialiseHelper.h #ifndef ModelSerialiseHelper_h #define ModelSerialiseHelper_h #import <objc/runtime.h> /* 序列化工具使用示例: 1 、TLObject头文件中声明NSCoding协议 @interface TLObject : NSObject<NSCoding> @property (nonatomic, copy) NSString *mName @property (nonatomic) BOOL mIsTrue @property (nonatomic) NSInteger mInteger @property (nonatomic) UIImage *mImage @end 2 、m文件中导入头文件和宏即可 #import "TLObject.h" #import "ModelSerialiseHelper.h" @implementation TLObject ModelCodingProtocol() @end */ #define ModelCodingProtocol()\ - (id)initWithCoder:(NSCoder *)coder\ {\ Class cls = [self class] while (cls != [NSObject class])\ {\ unsigned int count = 0 Ivar *ivarList = class_copyIvarList([cls class], &count) for (int i = 0 {\ const char *varName = ivar_getName(*(ivarList + i)) NSString *key = [NSString stringWithUTF8 String:varName] NSString *aStr = [key substringToIndex:1 ] if ([aStr isEqualToString:@"_" ]) {\ key = [key substringFromIndex:1 ] }\ id varValue = [coder decodeObjectForKey:key] if (varValue) {\ [self setValue:varValue forKey:key] }\ }\ free (ivarList) cls = class_getSuperclass(cls) }\ return self }\ - (void)encodeWithCoder:(NSCoder *)coder\ {\ Class cls = [self class] while (cls != [NSObject class])\ {\ unsigned int count = 0 Ivar *ivarList = class_copyIvarList([cls class], &count) for (int i = 0 {\ const char *varName = ivar_getName(*(ivarList + i)) NSString *key = [NSString stringWithUTF8 String:varName] NSString *aStr = [key substringToIndex:1 ] if ([aStr isEqualToString:@"_" ]) {\ key = [key substringFromIndex:1 ] }\ id varValue = [self valueForKey:key] if (varValue) {\ [coder encodeObject:varValue forKey:key] }\ }\ free (ivarList) cls = class_getSuperclass(cls) }\ }\ #define ModelCopyProtocol()\ - (id)copyWithZone:(NSZone *)zone\ {\ id copy = [[[self class] allocWithZone:zone] init] Class cls = [self class] while (cls != [NSObject class])\ {\ unsigned int count = 0 Ivar *ivarList = class_copyIvarList([cls class], &count) for (int i = 0 {\ const char *varName = ivar_getName(*(ivarList + i)) NSString *key = [NSString stringWithUTF8 String:varName] NSString *aStr = [key substringToIndex:1 ] if ([aStr isEqualToString:@"_" ]) {\ key = [key substringFromIndex:1 ] }\ id varValue = [self valueForKey:key] if (varValue) {\ [copy setValue:varValue forKey:key] }\ }\ free (ivarList) cls = class_getSuperclass(cls) }\ return copy }\ #endif /* ModelSerialiseHelper_h */
上述代码为ModelSerialiseHelper.h
,其中:
ModelCopyProtocol() 封装了copy协议的相关实现代码;
ModelCodingProtocol() 封装了encode和decode的相关代码;
在需要实现这两个协议的model
中,导入ModelSerialiseHelper.h
并引用上述两个宏定义即可。
6.json字典转Model 原理:获取模型的属性列表
,通过kvc
从字典中取值并赋值给对应的属性字段。
#示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #import "NSObject+JsonToModel.h" #import <objc/runtime.h> @implementation NSObject (JsonToModel ) - (instancetype )initModelWithDictionary:(NSDictionary *)jsonDic { if (self = [self init]) { NSMutableArray * keysArr; if (!jsonDic) { return nil ; } keysArr = [NSMutableArray array]; unsigned int count; objc_property_t * properties = class_copyPropertyList([self class ], &count); for (int i = 0 ; i < count; i++) { objc_property_t property = properties[i]; const char *charName = property_getName(property); NSString *propertyName = [NSString stringWithCString:charName encoding:NSUTF8StringEncoding ]; if (propertyName.length) { [keysArr addObject:propertyName]; } } free(properties); for (NSString * key in keysArr) { [self setValue:jsonDic[key] forKey:key]; } } return self ; }@end
调用示例:
1 2 NSDictionary * dic = @{ @"name": @"xxx", @"sex": @(1), @"age": @(20)}; PersonModel * person = [[PersonModel alloc] initModelWithDictionary:dic];
这里的示例只是一个简单的场景,实际应用中可能会有更复杂的需求,视情况做响应修改即可。
相关参考:
#©Apple