如何使用關(guān)聯(lián)對(duì)象給分類實(shí)現(xiàn)一個(gè)weak的屬性
通過(guò)關(guān)聯(lián)對(duì)象objc_setAssociatedObject中的策略policy可知,并不支持使用weak修飾對(duì)象屬性:
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, //assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //strong, nonatomic
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, //copy, nonatomic
OBJC_ASSOCIATION_RETAIN = 01401, //strong, atomic
OBJC_ASSOCIATION_COPY = 01403 //copy, atomic
};
思考:能否用assign實(shí)現(xiàn)?
weak和assign的區(qū)別如下:
- **assign:**對(duì)應(yīng)的所有權(quán)類型為_(kāi)_unsafe_unretained,當(dāng)修飾對(duì)象的時(shí)候,修飾的指針指向該對(duì)象,不會(huì)去持有該對(duì)象,也不會(huì)使retainCount +1,而在指向的對(duì)象被釋放時(shí),依然指向原來(lái)的對(duì)象地址,不會(huì)被自動(dòng)置為nil,所以造成了野指針,是不安全的;
- **weak:**弱引用,不會(huì)影響對(duì)象的釋放,而當(dāng)對(duì)象被釋放時(shí)(引用計(jì)數(shù)為0),所有指向它的弱引用都會(huì)自定被置為nil,防止野指針,之后再向該對(duì)象發(fā)消息也不會(huì)崩潰,weak是安全的;
看以下測(cè)試代碼,使用policy為OBJC_ASSOCIATION_ASSIGN的策略,會(huì)發(fā)生什么樣的情況?
//定義Person類
@interface Person : NSObject
@end
@implementation Person
- (void)dealloc {
NSLog(@"Person dealloc");
}
@end
@interface Person (Test)
//在分類中聲明UIViewController屬性,用assign修飾
@property(assign, nonatomic) UIViewController *viewController;
@end
@implementation Person (Test)
- (void)setViewController:(UIViewController *)viewController {
//利用objc_setAssociatedObject設(shè)置值,policy為OBJC_ASSOCIATION_ASSIGN
objc_setAssociatedObject(self, @selector(viewController), viewController, OBJC_ASSOCIATION_ASSIGN);
}
- (UIViewController *)viewController {//取值
return objc_getAssociatedObject(self, _cmd);
}
@end
使用assign修飾對(duì)象,當(dāng)離開(kāi)作用域后,產(chǎn)生野指針訪問(wèn)Crash(如圖),如何避免這個(gè)問(wèn)題?
1、通過(guò)中間對(duì)象的方式
1.1、利用OBJC_ASSOCIATION_RETAIN_NONATOMIC + weak來(lái)實(shí)現(xiàn);
創(chuàng)建中間類:
@interface WeakObjWrapper : NSObject
@property(weak, nonatomic) id weakObj;
@end
@implementation WeakObjWrapper
- (instancetype)initWithWeakObject:(id)weakObj {
if (self = [super init]) {
_weakObj = weakObj;
}
return self;
}
@end
實(shí)現(xiàn)屬性的setter和getter:
@interface Person (Test)
@property(weak, nonatomic) UIViewController *viewController;
@end
@implementation Person (Test)
- (void)setViewController:(UIViewController *)viewController {
WeakObjWrapper *warpper = objc_getAssociatedObject(self, @selector(viewController));
//用warpper保存?zhèn)鬟f進(jìn)來(lái)的值
if (!warpper) {//warpper不存在則創(chuàng)建
warpper = [[WeakObjWrapper alloc] initWithWeakObject:viewController];
}
else {//已經(jīng)存在直接賦值
warpper.weakObj = viewController;
}
//保存的實(shí)際上是warpper對(duì)象
objc_setAssociatedObject(self, @selector(viewController), warpper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIViewController *)viewController {
//獲取到warpper
WeakObjWrapper *warpper = objc_getAssociatedObject(self, _cmd);
//取出warpper中的值
return warpper.weakObj;
}
@end
objc_setAssociatedObject實(shí)際上存儲(chǔ)的是WeakObjWrapper對(duì)象,對(duì)WeakObjWrapper對(duì)象產(chǎn)生強(qiáng)引用,WeakObjWrapper對(duì)象內(nèi)部弱持有傳遞進(jìn)去的值,保證在對(duì)象釋放的時(shí)候,自動(dòng)把值設(shè)置為nil,避免了崩潰;
1.2、借助OBJC_ASSOCIATION_COPY_NONATOMIC和弱引用block
-(void)setWeakvalue:(NSObject *)weakvalue {
__weak typeof(weakvalue) weakObj = weakvalue;
typeof(weakvalue) (^block)() = ^(){
return weakObj;
};
objc_setAssociatedObject(self, weakValueKey, block, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSObject *)weakvalue {
id (^block)() = objc_getAssociatedObject(self, weakValueKey);
return block();
}
2、借助runtime
繼續(xù)使用OBJC_ASSOCIATION_ASSIGN,利用runtime動(dòng)態(tài)的創(chuàng)建傳進(jìn)去值的類的子類,在子類的dealloc中,將objc_setAssociatedObject中的value設(shè)置為nil,銷毀并移除了關(guān)聯(lián)對(duì)象,避免Crash,具體實(shí)現(xiàn)如下(具體使用已在注釋中說(shuō)明):
void weak_setAssociatedObject(id _Nonnull object,
const void * _Nonnull key,
id _Nullable value) {
//派生一個(gè)子類,類名為WeakObjWrapper+value對(duì)應(yīng)的類名
const char *clsName = [[NSString stringWithFormat:@"WeakObjWrapper%@", [value class]] UTF8String];
//獲取派生的子類
Class childCls = objc_getClass(clsName);
//如果子類不存在,利用runtime動(dòng)態(tài)的創(chuàng)建一個(gè)子類
if (!childCls) {
childCls = objc_allocateClassPair([value class], clsName, 0);
objc_registerClassPair(childCls);
}
//注冊(cè)dealloc方法SEL
SEL sel = sel_registerName("dealloc");
//獲取dealloc對(duì)應(yīng)的類型編碼
const char *deallocEncoding = method_getTypeEncoding(class_getInstanceMethod([value class], sel));
// 注意:內(nèi)部持有value此處需要弱引用處理一下
__weak typeof(value) weakValue = value;
// 創(chuàng)建一個(gè)指向在調(diào)用dealloc方法時(shí)調(diào)用指定block的函數(shù)指針
IMP deallocImp = imp_implementationWithBlock(^(id _childCls) {
//在子類的dealloc方法中將value設(shè)置為nil,避免崩潰
objc_setAssociatedObject(object, key, nil, OBJC_ASSOCIATION_ASSIGN);
//派生的子類的dealloc方法會(huì)被調(diào)用,父類的不再被調(diào)用,故在此處調(diào)用一下父類的
((void (*)(id, SEL))(void *)objc_msgSend)(weakValue, sel);
});
//給子類添加dealloc方法
class_addMethod(childCls, sel, deallocImp, deallocEncoding);
//將value對(duì)應(yīng)的isa指向子類
object_setClass(value, childCls);
//設(shè)置關(guān)聯(lián)對(duì)象
objc_setAssociatedObject(object, key, value, OBJC_ASSOCIATION_ASSIGN);
}
注意:在派生的子類,添加的實(shí)現(xiàn)dealloc的方法中,重新調(diào)用一下父類的dealloc保證原有的類的釋放關(guān)系不被破壞;調(diào)用(實(shí)現(xiàn)屬性的getter和setter):
@interface Person (Test)
@property(assign, nonatomic) UIViewController *viewController;
@end
@implementation Person (Test)
- (void)setViewController:(UIViewController *)viewController {
weak_setAssociatedObject(self, @selector(viewController), viewController);
}
- (UIViewController *)viewController {
return objc_getAssociatedObject(self, _cmd);
}
@end
總結(jié)
關(guān)聯(lián)對(duì)象中如何實(shí)現(xiàn)weak屬性?
- 關(guān)聯(lián)對(duì)象objc_setAssociatedObject中的策略policy可知,并不支持使用weak修飾對(duì)象屬性;
- 可以借助中間類(OBJC_ASSOCIATION_RETAIN_NONATOMIC + weak)來(lái)實(shí)現(xiàn);
- 也可以繼續(xù)使用OBJC_ASSOCIATION_ASSIGN,利用runtime動(dòng)態(tài)的創(chuàng)建傳進(jìn)去值的類的子類,在子類的dealloc中,將objc_setAssociatedObject中的value設(shè)置為nil,銷毀并移除了關(guān)聯(lián)對(duì)象,從而避免了Crash;
參考1:https://www.cnblogs.com/huangzs/p/14479408.html文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-833768.html
參考2:https://developer.aliyun.com/article/1321927#:~:text=1%20關(guān)聯(lián)對(duì)象objc_setAssociatedObject中的策略policy可知,并不支持使用weak修飾對(duì)象屬性;%202%20可以借助中間類(OBJC_ASSOCIATION_RETAIN_NONATOMIC,%2B%20weak)來(lái)實(shí)現(xiàn);%203%20也可以繼續(xù)使用OBJC_ASSOCIATION_ASSIGN,利用runtime動(dòng)態(tài)的創(chuàng)建傳進(jìn)去值的類的子類,在子類的dealloc中,將objc_setAssociatedObject中的value設(shè)置為nil,銷毀并移除了關(guān)聯(lián)對(duì)象,從而避免了Crash;文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-833768.html
到了這里,關(guān)于【iOS分類、關(guān)聯(lián)對(duì)象】如何使用關(guān)聯(lián)對(duì)象給分類實(shí)現(xiàn)一個(gè)weak的屬性的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!