Objective-C Runtime: 变量和属性
本文只是整理Runtime中,成员变量、属性、关联对象、方法交换使用实例。不会很细致的讲解
Runtime
的内容,如果想了解Runtime
更多内容,可以移步这里,查看大神们关于Runtime
博文。
成员变量和属性
成员变量(Ivar)
定义
1、Ivar:实例变量类型, 其实是一个指向objc_ivar结构体的指针
typedef struct objc_ivar *Ivar;
操作方法
// 获取成员变量名字
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
@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
- (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:类和对象
输出结果:
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结构体的指针。
typedef struct objc_property *objc_property_t;
2、objc_property_attribute_t: 定义了属性的特性,结构体如下:
typedef struct {
const char *name; /**< The name of the attribute */
const char *value; /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;
操作方法
// 获取属性名
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 添加
- (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);
}
输出结果
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,属性如下:
属性类型 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>中定义三个允许将任何键值在运行时关联到对象上的函数:
objc_setAssociatedObject
objc_getAssociatedObject
objc_removeAssociatedObjects
使用三个函数,开发者可以对已经存在的类扩展中添加自定义的属性。
删除属性
如果你尝试使用objc_removeAssociatedObjects()进行删除操作,但官方文档告诉我们不应该手动调用这个函数,通常使用objc_setAssocatedObject方法传入nil值清除关联。关于AssociatedObjects相关知识,请看NShipster上的文章Associated Objects。
使用实例
NSObject+AssociatedObject.h
@interface NSObject (AssociatedObject)
@property (nonatomic, strong) id associatedObject;
@end
NSObject+AssociatedObject.m
@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对应的函数,来修改方法的实现。
操作方法
//获取实例方法
class_getInstanceMethod
//获取方法的实现
method_getImplementation
//获取实现的编码类型
method_getTypeEncoding
//给方法添加实现
class_addMethod
//用一个方法的实现替换另一个方法的实
class_replaceMethod
//交换两个方法的实现
method_exchangeImplementations
使用实例
将UIViewController中的ViewWillAppear:方法替换成自定义方法
#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。
小结
文章作为学习笔记,目的是方便以后查看。另外,本人能力有限,如有错误欢迎指正。
参考资料
这里整理关于Runtime经典博文。