iOS動(dòng)態(tài)添加屬性的幾種方法

在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,所以也比較容易理解。在此就不舉例了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,762評(píng)論 0 9
  • 我們常常會(huì)聽(tīng)說(shuō) Objective-C 是一門動(dòng)態(tài)語(yǔ)言,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,231評(píng)論 0 7
  • 本文轉(zhuǎn)載自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex閱讀 779評(píng)論 0 1
  • 64/100 100天寫作計(jì)劃第64篇 今天這篇寫給自己看,最近重拾一些職場(chǎng)書(shū)籍。這幾天的枕邊書(shū)都是《職得:成為...
    頁(yè)彥夕閱讀 397評(píng)論 0 5
  • 小王子的狐貍好朋友告訴他:“比如說(shuō),你在下午四點(diǎn)鐘來(lái),一到三點(diǎn)鐘我就感到幸福了。” 昆德拉也說(shuō),幸福是周而復(fù)始的可...
    臻靜閱讀 297評(píng)論 1 1