本文只是整理Runtime中,成员变量、属性、关联对象、方法交换使用实例。不会很细致的讲解Runtime
的内容,如果想了解Runtime
更多内容,可以移步这里 ,查看大神们关于Runtime
博文。
成员变量和属性 成员变量(Ivar) 定义 1、Ivar:实例变量类型, 其实是一个指向objc_ivar结构体的指针
1 typedef struct objc_ivar *Ivar;
操作方法 1 2 3 4 5 6 7 // 获取成员变量名字 const char * ivar_getName(Ivar v); // 获取成员变量类型编码 const char * ivar_getTypeEncoding(Ivar v); //获取成员变量的偏移量 ptrdiff_t ivar_getOffset(Ivar v); //注:对于id类型或其他类型对象的实例变量,可以调用object_getIvar和object_setIvar直接访问成员变量,而不使用偏移量。
使用实例 User.h
1 2 3 4 5 6 7 8 9 10 11 12 @interface User : NSObject { NSString *_name; } @property NSString *sex; @property (nonatomic, copy) NSDictionary *dict; @property (nonatomic, assign) NSInteger age; @property (nonatomic, assign) double tall; @property (nonatomic, assign) NSUInteger height; - (void)getIvar; - (void)getProperty; @end
User.m
1 2 3 4 5 6 7 8 9 10 11 12 - (void)getIvar{ unsigned int count = 0; //获取所有的成员变量 Ivar *ivars = class_copyIvarList([User class], &count); for (unsigned i = 0; i < count; i ++) { Ivar ivar = ivars[i]; const char *name = ivar_getName(ivar); //成员变量名称 const char *type = ivar_getTypeEncoding(ivar); //成员变量类型 NSLog(@"类型 %s 的 %s", type, name); } free(ivars); //手动释放 }
注: class_copyIvarList()方法,请移步在另一篇文章:Objective-C Runtime:类和对象
输出结果:
1 2 3 4 5 6 LearnRuntime[836:9888] 类型 @"NSString" 的 _name LearnRuntime[836:9888] 类型 @"NSString" 的 _sex LearnRuntime[836:9888] 类型 @"NSDictionary" 的 _dict LearnRuntime[836:9888] 类型 q 的 _age LearnRuntime[836:9888] 类型 d 的 _tall LearnRuntime[836:9888] 类型 Q 的 _height
注:关于输出结果为什么是_name
, 请移步《招聘一个靠谱的 iOS》—参考答案(上)- 14题
属性(property) 定义 1、objc_property_t:是表示Objective-C声明的属性的类型,其实是指向objc_property结构体的指针。
1 typedef struct objc_property *objc_property_t;
2、objc_property_attribute_t: 定义了属性的特性,结构体如下:
1 2 3 4 typedef struct { const char *name; /**< The name of the attribute */ const char *value; /**< The value of the attribute (usually empty) */ } objc_property_attribute_t;
操作方法 1 2 3 4 5 6 7 8 // 获取属性名 const char * property_getName(objc_property_t property); // 获取属性特性描述字符串 const char * property_getAttributes(objc_property_t property); // 获取属性中指定的特性 char * property_copyAttributeValue ( objc_property_t property, const char *attributeName ); // 获取属性的特性列表 objc_property_attribute_t * property_copyAttributeList ( objc_property_t property, unsigned int *outCount );
注:property_copyAttributeValue和property_copyAttributeList, 返回值在使用后需要手动释放free();
使用实例 User.m 添加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 - (void)getProperty { unsigned int count = 0; // 获取属性列表 objc_property_t * properties = class_copyPropertyList([User class], &count); for (unsigned i = 0; i < count; i ++) { objc_property_t property = properties[i]; const char *name = property_getName(property); const char *propertyAttr = property_getAttributes(property); NSLog(@"属性描述为%s的%s", propertyAttr, name); unsigned int attrCount = 0; //属性的特性列表 objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount); for (unsigned i = 0; i < attrCount; i ++) { objc_property_attribute_t attr = attrs[i]; const char *name = attr.name; const char *value = attr.value; NSLog(@"属性的特性描述:%s值:%s", name, value); } free(attrs); NSLog(@"\n"); } free(properties); }
输出结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 LearnRuntime[1190:51044] 属性描述为T@"NSString",&,V_sex的sex LearnRuntime[1190:51044] 属性的特性描述:T值:@"NSString" LearnRuntime[1190:51044] 属性的特性描述:&值: LearnRuntime[1190:51044] 属性的特性描述:V值:_sex LearnRuntime[1190:51044] LearnRuntime[1190:51044] 属性描述为T@"NSDictionary",C,N,V_dict的dict LearnRuntime[1190:51044] 属性的特性描述:T值:@"NSDictionary" LearnRuntime[1190:51044] 属性的特性描述:C值: LearnRuntime[1190:51044] 属性的特性描述:N值: LearnRuntime[1190:51044] 属性的特性描述:V值:_dict LearnRuntime[1190:51044] LearnRuntime[1190:51044] 属性描述为Tq,N,V_age的age LearnRuntime[1190:51044] 属性的特性描述:T值:q LearnRuntime[1190:51044] 属性的特性描述:N值: LearnRuntime[1190:51044] 属性的特性描述:V值:_age LearnRuntime[1190:51044] LearnRuntime[1190:51044] 属性描述为Td,N,V_tall的tall LearnRuntime[1190:51044] 属性的特性描述:T值:d LearnRuntime[1190:51044] 属性的特性描述:N值: LearnRuntime[1190:51044] 属性的特性描述:V值:_tall LearnRuntime[1190:51044] LearnRuntime[1190:51044] 属性描述为TQ,N,V_height的height LearnRuntime[1190:51044] 属性的特性描述:T值:Q LearnRuntime[1190:51044] 属性的特性描述:N值: LearnRuntime[1190:51044] 属性的特性描述:V值:_height
注:objc_property_getAttribute_t结构体包含name和value,属性如下:
1 2 3 4 属性类型 name值:T value:变化 编码类型 name值:C(copy) &(strong) W(weak) 空(assign) 等 value:无 非/原子性 name值:空(atomic) N(Nonatomic) value:无 变量名称 name值:V value:变化
关联对象(Associated objects) 定义 Associated objects是Objective-C 2.0运行时一个特性。<objc/runtime.h>中定义三个允许将任何键值在运行时关联到对象上的函数:
1 2 3 objc_setAssociatedObject objc_getAssociatedObject objc_removeAssociatedObjects
使用三个函数,开发者可以对已经存在的类扩展中添加自定义的属性 。
删除属性 如果你尝试使用objc_removeAssociatedObjects()进行删除操作,但官方文档 告诉我们不应该手动调用这个函数,通常使用objc_setAssocatedObject方法传入nil值清除关联。关于AssociatedObjects相关知识,请看NShipster上的文章Associated Objects 。
使用实例 NSObject+AssociatedObject.h
1 2 3 @interface NSObject (AssociatedObject) @property (nonatomic, strong) id associatedObject; @end
NSObject+AssociatedObject.m
1 2 3 4 5 6 7 8 @implementation NSObject (AssociatedObject) @dynamic associatedObject; - (void)setAssociatedObject:(id)object { objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (id)associatedObject { return objc_getAssociatedObject(self, @selector(associatedObject)); }
方法交换(Method swizzling) 定义 Method Swizzling是改变selector的实际实现的技术,通过它可以在运行时通过修改类的分发表中selector对应的函数,来修改方法的实现。
操作方法 1 2 3 4 5 6 7 8 9 10 11 12 //获取实例方法 class_getInstanceMethod //获取方法的实现 method_getImplementation //获取实现的编码类型 method_getTypeEncoding //给方法添加实现 class_addMethod //用一个方法的实现替换另一个方法的实 class_replaceMethod //交换两个方法的实现 method_exchangeImplementations
使用实例 将UIViewController中的ViewWillAppear:方法替换成自定义方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #import <objc/runtime.h> @implementation UIViewController (tracking) /** * 理解: [self class] 和 object_getClass(self)区别和联系? */ + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; //注意:如果是类方法,则需要 Class class = object_getClass((id)self); SEL originalSelector = @selector(viewWillAppear:); SEL swizzledSelector = @selector(xxx_viewWillAppear:); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); //注意: 在交换方法实现,需要判断原有方法实现是否存在,存在才能交换 // 如何判断?添加原有方法,如果成功,表示原有方法不存在,失败,表示原有方法存在 // 原有方法可能没有实现,所以这里添加方法实现,用自己方法实现 // 这样做的好处:方法不存在,直接把自己方法的实现作为原有方法的实现,调用原有方法,就会来到当前方法的实现 BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } }); } /** * 添加自定义的方法 * * @param animated */ - (void)xxx_viewWillAppear: (BOOL) animated { [self xxx_viewWillAppear:animated]; //这里不会造成死循环,为什么呢? NSLog(@"viewWillAppear: %@", self); } @end
使用method swizzling 修改UIViewController的@selector(viewWillAppear:)对应的函数指针,指向自定义的xxx_viewWillAppear:的实现。具体讲解请看Method Swizzling 。
小结 文章作为学习笔记,目的是方便以后查看。另外,本人能力有限,如有错误欢迎指正。
参考资料 这里