前言
我们在 iOS 开发中经常需要使用分类(Category),为已经存在的类添加属性的需求,但是使用
@property
并不能在分类中正确创建实例变量和存取方法。不过,通过 Objective-C 运行时中的关联对象,也就是 Associated Object,我们可以实现上述需求。
@property声明属性
@interface MyViewController :UIViewController
//属性变量
@property (nonatomic, strong) UIButton *myButton;
@end
在使用上面代码时会做3件事:
- 生成实例变量 _myButton;
- 生成getter方法:-myButton
- 生成setter方法:-setMyButton:
@implementation MyViewController
{
UIButton *_myButton;
}
-(UIButton *)myButton{
return _myButton;
}
-(void)setMyButton:(UIButton *)myButton{
_myButton = myButton;
}
这些代码都是编译器为我们生成的,虽然我们看不到它,但是它确实在这里,不需要我们自己再写这些代码。
类目(Categary)中用@property声明属性
UIViewController+CommonMethod.h
#import <UIKit/UIKit.h>
@interface UIViewController (CommonMethod)
@property (nonatomic, strong) UIButton *LoginButton;
@end
UIViewController+CommonMethod.m 中会报下面警告
这里的警告告诉我们,loginButton 属性的存取方法需要自己手动去实现,或者使用
@dynamic
在运行时实现这些方法。换句话说,分类中的
@property
并没有为我们生成实例变量以及存取方法,而需要我们手动实现。
实现getter 和setter方法:
#import "UIViewController+CommonMethod.h"
#import <objc/runtime.h>
@implementation UIViewController (CommonMethod)
-(UIButton *)loginButton{
return objc_getAssociatedObject(self, _cmd);
}
-(void)setLoginButton:(UIButton *)loginButton{
objc_setAssociatedObject(self, @selector(loginButton), loginButton, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
OBJC_EXPORT void
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
OBJC_EXPORT id _Nullable
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
OBJC_EXPORT void
objc_removeAssociatedObjects(id _Nonnull object)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
注意:
这里的
_cmd
代指当前方法的选择子,也就是 @selector(loginButton)。
以键值对形式添加关联对象
OBJC_EXPORT void
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);objc_setAssociatedObject :把一个对象与另外一个对象进行关联。该函数需要四个参数:源对象,关键字,关联的对象和一个关联策略。
- OBJC_EXPORT:打包lib时,用来说明该函数是暴露给外界调用的。
- id _Nonnull object:表示关联者,是一个对象,变量名理所当然也是object。
- const void * _Nonnull key:关键字是一个void类型的指针。每一个关联的关键字必须是唯一的。通常都是会采用静态变量来作为关键字。
- id _Nullable value:表示被关联者,变量名是value,它
要关联到object上
的。- objc_AssociationPolicy policy:关联策略表明了相关的对象是通过赋值,保留引用还是复制的方式进行关联的;还有这种关联是原子的还是非原子的。这里的关联策略和声明属性时的很类似。这种关联策略是通过使用预先定义好的常量来表示的。
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { OBJC_ASSOCIATION_ASSIGN = 0, OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, OBJC_ASSOCIATION_COPY_NONATOMIC = 3, OBJC_ASSOCIATION_RETAIN = 01401, OBJC_ASSOCIATION_COPY = 01403 };
******注意*****
断开关联是使用objc_setAssociatedObject函数,传入nil值即可。
使用函数objc_removeAssociatedObjects可以断开所有关联。
根据
key
获取关联对象OBJC_EXPORT id _Nullable
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
- id _Nonnull object:
- const void * _Nonnull key:
移除所有关联对象
OBJC_EXPORT void
objc_removeAssociatedObjects(id _Nonnull object)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
- id _Nonnull object:
这个方法不推荐使用,因为会遍历所有的关联对象,并且全部释放,可能会造成别的模块功能缺陷。
相关文章: