【iOS分类、关联对象】如何使用关联对象给分类实现一个weak的属性

这篇具有很好参考价值的文章主要介绍了【iOS分类、关联对象】如何使用关联对象给分类实现一个weak的属性。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

如何使用关联对象给分类实现一个weak的属性

通过关联对象objc_setAssociatedObject中的策略policy可知,并不支持使用weak修饰对象属性:

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实现?

weak和assign的区别如下:

  • **assign:**对应的所有权类型为__unsafe_unretained,当修饰对象的时候,修饰的指针指向该对象,不会去持有该对象,也不会使retainCount +1,而在指向的对象被释放时,依然指向原来的对象地址,不会被自动置为nil,所以造成了野指针,是不安全的;
  • **weak:**弱引用,不会影响对象的释放,而当对象被释放时(引用计数为0),所有指向它的弱引用都会自定被置为nil,防止野指针,之后再向该对象发消息也不会崩溃,weak是安全的;

看以下测试代码,使用policy为OBJC_ASSOCIATION_ASSIGN的策略,会发生什么样的情况?

//定义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设置值,policy为OBJC_ASSOCIATION_ASSIGN
    objc_setAssociatedObject(self, @selector(viewController), viewController, OBJC_ASSOCIATION_ASSIGN);
}
- (UIViewController *)viewController {//取值
    return objc_getAssociatedObject(self, _cmd);
}
@end

【iOS分类、关联对象】如何使用关联对象给分类实现一个weak的属性,Objective-C,ios,分类,关联对象

使用assign修饰对象,当离开作用域后,产生野指针访问Crash(如图),如何避免这个问题?

1、通过中间对象的方式

1.1、利用OBJC_ASSOCIATION_RETAIN_NONATOMIC + weak来实现;

创建中间类:

@interface WeakObjWrapper : NSObject
@property(weak, nonatomic) id weakObj;
@end
@implementation WeakObjWrapper
- (instancetype)initWithWeakObject:(id)weakObj {
    if (self = [super init]) {
        _weakObj = weakObj;
    }
    return self;
}
@end

实现属性的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保存传递进来的值
    if (!warpper) {//warpper不存在则创建
        warpper = [[WeakObjWrapper alloc] initWithWeakObject:viewController];
    }
    else {//已经存在直接赋值
        warpper.weakObj = viewController;
    }
    //保存的实际上是warpper对象
    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实际上存储的是WeakObjWrapper对象,对WeakObjWrapper对象产生强引用,WeakObjWrapper对象内部弱持有传递进去的值,保证在对象释放的时候,自动把值设置为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

继续使用OBJC_ASSOCIATION_ASSIGN,利用runtime动态的创建传进去值的类的子类,在子类的dealloc中,将objc_setAssociatedObject中的value设置为nil,销毁并移除了关联对象,避免Crash,具体实现如下(具体使用已在注释中说明):

void weak_setAssociatedObject(id _Nonnull object,
                              const void * _Nonnull key,
                              id _Nullable value) {
    //派生一个子类,类名为WeakObjWrapper+value对应的类名
    const char *clsName = [[NSString stringWithFormat:@"WeakObjWrapper%@", [value class]] UTF8String];
    
    //获取派生的子类
    Class childCls = objc_getClass(clsName);
    
    //如果子类不存在,利用runtime动态的创建一个子类
    if (!childCls) {
        childCls = objc_allocateClassPair([value class], clsName, 0);
        objc_registerClassPair(childCls);
    }
    //注册dealloc方法SEL
    SEL sel = sel_registerName("dealloc");
    
    //获取dealloc对应的类型编码
    const char *deallocEncoding = method_getTypeEncoding(class_getInstanceMethod([value class], sel));
    
    // 注意:内部持有value此处需要弱引用处理一下
    __weak typeof(value) weakValue = value;
    
    // 创建一个指向在调用dealloc方法时调用指定block的函数指针
    IMP deallocImp = imp_implementationWithBlock(^(id _childCls) {
        //在子类的dealloc方法中将value设置为nil,避免崩溃
        objc_setAssociatedObject(object, key, nil, OBJC_ASSOCIATION_ASSIGN);
        //派生的子类的dealloc方法会被调用,父类的不再被调用,故在此处调用一下父类的
        ((void (*)(id, SEL))(void *)objc_msgSend)(weakValue, sel);
    });
    //给子类添加dealloc方法
    class_addMethod(childCls, sel, deallocImp, deallocEncoding);
    
    //将value对应的isa指向子类
    object_setClass(value, childCls);
    
    //设置关联对象
    objc_setAssociatedObject(object, key, value, OBJC_ASSOCIATION_ASSIGN);
}

注意:在派生的子类,添加的实现dealloc的方法中,重新调用一下父类的dealloc保证原有的类的释放关系不被破坏;调用(实现属性的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

总结

关联对象中如何实现weak属性?

  • 关联对象objc_setAssociatedObject中的策略policy可知,并不支持使用weak修饰对象属性;
  • 可以借助中间类(OBJC_ASSOCIATION_RETAIN_NONATOMIC + weak)来实现;
  • 也可以继续使用OBJC_ASSOCIATION_ASSIGN,利用runtime动态的创建传进去值的类的子类,在子类的dealloc中,将objc_setAssociatedObject中的value设置为nil,销毁并移除了关联对象,从而避免了Crash;

参考1:https://www.cnblogs.com/huangzs/p/14479408.html

参考2:https://developer.aliyun.com/article/1321927#:~:text=1%20关联对象objc_setAssociatedObject中的策略policy可知,并不支持使用weak修饰对象属性;%202%20可以借助中间类(OBJC_ASSOCIATION_RETAIN_NONATOMIC,%2B%20weak)来实现;%203%20也可以继续使用OBJC_ASSOCIATION_ASSIGN,利用runtime动态的创建传进去值的类的子类,在子类的dealloc中,将objc_setAssociatedObject中的value设置为nil,销毁并移除了关联对象,从而避免了Crash;文章来源地址https://www.toymoban.com/news/detail-833768.html

到了这里,关于【iOS分类、关联对象】如何使用关联对象给分类实现一个weak的属性的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 面试题:如何知道java对象被垃圾回收掉,用代码实现一个能监听对象被回收的功能

    Java中无法直接判断一个对象是否被垃圾回收掉,因为Java的垃圾回收机制是自动的,程序员并不需要手动清理对象。但是,如果我们想要知道一个对象何时被回收掉,可以使用 finalize() 方法。 每个Java对象都有一个 finalize() 方法,这个方法会在对象被垃圾回收前调用一次。我们

    2024年02月02日
    浏览(43)
  • 【iOS】—— 属性关键字及weak关键字底层原理

    参考博客:IOS开发基础——属性(copy strong weak等) 内存管理有关的: weak , assign , strong , retain , copy 线程安全有关的的: nonatomic , atomic 访问权限有关的的: readonly , readwrite (只读,可读写) 修饰变量的: const , static , extern 这些

    2024年02月16日
    浏览(42)
  • 【NLP】一个使用PyTorch实现图像分类的迁移学习实例

    在特征提取中,可以在预先训练好的网络结构后修改或添加一个简单的分类器,然后将源任务上预先训练好的网络作为另一个目标任务的特征提取器,只对最后增加的分类器参数重新学习,而预先训练好的网络参数不被修改或冻结。 在完成新任务的特征提取时使用的是源任务

    2024年02月14日
    浏览(42)
  • 实操-rapidminer进行关联分析、分类预测(使用相关算子,全流程讲解)

    目录 一、关联分析 1.构建过程 1.1导入数据 1.2检查缺失值,异常值 1.3 约减数据集中属性 2.对结果的评述 2.1 FP-Growth的支持度(Support)参数为0.95情况 2.2 不同min support对关联规则结果的影响 2.3 不同min confidence对结果的影响 3.促销政策 二、分类预测 1.对Titannic Unlabeld进行预测

    2024年02月05日
    浏览(52)
  • 数据挖掘18大算法实现以及其他相关经典DM算法:决策分类,聚类,链接挖掘,关联挖掘,模式挖掘、图算法,搜索算法等

    【机器学习入门与实践】入门必看系列,含数据挖掘项目实战:模型融合、特征优化、特征降维、探索性分析等,实战带你掌握机器学习数据挖掘 专栏详细介绍:【机器学习入门与实践】合集入门必看系列,含数据挖掘项目实战:数据融合、特征优化、特征降维、探索性分析

    2024年02月09日
    浏览(48)
  • 【NI USRP】每一个USRP是如何命名的呢,和原厂Ettus型号有什么关联呢?

    详细的硬件配置,非常有助于设备的选型。 如果您采购了X310+子板,是可以将其转化为对应的USRP型号的设备。 Ettus NI-USRP 频段 最大带宽 通道 B200mini 无 70 MHZ - 6 GHZ 56 MHz 1X1 B200mini-i 无 70 MHZ - 6 GHZ 56 MHz 1X1 B205mini-i 无 70 MHZ - 6 GHZ 56 MHz 1X1 B200 NI-2900 70 MHZ - 6 GHZ 56 MHz 1X1 B210 NI-2901

    2024年02月16日
    浏览(34)
  • IOS系统mobileconfig的作用,mobileconfig文件如何使用,苹果超级签实现

    .mobileconfig是苹果系统配置描述文件,目前常用于苹果超级签获取设备UDID。 官方介绍: 配置描述⽂件是XML ⽂件,包含以下内容:设备安全策略、VPN 配置信息、Wi-Fi 设置、APN 设置、Exchange帐户设置、邮件设置以及允许 iPhone 和 iPod touch 与企业系统配合使⽤的证书。 “iPhone配置

    2024年02月11日
    浏览(41)
  • list对象中如何根据对象中某个属性去重使用Java8流实现

    在 Java 8 的流操作中,可以使用 distinct 方法来对一个对象流进行去重,但是默认情况下它会使用对象的 equals() 方法来判断重复。如果你希望根据对象的某个属性进行去重,则可以使用 distinct 方法结合 map 方法来实现。 下面是一个示例代码,假设你有一个 List 对象 list,其中包

    2024年02月11日
    浏览(55)
  • Jmeter用于接口测试中,关联如何实现

    Jmeter用于接口测试时,后一个接口经常需要用到前一次接口返回的结果,应该如何获取前一次请求的结果值,应用于后一个接口呢,拿一个登录的例子来说明如何获取。 1、打开jmeter, 使用的3.3的版本,新建一个测试计划,在测试计划里新建一个线程组,新建一个登录的http请

    2024年02月04日
    浏览(49)
  • Java如何使用XMLBeans实现XML和Java对象的相互转换

    原文和更多内容: Java如何使用XMLBeans实现XML和Java对象的相互转换 (techdatafuture.com) 要使用XMLBeans实现XML和Java对象的相互转换,可以按照以下步骤进行操作: 1. 添加XMLBeans Maven依赖: dependency     groupIdorg.apache.xmlbeans/groupId     artifactIdxmlbeans/artifactId     version3.1.0/version /depen

    2024年02月16日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包