iOS 利用runtime关联对象
2016-03-14 14:30
417 查看
有次在大牛群看到一个问题:“如何给一个字典添加一个属性(不能继承)”,立马蒙逼了,不能用继承,难道用分类?但是分类貌似只能添加方法不能添加属性啊,百思不得其解,直到后来接触到了runtime才恍然大悟。
什么是关联对象
关联对象是指某个OC对象通过一个唯一的key连接到一个类的实例上。
举个例子:Duan是Person类的一个实例,他的狗dog(一个OC对象)通过一根绳子(key)被他牵着散步,这可以说Duan和nimo是关联起来的,当然Duan可以牵着多个dog。
怎样关联对象
runtime提供给我们的方法:
//关联对像
void objc_setAssociatedObject(id object,
const void *key,
id value, objc_AssociationPolicy policy)
//获取关联对像
id objc_getAssociatedObject(id object,
const void *key)
//移除关联的对像
void objc_removeAssociatedObjects(id object)
变量说明:
id object:被关联的对像(eg:Duan)
const void *key:关联的key,要求唯一
id value:关联的对像(eg:dog)
objc_AssociationPolicy policy:内存管理的策略
objc_AssociationPolicy policy的enum值有:
/**
* Policies related to associative references.
* These are options to objc_setAssociatedObject()
*/
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
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. */
};
当对象被释放时,会根据这个策略来决定是否释放关联的对象,当策略是retain/copy时,会释放(release)关联的对象,当是assign,将不会释放。
值得注意的是,我们不需要主动调用removeAssociated来解除关联的对象,如果需要解除指定的对象,可以使用setAssociatedObject置nil来实现。
关联对象的应用
1.添加公用属性
这是最常用的一个模式,通常我们会在类的声明里面添加属性,但是出于某些需求,我们需要在分类里添加一个货多个属性的话,编译器就会报错,这个问题的解决方案就是实用runtime的关联对象。
eg:我们需要自定义一个tabBar,并暴露公共的属性和方法。
@interface UITabBarController (custom)
@property (nonatomic,strong)
UIView *customTabBar;
@end
#import "UITabBarController+custom.h"
#import <objc/runtime.h>
@implementation UITabBarController (custom)
- (void)setCustomTabBar:(UITabBar *)customTabBar
{
//这里使用方法的指针地址作为唯一的key
objc_setAssociatedObject(self,
@selector(customTabBar), customTabBar,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIView *)customTabBar
{
return objc_getAssociatedObject(self,
@selector(customTabBar));
}
// 其他方法。。。
@end
这样我们就可以跟原生的tabbar一样使用自定义的tabbar:
[self.tabBarController.customTabBar doSomeThing];
2.添加私有成员变量
有时候,需要在分类中添加不想暴露在公共声明的成员变量。
eg:给按钮添加点击事件的回调
@interface UIButton (CallBack)
- (instancetype)initWithFrame:(CGRect)frame callBack:(void (^)(UIButton *))callBlock;
@end
@interface UIButton()
@property (nonatomic,copy)
void (^callbackBlock)(UIButton * button);
@end
@implementation UIButton (CallBack)
- (void)setCallbackBlock:(void (^)(UIButton *))callbackBlock {
objc_setAssociatedObject(self,
@selector(callbackBlock), callbackBlock,
OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void (^)(UIButton *))callbackBlock {
return objc_getAssociatedObject(self,
@selector(callbackBlock));
}
- (instancetype)initWithFrame:(CGRect)frame callBack:(void (^)(UIButton *))callBlock {
if (self == [super
initWithFrame:frame]) {
self.callbackBlock = callBlock;
[self
addTarget:self
action:@selector(didClickAction:)
forControlEvents:UIControlEventTouchUpInside];
}
return
self;
}
- (void)didClickAction:(UIButton *)button
{
self.callbackBlock(button);
}
3.关联KVO观察者
有时候我们在分类中使用KVO,推荐使用关联的对象作为观察者,尽量避免对像观察自身。
此应用模式就不再举例了。
什么是关联对象
关联对象是指某个OC对象通过一个唯一的key连接到一个类的实例上。
举个例子:Duan是Person类的一个实例,他的狗dog(一个OC对象)通过一根绳子(key)被他牵着散步,这可以说Duan和nimo是关联起来的,当然Duan可以牵着多个dog。
怎样关联对象
runtime提供给我们的方法:
//关联对像
void objc_setAssociatedObject(id object,
const void *key,
id value, objc_AssociationPolicy policy)
//获取关联对像
id objc_getAssociatedObject(id object,
const void *key)
//移除关联的对像
void objc_removeAssociatedObjects(id object)
变量说明:
id object:被关联的对像(eg:Duan)
const void *key:关联的key,要求唯一
id value:关联的对像(eg:dog)
objc_AssociationPolicy policy:内存管理的策略
objc_AssociationPolicy policy的enum值有:
/**
* Policies related to associative references.
* These are options to objc_setAssociatedObject()
*/
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
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. */
};
当对象被释放时,会根据这个策略来决定是否释放关联的对象,当策略是retain/copy时,会释放(release)关联的对象,当是assign,将不会释放。
值得注意的是,我们不需要主动调用removeAssociated来解除关联的对象,如果需要解除指定的对象,可以使用setAssociatedObject置nil来实现。
关联对象的应用
1.添加公用属性
这是最常用的一个模式,通常我们会在类的声明里面添加属性,但是出于某些需求,我们需要在分类里添加一个货多个属性的话,编译器就会报错,这个问题的解决方案就是实用runtime的关联对象。
eg:我们需要自定义一个tabBar,并暴露公共的属性和方法。
@interface UITabBarController (custom)
@property (nonatomic,strong)
UIView *customTabBar;
@end
#import "UITabBarController+custom.h"
#import <objc/runtime.h>
@implementation UITabBarController (custom)
- (void)setCustomTabBar:(UITabBar *)customTabBar
{
//这里使用方法的指针地址作为唯一的key
objc_setAssociatedObject(self,
@selector(customTabBar), customTabBar,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIView *)customTabBar
{
return objc_getAssociatedObject(self,
@selector(customTabBar));
}
// 其他方法。。。
@end
这样我们就可以跟原生的tabbar一样使用自定义的tabbar:
[self.tabBarController.customTabBar doSomeThing];
2.添加私有成员变量
有时候,需要在分类中添加不想暴露在公共声明的成员变量。
eg:给按钮添加点击事件的回调
@interface UIButton (CallBack)
- (instancetype)initWithFrame:(CGRect)frame callBack:(void (^)(UIButton *))callBlock;
@end
@interface UIButton()
@property (nonatomic,copy)
void (^callbackBlock)(UIButton * button);
@end
@implementation UIButton (CallBack)
- (void)setCallbackBlock:(void (^)(UIButton *))callbackBlock {
objc_setAssociatedObject(self,
@selector(callbackBlock), callbackBlock,
OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void (^)(UIButton *))callbackBlock {
return objc_getAssociatedObject(self,
@selector(callbackBlock));
}
- (instancetype)initWithFrame:(CGRect)frame callBack:(void (^)(UIButton *))callBlock {
if (self == [super
initWithFrame:frame]) {
self.callbackBlock = callBlock;
[self
addTarget:self
action:@selector(didClickAction:)
forControlEvents:UIControlEventTouchUpInside];
}
return
self;
}
- (void)didClickAction:(UIButton *)button
{
self.callbackBlock(button);
}
3.关联KVO观察者
有时候我们在分类中使用KVO,推荐使用关联的对象作为观察者,尽量避免对像观察自身。
此应用模式就不再举例了。
相关文章推荐
- iOS 基础知识大全(OC篇零基础可学习)
- IOS开发 CocoaPods 使用 pod Install 出现 Updating local specs repositories
- iOS开发之简单实现圆形进度条
- iOS 图标、图形尺寸?
- 聪明的iOS开发者的Xcode使用技巧
- ios 计算字符串长度<转>
- iOS获取键盘的高度
- iOS --VVDocumenter注释的安装和使用
- iOS --VVDocumenter注释的安装和使用
- 我推荐的一些iOS开发书单
- iOS开发系列--触摸事件、手势识别、摇晃事件、耳机线控
- IOS 上架要求视频
- iOS多线程GCD
- iOS 隐藏状态栏(时间栏,电池栏)
- 如何轻松实现iOS9多任务管理器效果(iCarousel高级教程)
- iOS开发中使用Bmob RESTful API
- iOS-Sandbox
- iOS 关键帧动画 详解
- ios 官方sample
- iOS 视频播放