扩展类中添加属性
一般需要有个成员变量来保存数据,此时就需要用到运行时库了。只要用到的运行时函数有objc_getAssociatedObject,objc_setAssociatedObject。这两个函数有个参数是 key 是一个 const void * 的类型。你可以直接定义,或者是使用class_getClassMethod, class_getInstanceMethod 来当成 key 使用 运行时库没有提供弱引用的方式,只有如下定义
/* Associative References */
/**
* 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. */
};
扩展类中添加弱引用属性
首先要知道,为对象A关联一个动态属性B,那么当A被释放时,此时B若没有被其它对象引用,那么B也会被释放。这是我们解决弱引用的关键。简单描述下要实现的过程,后面会放上代码
假设有以下等式和类C,其中A和B为对象,weakObject是A的弱引用属性,key是关联的关键词, 类C非常简单,只需要在释放时去取消指定的关联
A.weakObject = B
//类C的伪实现
@interface C : NSObject
@property (nonatomic, weak) id object;
@property (nonatomic, assign) const void * key;
@end
@implementation C
- (void)dealloc
{
if (self.object && self.key)
{
objc_setAssociatedObject(self.object, self.key, nil, OBJC_ASSOCIATION_ASSIGN);
}
}
@end
关键的实现步骤
为实现此操作,需要有以下步骤
- A关联B,对应操作:objc_setAssociatedObject(A, key, B, OBJC_ASSOCIATION_ASSIGN);
- B关联自释放对象C: objc_setAssociatedObject(B, key, c1, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- A关联自释放对象C: objc_setAssociatedObject(A, key, c2, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
步骤1:为了用户可以存储变量 步骤2:当B被释放时,自动把与A关联的数据清除,此时要清空两处,一个为对应属性,一个辅助数据(步骤3创建的) 步骤3:当A本身被释放,但是B还存在时,可以清除B中被A关联的信息,此时只需要清空B的数据(步骤2创建)
可能会有些绕口,简单来说就是A挂了,就把B里有关A的信息清除;若是B挂了,就把A里的信息清除
当然还有其它事情要处理,其它的具体见以下Demo例子:
/******************************************************************************************
类名: JxAutoReleaseAssociated
说明: 用于辅助释放指定的关联
******************************************************************************************/
#pragma mark - ✍️JxAutoReleaseAssociated
@interface JxAutoReleaseAssociated : NSObject
@property (nonatomic, weak) id object1;
@property (nonatomic, assign) const void * key1;
@property (nonatomic, weak) id object2;
@property (nonatomic, assign) const void * key2;
@end
@implementation JxAutoReleaseAssociated
- (instancetype)init
{
self = [super init];
logoutObject(self);
return self;
}
- (void)dealloc
{
if (self.object1 && self.key1)
{
objc_setAssociatedObject(self.object1, self.key1, nil, OBJC_ASSOCIATION_ASSIGN);
}
if (self.object2 && self.key2)
{
objc_setAssociatedObject(self.object2, self.key2, nil, OBJC_ASSOCIATION_ASSIGN);
}
logoutObject(self);
}
@end
/******************************************************************************************
类名: MyObject
说明: 测试类
******************************************************************************************/
#pragma mark - ✍️MyObject
@interface MyObject : NSObject
@end
@implementation MyObject
- (instancetype)init
{
self = [super init];
logoutObject(self);
return self;
}
- (void)dealloc
{
logoutObject(self);
}
@end
/******************************************************************************************
类名: MyObject(Jx)
说明: 测试类的扩展类
******************************************************************************************/
#pragma mark - ✍️MyObject(Jx)
@interface MyObject(Jx)
@property (nonatomic, weak) id weakObject;
@end
@implementation MyObject(Jx)
- (id)weakObject
{
const void *key = class_getInstanceMethod([self class], @selector(weakObject));
return objc_getAssociatedObject(self, key);
}
- (void)setWeakObject:(id)aWeakObject
{
id previousWeakObject = self.weakObject;
if (previousWeakObject != aWeakObject)
{
const void *key = class_getInstanceMethod([self class], @selector(weakObject));
const void *clearAssObjKey = class_getInstanceMethod([self class], @selector(setWeakObject:));
if (previousWeakObject)
{
//清除之前关联的信息
JxAutoReleaseAssociated *assObj = objc_getAssociatedObject(previousWeakObject, key);
if (assObj && [assObj isKindOfClass:[JxAutoReleaseAssociated class]])
{
objc_setAssociatedObject(previousWeakObject, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
if (aWeakObject)
{
//关联弱引用对象 self->key->aWeakObject
objc_setAssociatedObject(self, key, aWeakObject, OBJC_ASSOCIATION_ASSIGN);
//当aWeakObject释放时:清空self对象的属性,相关数据
JxAutoReleaseAssociated *obj = [[JxAutoReleaseAssociated alloc] init];
obj.object1 = self;
obj.key1 = key;
obj.object2 = self;
obj.key2 = clearAssObjKey;
objc_setAssociatedObject(aWeakObject, key, obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//为self关联自释放对象:当本身释放时,清除已关联的对象的数据 self->clearAssObjKey->pWeakObject
obj = [[JxAutoReleaseAssociated alloc] init];
obj.object1 = aWeakObject;
obj.key1 = key;
objc_setAssociatedObject(self, clearAssObjKey, obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
}
@end
以宏实现,方便使用
typedef void(^JxEmptyBlock)();
/***********************************************************************
File : JxAutoRunBlockWhenFree
Author : Terry
Email : jxd524@163.com
CreateTime : 2017-03-09 14:34
LastModify :
Description: 当对象被释放时,自动执行相应的回调
************************************************************************/
#import <Foundation/Foundation.h>
@interface JxAutoRunBlockWhenFree : NSObject
+ (JxAutoRunBlockWhenFree *)Create:(JxEmptyBlock)aBlock;
@end
@implementation JxAutoRunBlockWhenFree
{
JxEmptyBlock mDeallocBlock;
}
- (instancetype)initWithDeallocBlock:(JxEmptyBlock)aDeallocBlock
{
self = [super init];
mDeallocBlock = aDeallocBlock;
return self;
}
- (void)dealloc
{
if (mDeallocBlock) mDeallocBlock();
}
+ (JxAutoRunBlockWhenFree *)Create:(JxEmptyBlock)aBlock
{
return [[JxAutoRunBlockWhenFree alloc] initWithDeallocBlock:aBlock];
}
@end
/**
定义弱引用的动态时属性
@param aName 获取
@param aSetter 设置
@param aType 类型
使用例子
RuntimeWeakImpl(weakObject, setWeakObject, id);
使用
A.weakObject = B;
当A或B被释放时,会清除相应的数据
*/
#define RuntimeWeakImpl(aName, aSetter, aType) \
- (aType)aName{ \
const void *key = class_getInstanceMethod([self class], @selector(aName)); \
return objc_getAssociatedObject(self, key); \
} \
- (void)aSetter:(id)aWeakObject { \
id previousWeakObject = [self aName]; \
if (previousWeakObject != aWeakObject) { \
const void *key = class_getInstanceMethod([self class], @selector(aName)); \
const void *clearAssObjKey = class_getInstanceMethod([self class], @selector(aSetter:)); \
if (previousWeakObject) { \
JxAutoRunBlockWhenFree *assObj = objc_getAssociatedObject(previousWeakObject, key); \
if (assObj && [assObj isKindOfClass:[JxAutoRunBlockWhenFree class]]) { \
objc_setAssociatedObject(previousWeakObject, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); \
} \
} \
if (aWeakObject) { \
__weak typeof(self) pWeakSelf = self; \
__weak typeof(aWeakObject) pWeakObject = aWeakObject; \
objc_setAssociatedObject(self, key, aWeakObject, OBJC_ASSOCIATION_ASSIGN); \
JxAutoRunBlockWhenFree *obj = [JxAutoRunBlockWhenFree Create:^{ \
__strong typeof(pWeakSelf) pSelf = pWeakSelf; \
if (pSelf){ \
objc_setAssociatedObject(pSelf, key, nil, 0); \
objc_setAssociatedObject(pSelf, clearAssObjKey, nil, 0); \
} \
}]; \
objc_setAssociatedObject(aWeakObject, key, obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); \
obj = [JxAutoRunBlockWhenFree Create:^{ \
__strong typeof(pWeakObject) pObject = pWeakObject; \
if (pObject){ \
objc_setAssociatedObject(pObject, key, nil, 0); \
} \
}]; \
objc_setAssociatedObject(self, clearAssObjKey, obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); \
} \
} \
}
实现上使用例子
/******************************************************************************************
类名: MyObject(Jx)
说明: 测试类的扩展类
******************************************************************************************/
#pragma mark - ✍️MyObject(Jx)
@interface MyObject(Jx)
@property (nonatomic, weak) id weakObject;
@end
@implementation MyObject(Jx)
RuntimeWeakImpl(weakObject, setWeakObject, id);
@end
mTestObject2 = [[MyObject alloc] init];
mTestObject2.weakObject = mTestObject;
结束