类扩展时Apple又只提供了对方法的扩展,对属性的扩展是不会自动生成_变量以及属性的setter:和getter方法。但苹果提供了runtime,我们可以通过runtime使用关联API就可以做到对属性的扩展。
/**
* Sets an associated value for a given object using a given key and association policy.
*
* @param object The source object for the association.
* @param key The key for the association.
* @param value The value to associate with the key key for object. Pass nil to clear an existing association.
* @param policy The policy for the association. For possible values, see “Associative Object Behaviors.”
*/
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
/**
* Returns the value associated with a given object for a given key.
*
* @param object The source object for the association.
* @param key The key for the association.
*
* @return The value associated with the key \e key for \e object.
*/
id objc_getAssociatedObject(id object, const void *key)
/**
* Removes all associations for a given object.
*
* @param object An object that maintains associated objects.
*
* @note The main purpose of this function is to make it easy to return an object
* to a "pristine state”. You should not use this function for general removal of
* associations from objects, since it also removes associations that other clients
* may have added to the object. Typically you should use \c objc_setAssociatedObject
* with a nil value to clear an association.
*/
void objc_removeAssociatedObjects(id object)
- 设置关联值
对于设置关联,我们需要使用下面的API关联起来:
/**
object:与谁关联,通常是传self
key:唯一键,在获取值时通过该键获取,通常是使用static const void *来声明
value:关联所设置的值
policy:内存管理策略,比如使用copy
*/
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
- 获取关联值
如果我们要获取所关联的值,需要通过key来获取,调用如下函数:
/**
object:与谁关联,通常是传self,在设置关联时所指定的与哪个对象关联的那个对象
key:唯一键,在设置关联时所指定的键
*/
id objc_getAssociatedObject(id object, const void *key)
- 关联策略
我们先看看设置关联时所指定的policy,它是一个枚举类型,看官方说明:
/**
* 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. */
};
关联类型与等效@property属性
OBJC_ASSOCIATION_ASSIGN:assign(弱引用关联,通常是基本数据类型,如int、float)
OBJC_ASSOCIATION_RETAIN_NONATOMIC:nonatomic,retain(表示强(strong)引用关联对象)
OBJC_ASSOCIATION_COPY_NONATOMIC:nonatomic,copy(表示关联对象copy)
OBJC_ASSOCIATION_RETAIN:retain(表示强(strong)引用关联对象,但不是线程安全的)
OBJC_ASSOCIATION_COPY:copy(表示关联对象copy,但不是线程安全的)
例子
UIViewController + viewInfo.h文件
#import <UIKit/UIKit.h>
@interface UIViewController (viewInfo)
@property (nonatomic, copy) NSString *name; //视图名字
@property (nonatomic, assign) BOOL hasChildViewController; //是否有子视图
@property (nonatomic, strong) UIImage *backgroundImage; //背景图片
@end
UIViewController + viewInfo.m文件
#import "UIViewController+viewInfo.h"
#import <objc/runtime.h>
static const void *kName = "name";
static const void *kHasChildViewController = @"hasChildViewController";
static const void *kBackgroundImage = @"backgroundImage";
@implementation UIViewController (Information)
#pragma mark - 字符串类型的动态绑定
- (NSString *)name {
return objc_getAssociatedObject(self, kName);
}
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self, kName, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
#pragma mark - BOOL类型的动态绑定
- (BOOL)hasChildViewController {
return [objc_getAssociatedObject(self, kHasChildViewController) boolValue];
}
- (void)setHasChildViewController:(BOOL)hasChildViewController {
objc_setAssociatedObject(self, kHasChildViewController, [NSNumber numberWithBool:hasChildViewController], OBJC_ASSOCIATION_ASSIGN);
}
#pragma mark - 类类型的动态绑定
- (UIImage *)backgroundImage {
return objc_getAssociatedObject(self, kBackgroundImage);
}
- (void)setBackgroundImage:(UIImage *)backgroundImage {
objc_setAssociatedObject(self, kBackgroundImage, backgroundImage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end