ios动态添加属性的几种方法

http://blog.csdn.net/shengyumojian/article/details/44919695

在ios运行过程中,有几种方式能够动态的添加属性。

1-通过runtime动态关联对象

主要用到了objc_setAssociatedObject,objc_getAssociatedObject以及objc_removeAssociatedObjects

[objc] view plain copy

  1. //在目标target上添加关联对象,属性名propertyname(也能用来添加block),值value
  2. + (void)addAssociatedWithtarget:(id)target withPropertyName:(NSString *)propertyName withValue:(id)value {
  3. id property = objc_getAssociatedObject(target, &propertyName);
  4. if(property == nil)
  5. {
  6. property = value;
  7. objc_setAssociatedObject(target, &propertyName, property, OBJC_ASSOCIATION_RETAIN);
  8. }
  9. }
  10. //获取目标target的指定关联对象值
  11. + (id)getAssociatedValueWithTarget:(id)target withPropertyName:(NSString *)propertyName {
  12. id property = objc_getAssociatedObject(target, &propertyName);
  13. return property;
  14. }

优点:这种方式能够使我们快速的在一个已有的class内部添加一个动态属性或block块。

缺点:不能像遍历属性一样的遍历我们所有关联对象,且不能移除制定的关联对象,只能通过removeAssociatedObjects方法移除所有关联对象。

2-通过runtime动态添加Ivar

主要用到objc_allocateClassPair,class_addIvar,objc_registerClassPair

[objc] view plain copy

  1. //在目标target上添加属性(已经存在的类不支持,可跳进去看注释),属性名propertyname,值value
  2. + (void)addIvarWithtarget:(id)target withPropertyName:(NSString *)propertyName withValue:(id)value {
  3. if (class_addIvar([target class], [propertyName UTF8String], sizeof(id), log2(sizeof(id)), "@")) {
  4. YYLog(@"创建属性Ivar成功");
  5. }
  6. }
  7. //获取目标target的指定属性值
  8. + (id)getIvarValueWithTarget:(id)target withPropertyName:(NSString *)propertyName {
  9. Ivar ivar = class_getInstanceVariable([target class], [propertyName UTF8String]);
  10. if (ivar) {
  11. id value = object_getIvar(target, ivar);
  12. return value;
  13. } else {
  14. return nil;
  15. }
  16. }

优点:动态添加Ivar我们能够通过遍历Ivar得到我们所添加的属性。

缺点:不能在已存在的class中添加Ivar,所有说必须通过objc_allocateClassPair动态创建一个class,才能调用class_addIvar创建Ivar,最后通过objc_registerClassPair注册class。

3-通过runtime动态添加property

主要用到class_addProperty,class_addMethod,class_replaceProperty,class_getInstanceVariable

[objc] view plain copy

  1. //在目标target上添加属性,属性名propertyname,值value
  2. + (void)addPropertyWithtarget:(id)target withPropertyName:(NSString *)propertyName withValue:(id)value {
  3. //先判断有没有这个属性,没有就添加,有就直接赋值
  4. Ivar ivar = class_getInstanceVariable([target class], [[NSString stringWithFormat:@"_%@", propertyName] UTF8String]);
  5. if (ivar) {
  6. return;
  7. }
  8. /*
  9. objc_property_attribute_t type = { "T", "@\"NSString\"" };
  10. objc_property_attribute_t ownership = { "C", "" }; // C = copy
  11. objc_property_attribute_t backingivar  = { "V", "_privateName" };
  12. objc_property_attribute_t attrs[] = { type, ownership, backingivar };
  13. class_addProperty([SomeClass class], "name", attrs, 3);
  14. */
  15. //objc_property_attribute_t所代表的意思可以调用getPropertyNameList打印,大概就能猜出
  16. objc_property_attribute_t type = { "T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([value class])] UTF8String] };
  17. objc_property_attribute_t ownership = { "&", "N" };
  18. objc_property_attribute_t backingivar  = { "V", [[NSString stringWithFormat:@"_%@", propertyName] UTF8String] };
  19. objc_property_attribute_t attrs[] = { type, ownership, backingivar };
  20. if (class_addProperty([target class], [propertyName UTF8String], attrs, 3)) {
  21. //添加get和set方法
  22. class_addMethod([target class], NSSelectorFromString(propertyName), (IMP)getter, "@@:");
  23. class_addMethod([target class], NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]), (IMP)setter, "[email protected]:@");
  24. //赋值
  25. [target setValue:value forKey:propertyName];
  26. NSLog(@"%@", [target valueForKey:propertyName]);
  27. YYLog(@"创建属性Property成功");
  28. } else {
  29. class_replaceProperty([target class], [propertyName UTF8String], attrs, 3);
  30. //添加get和set方法
  31. class_addMethod([target class], NSSelectorFromString(propertyName), (IMP)getter, "@@:");
  32. class_addMethod([target class], NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]), (IMP)setter, "[email protected]:@");
  33. //赋值
  34. [target setValue:value forKey:propertyName];
  35. }
  36. }
  37. id getter(id self1, SEL _cmd1) {
  38. NSString *key = NSStringFromSelector(_cmd1);
  39. Ivar ivar = class_getInstanceVariable([self1 class], "_dictCustomerProperty");  //basicsViewController里面有个_dictCustomerProperty属性
  40. NSMutableDictionary *dictCustomerProperty = object_getIvar(self1, ivar);
  41. return [dictCustomerProperty objectForKey:key];
  42. }
  43. void setter(id self1, SEL _cmd1, id newValue) {
  44. //移除set
  45. NSString *key = [NSStringFromSelector(_cmd1) stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:@""];
  46. //首字母小写
  47. NSString *head = [key substringWithRange:NSMakeRange(0, 1)];
  48. head = [head lowercaseString];
  49. key = [key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:head];
  50. //移除后缀 ":"
  51. key = [key stringByReplacingCharactersInRange:NSMakeRange(key.length - 1, 1) withString:@""];
  52. Ivar ivar = class_getInstanceVariable([self1 class], "_dictCustomerProperty");  //basicsViewController里面有个_dictCustomerProperty属性
  53. NSMutableDictionary *dictCustomerProperty = object_getIvar(self1, ivar);
  54. if (!dictCustomerProperty) {
  55. dictCustomerProperty = [NSMutableDictionary dictionary];
  56. object_setIvar(self1, ivar, dictCustomerProperty);
  57. }
  58. [dictCustomerProperty setObject:newValue forKey:key];
  59. }
  60. + (id)getPropertyValueWithTarget:(id)target withPropertyName:(NSString *)propertyName {
  61. //先判断有没有这个属性,没有就添加,有就直接赋值
  62. Ivar ivar = class_getInstanceVariable([target class], [[NSString stringWithFormat:@"_%@", propertyName] UTF8String]);
  63. if (ivar) {
  64. return object_getIvar(target, ivar);
  65. }
  66. ivar = class_getInstanceVariable([target class], "_dictCustomerProperty");  //basicsViewController里面有个_dictCustomerProperty属性
  67. NSMutableDictionary *dict = object_getIvar(target, ivar);
  68. if (dict && [dict objectForKey:propertyName]) {
  69. return [dict objectForKey:propertyName];
  70. } else {
  71. return nil;
  72. }
  73. }

优点:这种方法能够在已有的类中添加property,且能够遍历到动态添加的属性。

缺点:比较麻烦,getter和setter需要自己写,且值也需要自己存储,如上面的代码,我是把setter中的值存储到了_dictCustomerProperty里面,在getter中再从_dictCustomerProperty读出值。

4-通过setValue:forUndefinedKey动态添加键值

这种方法优点类似property,需要重写setValue:forUndefinedKey和valueForUndefinedKey:,存值方式也一样,需要借助一个其它对象。由于这种方式没通过runtime,所以也比较容易理解。在此就不举例了。

时间: 2024-08-06 11:56:28

ios动态添加属性的几种方法的相关文章

Android在布局中动态添加view的两种方法

一.说明 添加视图文件的时候有两种方式:1.通过在xml文件定义layout:2.java代码编写 二.前言说明 1.构造xml文件 2.LayoutInflater 提到addview,首先要了解一下LayoutInflater类.这个类最主要的功能就是实现将xml表述的layout转化为View的功能.为了便于理解,我们可以将它与findViewById()作一比较,二者都是实例化某一对象,不同的是findViewById()是找xml布局文件下的具体widget控件实例化,而LayoutI

iOS 动态添加属性方法

写在前面: 动态增加属性也不是很肆意的增加,是需要代码进行写的.如果一个类没有那个属性,那么在编译阶段就会报错而导致无法进行.之所以还叫做动态添加大致上是因为可以不重写某个类,而进行相应的属性的添加吧!不扯那么多了.切入正题: 首先需要创建类别,用来写入相应的set/get方法. #import <UIKit/UIKit.h> @interface UILabel (Associate) - (void) setFlashColor:(UIColor *) flashColor; - (UIC

Android中动态添加View的两种方法

一.使用xml的方式: 1.LayoutInflater: 这个类可以把xml表述的Layout解析为View,从而可以使addView()方法添加View. 2.LayoutInflater与findViewById的区别: 两者都是实例化某一个对象,不同的是findViewById是通过找xml布局文件下的一个具体的widget控件进行实例化,而LayoutInflater是找res/layout 下的xml布局文件来实例化的. 3.使用方法: LayoutInflater inflater

js+jquery动态设置/增加/删除/获取元素属性的两种方法集锦对比(动态onclick属性设置+动态title设置)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html140/strict.dtd"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> <title>

WPF编程,通过Double Animation动态更改控件属性的一种方法。

原文:WPF编程,通过Double Animation动态更改控件属性的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/article/details/87251422 DoubleAnimation类指定起始值(From="30").终点值(To="300").时间(Duration="3"),以及动画结束应该如何(FillBehavior="Stop&qu

WPF编程,通过【帧】动态更改控件属性的一种方法。

原文:WPF编程,通过[帧]动态更改控件属性的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/article/details/87249998 WPF提供了一种基于帧的动画实现方式,由CompositionTarget类来完成. WPF会在每次界面刷新时调用该回调函数. CompositionTarget的刷新率与窗体保持一致,因此很难人工控制动画的快慢. ?1.前台 <Grid> <Grid.RowDefin

Python3动态添加属性,删除属性,判断属性函数

动态添加属性是指在类的外面给对象动态绑定属性,添加的方法有两种:1.直接绑定到对象上,例如:p.name="Tom"2.使用setattr函数:setattr(p,'name','Tom')判断对象是否拥有某个属性:hasattr(p,'name'),返回布尔值:也可以用来判断对象是否拥有某个方法删除某个属性:del关键字:del p.namedelattr函数:delattr(p,'name') 原文地址:https://blog.51cto.com/13560219/2479011

IOS 隐藏软键盘的3种方法

IOS开发里软键盘是经常需要打交道的地方,下面为大家带来我整理总结的3种隐藏键盘的方法. 使用软键盘的 Done 键隐藏键盘 出发软键盘隐藏最常用的事件是文本框的 Did End on Exit,它在用户按软键盘中的 Done 键时发生.选中一个UITextField控件,点击鼠标右键弹出面板,鼠标左键按住 Did End on Exit 事件旁边的圆圈,然后拖曳到右侧 .h 文件中,命名为 CloseTheKeyBoard,在 m 文件中具体实现如下图所示(此处同时显示 .h 文件与 .m 文

BAP中创建动态内表的三种方法(转载)

BAP中创建动态内表的三种方法 第一种: 如果我们需要的动态内表字段或者动态工作区和数据字典中的类型一致,可以直接使用CREATE DATA生成,当然也可以是自定义类型. 比如要产生和数据表MARA结构一致的动态内表: DATA : DY_TABLE TYPE REF TO DATA, WA_LINE TYPE REF TO DATA. FIELD-SYMBOLS: <DYN_TABLE> TYPE STANDARD TABLE . CREATE DATA DY_TABLE TYPE TABL