之前的一篇文章,我们讲解了Runtime的一些基础知识,接下来,我会讲一些怎么来运用这种Runtime机制,用到实际的编码中,有哪些情况下,我们需要用到这种机制
关联对象的应用
一般的,我们都在类声明中添加属性,但是出于某种原因,我们需要在分类中添加属性,但是分类中只能添加方法,不能添加属性,这时候我们Runtime就起到关键性作用了
Runtime提供了三个方法来设置关联对象
//设置关联对象
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
//获取关联对象
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
//移除关联对象
OBJC_EXPORT void objc_removeAssociatedObjects(id object)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
//参数解释
id object 被关联的对象
const void *key 关联的key 必须唯一
id value 关联的对象
objc_AssociationPolicy policy 关联策略
//其中的关联策略就相当于我们的property中的copy assign之类的
OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
* The association is not made atomically. */
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied.
* The association is not made atomically. */
OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object.
* The association is made atomically. */
OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied.
* The association is made atomically.
添加公共属性
两种解决办法:
- 继承NSArray,在子类中添加一个属性
- 使用分类,利用Runtime实现添加属性
我们举例第二种:
#import
@interface NSArray (PPS)
@property (nonatomic, copy) NSString *myString;
@end
#import "NSArray+PPS.h"
#import
char * const MY_STRING = "my_string";
@implementation NSArray (PPS)
-(NSString *)myString{
id myString = objc_getAssociatedObject(self, MY_STRING);
return myString;
}
-(void)setMyString:(NSString *)myString{
objc_setAssociatedObject(self, MY_STRING, myString, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
@end
这样,我们可以直接用点语法,对属性直接操作
添加私有成员变量
给按钮添加点击事件的回调,不用addtarget的方式
#import
@interface UIButton (PPS)
//传入点击事件的回调
- (instancetype)initWithFrame:(CGRect)frame callback:(void (^)(UIButton *button))callbackBlock;
@end
#import "UIButton+PPS.h"
#import
char * CALLBACK_BLOCK_IDENTIFER = "CALLBACK_BLOCK_IDENTIFER";
@interface UIButton()
@property (nonatomic, copy) void (^callbackBlock)(UIButton * button);
@end
@implementation UIButton (PPS)
- (void (^)(UIButton *))callbackBlock {
return objc_getAssociatedObject(self, CALLBACK_BLOCK_IDENTIFER);
}
- (void)setCallbackBlock:(void (^)(UIButton *))callbackBlock {
objc_setAssociatedObject(self, CALLBACK_BLOCK_IDENTIFER, callbackBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (instancetype)initWithFrame:(CGRect)frame callback:(void (^)(UIButton *))callbackBlock {
if (self = [super initWithFrame:frame]) {
self.callbackBlock = callbackBlock;
[self addTarget:self action:@selector(didClickAction:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
- (void)didClickAction:(UIButton *)button {
//想想这里为什么需要使用weak一下
__weak typeof(self) weakSelf = self;
weakSelf.callbackBlock(button);
}
@end
我们在初始化button的时候,可以直接处理点击事件
self.btn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 100, 50) callback:^(UIButton *button) {
NSLog(@"点击事件");
}];
成员变量和属性
这个的使用,运用最广泛的还是在json和model的转换,我们可以通过Runtime机制,将model中的所有成员属性都找出来,然后将这些成员属性的名称和返回的json字典中对比,查看有哪些匹配,然后纷纷赋值进去
json转model
- (instancetype)initWithDict:(NSDictionary *)dict {
if (self = [self init]) {
//(1)获取类的属性及属性对应的类型
NSMutableArray * keys = [NSMutableArray array];
NSMutableArray * attributes = [NSMutableArray array];
/*
* 例子
* name = value3 attribute = T@"NSString",C,N,V_value3
* name = value4 attribute = T^i,N,V_value4
*/
unsigned int outCount;
objc_property_t * properties = class_copyPropertyList([self class], &outCount);
for (int i = 0; i < outCount; i ++) {
objc_property_t property = properties[i];
//通过property_getName函数获得属性的名字
NSString * propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
[keys addObject:propertyName];
//通过property_getAttributes函数可以获得属性的名字和@encode编码
NSString * propertyAttribute = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
[attributes addObject:propertyAttribute];
}
//立即释放properties指向的内存
free(properties);
//(2)根据类型给属性赋值
for (NSString * key in keys) {
if ([dict valueForKey:key] == nil) continue;
[self setValue:[dict valueForKey:key] forKey:key];
}
}
return self;
}
当然这上面只是最简单的转换,其中还有很多问题待解决
如何识别int等基础类型数据
如何处理nil和Null
json嵌套如何处理
访问私有变量
我们知道,如果成员变量放在了m文件中,就成了私有变量,但是我们依然可以通过Runtime获取,这时候,我们就需要知道成员变量的名称了
Ivar ivar = class_getInstanceVariable([Model class], "_str1");
NSString * str1 = object_getIvar(model, ivar);
OC没有绝对的私有变量和方法,方法当然也可以这样获取出来
抛砖引玉,到此。。。
欢迎大家关注我的公众号,我会定期分享一些我在项目中遇到问题的解决办法和一些iOS实用的技巧,现阶段主要是整理出一些基础的知识记录下来

文章也会同步更新到我的博客:
ppsheep.com