RunTime运行时之动态添加属性
2018-03-05 本文已影响0人
平安喜乐698
目录
1. 方法1: 动态关联对象
添加关联对象,传nil可以移除关联对象
void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy)
获取关联对象
id objc_getAssociatedObject(id object, const void * key)
移除所有的关联对象
void objc_removeAssociatedObjects(id object)
object:
在分类中填self
key:
key与关联的对象是一一对应关系。
必须全局唯一。
通常用@selector(methodName)作为key。
value:
要关联的对象。
policy:
关联策略。有五种关联策略。
OBJC_ASSOCIATION_ASSIGN 等价于 @property(assign)。
OBJC_ASSOCIATION_RETAIN_NONATOMIC等价于 @property(strong, nonatomic)。
OBJC_ASSOCIATION_COPY_NONATOMIC等价于@property(copy, nonatomic)。
OBJC_ASSOCIATION_RETAIN等价于@property(strong,atomic)。
OBJC_ASSOCIATION_COPY等价于@property(copy, atomic)。
优点:
使用方便快速
缺点:
不能像遍历属性一样的遍历我们所有关联对象。
不能移除制定的关联对象,只能通过removeAssociatedObjects方法移除所有关联对象。
- 例
例1
@property (nonatomic, assign) NSTimeInterval timeInterVal;
- (void)setTimeInterVal:(NSTimeInterval)timeInterVal{
objc_setAssociatedObject(self, @selector(timeInterVal), @(timeInterVal), OBJC_ASSOCIATION_ASSIGN);
}
- (NSTimeInterval)timeInterVal{
return [objc_getAssociatedObject(self, _cmd) doubleValue];
}
例2
@interface Person(Name)
@property (nonatomic,copy) NSString *name;
@end
@implementation Person(Name)
static NSString *name;
- (void)setName:(NSString *)nameStr{
objc_setAssociatedObject(self,&name,nameStr,OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *) name{
return objc_getAssociatedObject(self, &name);
}
@end
- 原理
关联对象并不是存储在被关联对象本身内存中,而是存储在全局的统一的一个AssociationsManager中。
从AssociationsManager关联对象管理者中找到关联对象,然后在查找关联对象的关联属性进行读取值。
![](https://img.haomeiwen.com/i5111884/5fb02067b6809ad5.png)
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
//查看这个对象是否设置过关联
if (i != associations.end()) {
//找到了证明这个对象设置过关联
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
//查找这个key有没有对应的ObjcAssociation
if (j != refs->end()) {
//有的话直接替换成新值
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
//没有的话根据这个key创建一个新的ObjcAssociation
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// create the new association (first time).
//没有设置过根据这个disguised_object创建一个新的ObjectAssociationMap
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).
if (old_association.hasValue()) ReleaseValue()(old_association);
}
id objc_getAssociatedObject(id object, const void *key) {
return _object_get_associative_reference(object, (void *)key);
}
id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
ObjcAssociation &entry = j->second;
value = entry.value();
policy = entry.policy();
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
objc_retain(value);
}
}
}
}
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
objc_autorelease(value);
}
return value;
}
void objc_removeAssociatedObjects(id object)
{
if (object && object->hasAssociatedObjects()) {
_object_remove_assocations(object);
}
}
void _object_remove_assocations(id object) {
vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
if (associations.size() == 0) return;
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// copy all of the associations that need to be removed.
ObjectAssociationMap *refs = i->second;
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
// remove the secondary table.
delete refs;
associations.erase(i);
}
}
// the calls to releaseValue() happen outside of the lock.
for_each(elements.begin(), elements.end(), ReleaseValue());
}
方法2. 动态添加Ivar
优点:
能够通过遍历Ivar查到
缺点:
不能在已存在的class中添加Ivar。
即必须通过objc_allocateClassPair动态创建一个class,才能调用class_addIvar创建Ivar,最后通过objc_registerClassPair注册class
// 自定义方法
+ (void)addIvarWithtarget:(id)target withPropertyName:(NSString *)propertyName withValue:(id)value {
if (class_addIvar([target class], [propertyName UTF8String], sizeof(id), log2(sizeof(id)), "@")) {
YYLog(@"创建属性Ivar成功");
}
}
// 自定义方法
+ (id)getIvarValueWithTarget:(id)target withPropertyName:(NSString *)propertyName {
Ivar ivar = class_getInstanceVariable([target class], [propertyName UTF8String]);
if (ivar) {
id value = object_getIvar(target, ivar);
return value;
} else {
return nil;
}
}
方法3. 动态添加property
优点:
可在已有的类中动态添加property,遍历属性时可遍历到。
缺点:
相比方法1繁琐,且需要手动存取值。
使用到的几个方法
- class_getInstanceVariable:获取成员变量ivar
判断是否存在该成员变量ivar,如果存在则无需进行后续操作,不存在就动态添加。
/**
targetClass: 表示要添加的属性的类
propertyName: 表示要添加的属性名
*/
Ivar ivar = class_getInstanceVariable(targetClass, [[NSString stringWithFormat:@"_%@", propertyName] UTF8String]);
- class_addProperty:添加属性
成功(返回YES)则添加属性,失败(返回NO)则动态替换属性):
// value:属性的赋值,根据属性值,判断属性类型
objc_property_attribute_t type = { "T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([value class])] UTF8String] };
objc_property_attribute_t ownership = { "&", "N" };
objc_property_attribute_t backingivar = { "V", [[NSString stringWithFormat:@"_%@", propertyName] UTF8String] };
objc_property_attribute_t attrs[] = { type, ownership, backingivar };
/**
targetClass: 表示要添加的属性的类
propertyName: 表示要添加的属性名
attrs: 类特性列表
attrsCount: 类特性个数
*/
unsigned int attrsCount = 3;
class_addProperty(targetClass, [propertyName UTF8String], attrs, attrsCount)
- class_replaceProperty:替换属性
属性添加失败时执行
class_replaceProperty(targetClass, [propertyName UTF8String], attrs, attrsCount);
- class_addMethod:添加方法
(void)addMethod(Class _Nullable targetClass, NSString * propertyName)
{
class_addMethod(targetClass, NSSelectorFromString(propertyName), (IMP)customGetter, "@@:");
class_addMethod(targetClass, NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]), (IMP)customSetter, "v@:@");
}
// 以属性名为key去存储新属性值.
id customGetter(id targetClass, SEL _targetCmd) {
if (dictCustomerProperty == nil) {
dictCustomerProperty = [NSMutableDictionary new];
}
NSString *key = NSStringFromSelector(_targetCmd);
return [dictCustomerProperty objectForKey:key];
}
void customSetter(id targetClass, SEL _targetCmd, id newValue) {
//移除set
NSString *key = [NSStringFromSelector(_targetCmd) 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:@""];
if (dictCustomerProperty == nil) {
dictCustomerProperty = [NSMutableDictionary new];
}
[dictCustomerProperty setObject:newValue forKey:key];
}
封装
创建一个类DynamicProperty
static NSMutableDictionary *dictCustomerProperty;
// 在目标target上添加属性,属性名propertyname,值value
+ (void)addPropertyWithtarget:(id)target withPropertyName:(NSString *)propertyName withValue:(id)value {
//先判断有没有这个属性,没有就添加,有就返回
Ivar ivar = class_getInstanceVariable([target class], [[NSString stringWithFormat:@"_%@", propertyName] UTF8String]);
if (ivar) {
return;
}
objc_property_attribute_t type = { "T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([value class])] UTF8String] };
objc_property_attribute_t ownership = { "&", "N" };
objc_property_attribute_t backingivar = { "V", [[NSString stringWithFormat:@"_%@", propertyName] UTF8String] };
objc_property_attribute_t attrs[] = { type, ownership, backingivar };
if (class_addProperty([target class], [propertyName UTF8String], attrs, 3)) {
//添加get和set方法
class_addMethod([target class], NSSelectorFromString(propertyName), (IMP)customGetter, "@@:");
class_addMethod([target class], NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]), (IMP)customSetter, "v@:@");
//赋值
[target setValue:value forKey:propertyName];
NSLog(@"%@", [target valueForKey:propertyName]);
NSLog(@"创建属性Property成功");
} else {
class_replaceProperty([target class], [propertyName UTF8String], attrs, 3);
//添加get和set方法
class_addMethod([target class], NSSelectorFromString(propertyName), (IMP)customGetter, "@@:");
class_addMethod([target class], NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]), (IMP)customSetter, "v@:@");
//赋值
[target setValue:value forKey:propertyName];
}
}
id customGetter(id self1, SEL _cmd1) {
if (dictCustomerProperty == nil) {
dictCustomerProperty = [NSMutableDictionary new];
}
NSString *key = NSStringFromSelector(_cmd1);
return [dictCustomerProperty objectForKey:key];
}
void customSetter(id self1, SEL _cmd1, id newValue) {
//移除set
NSString *key = [NSStringFromSelector(_cmd1) 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:@""];
if (dictCustomerProperty == nil) {
dictCustomerProperty = [NSMutableDictionary new];
}
[dictCustomerProperty setObject:newValue forKey:key];
}
在需要添加属性的分类中
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[DynamicProperty addPropertyWithtarget:[self new] withPropertyName:@"name" withValue:[NSString new]];
[DynamicProperty addPropertyWithtarget:[self new] withPropertyName:@"childArr" withValue:[NSArray new]];
});
}
方法4. 通过KVC添加
这种方式不属于动态运行时处理。
重写setValue:forUndefinedKey、valueForUndefinedKey: