博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS-动态添加属性
阅读量:4135 次
发布时间:2019-05-25

本文共 6302 字,大约阅读时间需要 21 分钟。

前一章介绍了,这章主要讲动态添加属性。

一、动态添加实例变量Ivar:

通过函数class_addIvar()添加属性,更准确的说是添加成员变量,函数定义如下

OBJC_EXPORT BOOLclass_addIvar(Class _Nullable cls, const char * _Nonnull name, size_t size,               uint8_t alignment, const char * _Nullable types)     OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

使用该方法需要注意:

1、该函数只有在动态添加的类中使用有效,且只能在objc_allocatelasspair()之后和objc_registerClassPair()之前调用,在已经存在的类中使用该函数无效;
2、参数cls类不能是元类,不支持向元类添加实例变量;
3、如果成功添加了实例变量,返回YES,否则返回NO(例如,该类已包含具有该名称的实例变量)。
4、Ivarinstance variable简写,意为实例变量,也就是我们通常说的成员变量,它和@property属性不同,它没有自己的settergetter方法。

举个例子,动态创建一个Person类,通过函数class_addIvar()为其动态添加成员变量:

Class Person = objc_allocateClassPair(NSObject.class, "Person", 0);    class_addIvar(Person, "_name", sizeof(NSString *), log2(sizeof(NSString *)), "@");    class_addIvar(Person, "_dict", sizeof(NSDictionary *), log2(sizeof(NSDictionary *)), "@");    objc_registerClassPair(Person);

给成员变量赋值:先通过函数class_getInstanceVariable()获取成员变量Ivar,然后通过函数object_setIvar()给成员变量赋值,代码如下:

id person = Person.new; // 相当于调用 alloc 和 init 方法    Ivar ivar1 = class_getInstanceVariable(Person, "_name");    Ivar ivar2 = class_getInstanceVariable(Person, "_dict");        NSString *str = @"小玉子";    NSDictionary *dic = @{
@"height":@"183cm"}; object_setIvar(person, ivar1, [str copy]); object_setIvar(person, ivar2, [dic copy]);

通过函数object_getIvar()获取成员变量的值,代码如下:

id value1 = object_getIvar(person, ivar1);    id value2 = object_getIvar(person, ivar2);        NSLog(@"%@",value1);    NSLog(@"%@",value2);

输出结果:

小玉子{
height = 183cm;}

我们来遍历一下Person类中的成员变量和属性,通过函数class_copyIvarList()获取成员变量列表,通过函数class_copyPropertyList()获取类的property属性列表,代码如下:

unsigned int ivarCount = 0;    Ivar *ivars = class_copyIvarList(Person, &ivarCount);    for (int i = 0; i < ivarCount; i ++) {
Ivar ivar = ivars[i]; NSLog(@"名字:%s----类型:%s",ivar_getName(ivar),ivar_getTypeEncoding(ivar)); } free(ivars); unsigned int propertyCount; objc_property_t *properties = class_copyPropertyList(Person, &propertyCount); for (int i = 0; i < propertyCount; i++) {
objc_property_t property = properties[i]; NSLog(@"名字:%s----属性:%s",property_getName(property),property_getAttributes(property)); } free(properties);

打印结果:

名字:_name----类型:@名字:_dict----类型:@

@,显然,Person类的成员变量列表中存在我们刚刚动态添加的两个成员变量_name和_dict,而property属性列表却没有值。添加成员变量Ivar其实就相当于我们通常写的下面代码:

@interface Person : NSObject{
NSString *_name;}// @property (nonatomic, copy) NSString *name;@end

对于动态创建的类我们通过函数class_addIvar()添加实例变量, 它会改变一个已有类的内存布局,一般是通过objc_allocateClassPair()动态创建一个class,才能调用函数class_addIvar()创建Ivar,最后通过函数objc_registerClassPair()注册class。而对于已经存在的类我们用函数class_addProperty()方法来添加属性,下面我们动态添加property属性对比一下。

二、动态添加property属性:

动态添加property属性,必须是已经存在的类,不同于动态添加Ivar。在添加property属性之前我们先创建一个Person类:

@interface Person : NSObject// 静态添加property属性 grade@property (nonatomic, copy) NSString *grade;@end

这里我们要先知道@propertyIvar的区别,当我们添加一个property属性的时候,系统会自动为我们生成属性的setter、getter方法,还会生成一个带有下划线的成员变量也就是Ivar,所以如果要动态添加property属性,就要满足三点:属性的setter、getter方法和一个对应的Ivar。看下面一段代码:

- (void)addPropertyWithPropertyName:(NSString *)propertyName{
Class pClass = Person.class; // type(举例NSString类型) objc_property_attribute_t type = {
"T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([NSString class])] UTF8String] }; // C = copy objc_property_attribute_t ownership0 = {
"C", "" }; // N = nonatomic objc_property_attribute_t ownership = {
"N", "" }; // instance variable name objc_property_attribute_t backingivar = {
"V", [[NSString stringWithFormat:@"_%@", propertyName] UTF8String] }; // 对比系统生成的顺序,例如我们静态添加的grade属性 objc_property_attribute_t attrs[] = {
type, ownership0, ownership, backingivar}; BOOL isProperty = class_addProperty(pClass, [propertyName UTF8String], attrs, 4); if (!isProperty) {
// 添加属性失败,替换属性 class_replaceProperty(pClass, [propertyName UTF8String], attrs, 4); } //添加get和set方法 class_addMethod(pClass, NSSelectorFromString(propertyName), (IMP)getter, "@@:"); class_addMethod(pClass, NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]), (IMP)setter, "v@:@"); }id getter(id self, SEL _cmd) {
NSString *key = [NSString stringWithFormat:@"_%@",NSStringFromSelector(_cmd)]; Ivar ivar = class_getInstanceVariable([self class], key.UTF8String); id value = object_getIvar(self, ivar); return value;}void setter(id self, SEL _cmd, id newValue) {
//移除set NSString *key = [NSStringFromSelector(_cmd) stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:@""]; //首字母小写(不支持开发者故意把属性名称首字母大写的可能) NSString *head = [key substringWithRange:NSMakeRange(0, 1)]; head = [head lowercaseString]; key = [key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:head]; //移除后缀 ":" key = [key stringByReplacingCharactersInRange:NSMakeRange(key.length - 1, 1) withString:@""]; Ivar ivar = class_getInstanceVariable([self class], [NSString stringWithFormat:@"_%@",key].UTF8String); object_setIvar(self, ivar, newValue);}

添加操作

[self addPropertyWithPropertyName:@"name"];Person *p = Person.new;// KVC 赋值[p setValue:@"小玉子" forKey:@"name"]; NSLog(@"%@",[p valueForKey:@"name"]);

打印结果:

(null)

通过上述代码,我们为Person类添加了一个名为name的property属性,然后为其手动添加了setter和getter方法,然后用KVC的方式来给属性赋值(使用点语法或者成员变量赋值,Person对象识别不了),然而打印结果属性name的值却为空。具体原因下面说,我们先来遍历一下Person类的property属性列表和Ivar实例变量列表,结果如下:

Ivar 名字:_grade----类型:@"NSString"Property 名字:name----属性:T@"NSString",C,N,V_nameProperty 名字:grade----属性:T@"NSString",C,N,V_grade

打印分析:对于我们为Person类静态添加的grade属性,系统为我们自动生成一个相应的_grade的成员变量(Ivar),而对于我们动态添加的name属性,系统并没有为我们生成相应的_name成员变量;只有stter和getter方法没有成员变量我们就没办法给属性赋值,如何为其添加Ivar呢?显然class_addIvar()方法是不可以的,因为它只能为动态类添加成员变量。这里有两个方法可以保存name值的解决:1.手动为name属性添加成员变量_name

{
NSString *_name;}

再来看打印结果,属性name的值果然可以保存并获取:

小玉子
Ivar 名字:_name----类型:@"NSString"Ivar 名字:_grade----类型:@"NSString"Property 名字:name----属性:T@"NSString",C,N,V_nameProperty 名字:grade----属性:T@"NSString",C,N,V_grade

2.通过别的方式在调用setter方法时将值保存起来,然后调用getter方法时再将值取出并返回。

动态添加property属性小结:

1、动态添加property属性需要在已存在的类中添加;
2、动态添加property属性需要手动实现setter和getter方法;
3、动态添加property属性手动添加成员变量或者别的方式保存值,比较麻烦,使用不多;
4、用一个公式表示Ivar和property的区别:@property = Ivar + setter + getter

三、添加关联(Associated)属性:

该方法以前讲过。

转载地址:http://sgivi.baihongyu.com/

你可能感兴趣的文章
collect2: ld returned 1 exit status
查看>>
C#入门
查看>>
C#中ColorDialog需点两次确定才会退出的问题
查看>>
数据库
查看>>
nginx反代 499 502 bad gateway 和timeout
查看>>
linux虚拟机安装tar.gz版jdk步骤详解
查看>>
python实现100以内自然数之和,偶数之和
查看>>
python数字逆序输出及多个print输出在同一行
查看>>
苏宁产品经理面经
查看>>
百度产品经理群面
查看>>
去哪儿一面+平安科技二面+hr面+贝贝一面+二面产品面经
查看>>
element ui 弹窗在IE11中关闭时闪现问题修复
查看>>
vue 遍历对象并动态绑定在下拉列表中
查看>>
Vue动态生成el-checkbox点击无法选中的解决方法
查看>>
python __future__
查看>>
MySQL Tricks1
查看>>
python 变量作用域问题(经典坑)
查看>>
pytorch
查看>>
pytorch(三)
查看>>
ubuntu相关
查看>>