在ios運(yùn)行過(guò)程中,有幾種方式能夠動(dòng)態(tài)的添加屬性。
1-通過(guò)runtime動(dòng)態(tài)關(guān)聯(lián)對(duì)象
主要用到了objc_setAssociatedObject,objc_getAssociatedObject以及objc_removeAssociatedObjects
//在目標(biāo)target上添加關(guān)聯(lián)對(duì)象,屬性名propertyname(也能用來(lái)添加block),值value+ (void)addAssociatedWithtarget:(id)target withPropertyName:(NSString *)propertyName withValue:(id)value {? ? id property = objc_getAssociatedObject(target, &propertyName);? ? ? ? if(property == nil)? ? {? ? ? ? property = value;? ? ? ? objc_setAssociatedObject(target, &propertyName, property, OBJC_ASSOCIATION_RETAIN);? ? }}//獲取目標(biāo)target的指定關(guān)聯(lián)對(duì)象值+ (id)getAssociatedValueWithTarget:(id)target withPropertyName:(NSString *)propertyName {? ? id property = objc_getAssociatedObject(target, &propertyName);? ? return property;}
優(yōu)點(diǎn):這種方式能夠使我們快速的在一個(gè)已有的class內(nèi)部添加一個(gè)動(dòng)態(tài)屬性或block塊。
缺點(diǎn):不能像遍歷屬性一樣的遍歷我們所有關(guān)聯(lián)對(duì)象,且不能移除制定的關(guān)聯(lián)對(duì)象,只能通過(guò)removeAssociatedObjects方法移除所有關(guān)聯(lián)對(duì)象。
2-通過(guò)runtime動(dòng)態(tài)添加Ivar
主要用到objc_allocateClassPair,class_addIvar,objc_registerClassPair
//在目標(biāo)target上添加屬性(已經(jīng)存在的類不支持,可跳進(jìn)去看注釋),屬性名propertyname,值value+ (void)addIvarWithtarget:(id)target withPropertyName:(NSString *)propertyName withValue:(id)value {? ? if (class_addIvar([target class], [propertyName UTF8String], sizeof(id), log2(sizeof(id)), "@")) {? ? ? ? YYLog(@"創(chuàng)建屬性Ivar成功");? ? }}//獲取目標(biāo)target的指定屬性值+ (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;? ? }}
優(yōu)點(diǎn):動(dòng)態(tài)添加Ivar我們能夠通過(guò)遍歷Ivar得到我們所添加的屬性。
缺點(diǎn):不能在已存在的class中添加Ivar,所有說(shuō)必須通過(guò)objc_allocateClassPair動(dòng)態(tài)創(chuàng)建一個(gè)class,才能調(diào)用class_addIvar創(chuàng)建Ivar,最后通過(guò)objc_registerClassPair注冊(cè)class。
3-通過(guò)runtime動(dòng)態(tài)添加property
主要用到class_addProperty,class_addMethod,class_replaceProperty,class_getInstanceVariable
//在目標(biāo)target上添加屬性,屬性名propertyname,值value+ (void)addPropertyWithtarget:(id)target withPropertyName:(NSString *)propertyName withValue:(id)value {? ? ? ? //先判斷有沒(méi)有這個(gè)屬性,沒(méi)有就添加,有就直接賦值? ? Ivar ivar = class_getInstanceVariable([target class], [[NSString stringWithFormat:@"_%@", propertyName] UTF8String]);? ? if (ivar) {? ? ? ? return;? ? }? ? ? ? /*? ? objc_property_attribute_t type = { "T", "@/"NSString/"" };? ? objc_property_attribute_t ownership = { "C", "" }; // C = copy? ? objc_property_attribute_t backingivar? = { "V", "_privateName" };? ? objc_property_attribute_t attrs[] = { type, ownership, backingivar };? ? class_addProperty([SomeClass class], "name", attrs, 3);? ? */? ? ? ? //objc_property_attribute_t所代表的意思可以調(diào)用getPropertyNameList打印,大概就能猜出? ? 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)getter, "@@:");? ? ? ? class_addMethod([target class], NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]), (IMP)setter, "v@:@");? ? ? ? ? ? ? ? //賦值? ? ? ? [target setValue:value forKey:propertyName];? ? ? ? NSLog(@"%@", [target valueForKey:propertyName]);? ? ? ? ? ? ? ? YYLog(@"創(chuàng)建屬性Property成功");? ? } else {? ? ? ? class_replaceProperty([target class], [propertyName UTF8String], attrs, 3);? ? ? ? //添加get和set方法? ? ? ? class_addMethod([target class], NSSelectorFromString(propertyName), (IMP)getter, "@@:");? ? ? ? class_addMethod([target class], NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]), (IMP)setter, "v@:@");? ? ? ? ? ? ? ? //賦值? ? ? ? [target setValue:value forKey:propertyName];? ? }}id getter(id self1, SEL _cmd1) {? ? NSString *key = NSStringFromSelector(_cmd1);? ? Ivar ivar = class_getInstanceVariable([self1 class], "_dictCustomerProperty");? //basicsViewController里面有個(gè)_dictCustomerProperty屬性? ? NSMutableDictionary *dictCustomerProperty = object_getIvar(self1, ivar);? ? return [dictCustomerProperty objectForKey:key];}void setter(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:@""];? ? ? ? Ivar ivar = class_getInstanceVariable([self1 class], "_dictCustomerProperty");? //basicsViewController里面有個(gè)_dictCustomerProperty屬性? ? NSMutableDictionary *dictCustomerProperty = object_getIvar(self1, ivar);? ? if (!dictCustomerProperty) {? ? ? ? dictCustomerProperty = [NSMutableDictionary dictionary];? ? ? ? object_setIvar(self1, ivar, dictCustomerProperty);? ? }? ? [dictCustomerProperty setObject:newValue forKey:key];}+ (id)getPropertyValueWithTarget:(id)target withPropertyName:(NSString *)propertyName {? ? //先判斷有沒(méi)有這個(gè)屬性,沒(méi)有就添加,有就直接賦值? ? Ivar ivar = class_getInstanceVariable([target class], [[NSString stringWithFormat:@"_%@", propertyName] UTF8String]);? ? if (ivar) {? ? ? ? return object_getIvar(target, ivar);? ? }? ? ? ? ivar = class_getInstanceVariable([target class], "_dictCustomerProperty");? //basicsViewController里面有個(gè)_dictCustomerProperty屬性? ? NSMutableDictionary *dict = object_getIvar(target, ivar);? ? if (dict && [dict objectForKey:propertyName]) {? ? ? ? return [dict objectForKey:propertyName];? ? } else {? ? ? ? return nil;? ? }}
優(yōu)點(diǎn):這種方法能夠在已有的類中添加property,且能夠遍歷到動(dòng)態(tài)添加的屬性。
缺點(diǎn):比較麻煩,getter和setter需要自己寫,且值也需要自己存儲(chǔ),如上面的代碼,我是把setter中的值存儲(chǔ)到了_dictCustomerProperty里面,在getter中再?gòu)腳dictCustomerProperty讀出值。
4-通過(guò)setValue:forUndefinedKey動(dòng)態(tài)添加鍵值
這種方法優(yōu)點(diǎn)類似property,需要重寫setValue:forUndefinedKey和valueForUndefinedKey:,存值方式也一樣,需要借助一個(gè)其它對(duì)象。由于這種方式?jīng)]通過(guò)runtime,所以也比較容易理解。在此就不舉例了。