作者:erryeg_342 | 来源:互联网 | 2023-10-12 09:10
前言在开发中经常需要给已有的类添加方法和属性,但是Objective-C是不允许给已有类通过分类添加属性的,因为类分类是不会自动生成成员变量的。但是,我们可以通过运行时机制就可以做到了。本篇文章适合新
前言
在开发中经常需要给已有的类添加方法和属性,但是Objective-C
是不允许给已有类通过分类添加属性的,因为类分类是不会自动生成成员变量的。但是,我们可以通过运行时机制就可以做到了。
本篇文章适合新手阅读,手把手教你如何在项目中使用关联属性!
API介绍
我们先看看Runtime提供的关联API,只有这三个API,使用也是非常简单的:
123456789101112131415161718192021222324252627282930313233343536373839404142 |
/** * 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.” * * @see objc_setAssociatedObject * @see objc_removeAssociatedObjects */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. * * @see objc_setAssociatedObject */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. * * @see objc_setAssociatedObject * @see objc_getAssociatedObject */void objc_removeAssociatedObjects(id object) |
实际上,我们几乎不会使用到objc_removeAssociatedObjects
函数,这个函数的功能是移除指定的对象上所有的关联。既然我们要添加关联属性,几乎不会存在需要手动取消关联的场合。
设置关联值(Setter)
对于设置关联,我们需要使用下面的API关联起来:
123 |
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) |
参数说明:
object
:与谁关联,通常是传self
key
:唯一键,在获取值时通过该键获取,通常是使用static const void *
来声明
value
:关联所设置的值
policy
:内存管理策略,比如使用copy
获取关联值(Getter)
如果我们要获取所关联的值,需要通过key
来获取,调用如下函数:
123 |
id objc_getAssociatedObject(id object, const void *key) |
参数说明:
object
:与谁关联,通常是传self
,在设置关联时所指定的与哪个对象关联的那个对象
key
:唯一键,在设置关联时所指定的键
关联策略
我们先看看设置关联时所指定的policy
,它是一个枚举类型,看官方说明:
1234567891011121314151617 |
/** * Policies related to associative references. * These are options to objc_setAssociatedObject() */typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { OBJC_ASSOCIATION_ASSIGN = 0, /** OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /** * The association is not made atomically. */ OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /** * The association is not made atomically. */ OBJC_ASSOCIATION_RETAIN = 01401, /** * The association is made atomically. */ OBJC_ASSOCIATION_COPY = 01403 /** * The association is made atomically. */}; |
我们说明一下各个值的作用:
- OBJC_ASSOCIATION_ASSIGN:表示弱引用关联,通常是基本数据类型,如
int
、float
,非线程安全
- OBJC_ASSOCIATION_RETAIN_NONATOMIC:表示强(strong)引用关联对象,非线程安全
- OBJC_ASSOCIATION_COPY_NONATOMIC:表示关联对象copy,非线程安全
- OBJC_ASSOCIATION_RETAIN:表示强(strong)引用关联对象,是线程安全的
- OBJC_ASSOCIATION_COPY:表示关联对象copy,是线程安全的
扩展属性
我们来写一个例子,扩展UIControl
添加Block版本的TouchUpInside
事件。
扩展头文件声明:
1234567891011 |
#import typedef void (^HYBTouchUpBlock)(id sender); @interface UIControl (HYBBlock) @property (nonatomic, copy) HYBTouchUpBlock hyb_touchUpBlock; @end |
扩展实现文件:
123456789101112131415161718192021222324252627282930313233343536373839 |
#import "UIControl+HYBBlock.h"#import static const void *sHYBUIControlTouchUpEventBlockKey = "sHYBUIControlTouchUpEventBlockKey"; @implementation UIControl (HYBBlock) - (void)setHyb_touchUpBlock:(HYBTouchUpBlock)hyb_touchUpBlock { objc_setAssociatedObject(self, sHYBUIControlTouchUpEventBlockKey, hyb_touchUpBlock, OBJC_ASSOCIATION_COPY); [self removeTarget:self action:@selector(hybOnTouchUp:) forControlEvents:UIControlEventTouchUpInside]; if (hyb_touchUpBlock) { [self addTarget:self action:@selector(hybOnTouchUp:) forControlEvents:UIControlEventTouchUpInside]; }} - (HYBTouchUpBlock)hyb_touchUpBlock { return objc_getAssociatedObject(self, sHYBUIControlTouchUpEventBlockKey);} - (void)hybOnTouchUp:(UIButton *)sender { HYBTouchUpBlock touchUp = self.hyb_touchUpBlock; if (touchUp) { touchUp(sender); }} @end |