美文网首页
iOS 关联对象及其应用场景

iOS 关联对象及其应用场景

作者: 大成小栈 | 来源:发表于2020-09-07 18:40 被阅读0次

    在初学iOS时,对关联对象感到很疑惑,最近在看有关转场动画时又看到相关代码,觉得有必要记录一下。其中,关联对象的代码如下:

    //// .h中
    
    @interface UIViewController (Transition)
    
    @property (nonatomic, strong) MATransition *transition;
    
    @end
    
    
    //// .m中
    
    @implementation UIViewController (Transition)
    
    - (MATransition *)transition {
        return objc_getAssociatedObject(self, _cmd);
    }
    
    - (void)setTransition:(MATransition *)transition {
        objc_setAssociatedObject(self, @selector(transition), transition, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    @end
    

    上面代码的目的是,在分类中为已经存在的类添加属性

    1. 关联对象的基本使用

    在下面的实例中,可以看出关联对象的基本使用过程,我们可以把一个label关联道一个button实例上:

    
    #import "ViewController.h"
    
    // 引入头文件
    #import <objc/runtime.h>
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.title = @"首页";
        self.view.backgroundColor = UIColor.whiteColor;
        
        UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
        [view removeFromSuperview];
        view.backgroundColor = UIColor.whiteColor;
        
        //要关联的对象的键值,一般设置成静态的,用于获取关联对象的值
        static char UIButtonKey;
    
        //创建两个需要关联的对象
        UIButton * button = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 20, 20)];
        button.center = self.view.center;
        button.backgroundColor = [UIColor redColor];
        [self.view addSubview:button];
    
        UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 20, 20)];
        label.backgroundColor = [UIColor yellowColor];
        label.text =@"获取";
        //给对象确立关联(让label关联到button上)
        objc_setAssociatedObject(button, &UIButtonKey, label, OBJC_ASSOCIATION_RETAIN);
    
        //根据键值获取到关联对象
        UILabel * lab =  objc_getAssociatedObject(button, &UIButtonKey);
        NSLog(@"lab.text = %@",lab.text);
    
        //断开关联
        objc_setAssociatedObject(button, &UIButtonKey, nil, OBJC_ASSOCIATION_ASSIGN);
        UILabel * lab1 =  objc_getAssociatedObject(button, &UIButtonKey);
        NSLog(@"断开关联之后的lab1.text = %@",lab1.text);
    }
    @end
    

    主要方法说明:

    // key:要保证全局唯一,key与关联的对象是一一对应关系。必须全局唯一。通常用@selector(methodName)作为key。
    // value:要关联的对象。
    // policy:关联策略。有五种关联策略。
    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
    
    id objc_getAssociatedObject(id object, const void *key); 
    
    void objc_removeAssociatedObjects(id object);
    
    // objc_AssociationPolicy的值如下
    OBJC_ASSOCIATION_ASSIGN 等价于 @property(assign)。
    OBJC_ASSOCIATION_RETAIN_NONATOMIC等价于 @property(strong, nonatomic)。
    OBJC_ASSOCIATION_COPY_NONATOMIC等价于@property(copy, nonatomic)。
    OBJC_ASSOCIATION_RETAIN等价于@property(strong,atomic)。
    OBJC_ASSOCIATION_COPY等价于@property(copy, atomic)。
    

    2. 关联对象的应用场景

    iOS开发过程中,在正常的类中用@property定义一个属性变量:

    @interface DKObject : NSObject
    
    @property (nonatomic, strong) NSString *property;
    
    @end
    
    

    「@property」相当于一个宏定义标签,在使用编译上述代码时,系统会对这个变量做三件事:生成实例变量 _property、生成 getter 方法 - property、生成 setter 方法 - setProperty:

    @implementation DCObject {
        NSString *_property;
    }
    
    - (NSString *)property {
        return _property;
    }
    
    - (void)setProperty:(NSString *)property {
        _property = property;
    }
    
    @end
    

    @property 其实有元编程的思想,它能够为我们自动生成实例变量以及存取方法,而这三者构成了属性这个类似于语法糖的概念,为我们提供了更便利的点语法来访问属性:

    self.property <=> [self property]
    self.property = value <=> [self setProperty:value]
    
    

    但是,在分类中因为类的实例变量的布局已经固定,使用 @property 已经无法向固定的布局中添加新的实例变量(这样做可能会覆盖子类的实例变量)

    如果我们在定义的分类中加一个属性,那么在使用这个属性的时候就会报警告。如:

    @interface UIViewController (Transition)
    
    @property (nonatomic, strong) MATransition *transition;
    
    @end
    

    Property 'transition' requires method 'setTransition:' to be defined - use @dynamic or provide a method implementation in this category.
    Property 'transition' requires method 'transition' to be defined - use @dynamic or provide a method implementation in this category.

    该warnning提示 categoryProperty 属性的存取方法需要手动去实现,或者以@dynamic 在运行时实现这些方法。即,分类中的 @property 编译时,并没有为我们生成实例变量以及存取方法,而需要我们手动实现。

    下面使用关联对象的两个方法,来模拟构成属性的三个要素,就可以解决上述问题:

    @implementation UIViewController (Transition)
    
    - (MATransition *)transition {
        return objc_getAssociatedObject(self, _cmd);
    }
    
    - (void)setTransition:(MATransition *)transition {
        objc_setAssociatedObject(self, @selector(transition), transition, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    @end
    
    1. objc_getAssociatedObjectobjc_setAssociatedObject 两个方法,来模拟『属性』的存取,即,将变量transition与UIViewController实例关联在一起;
    2. _cmd 代指当前方法的选择子,也即 @selector(categoryProperty)
    3. OBJC_ASSOCIATION_RETAIN_NONATOMIC是objc_AssociationPolicy类型值,相当于属性修饰符nonatomic、assign...等。

    相关文章

      网友评论

          本文标题:iOS 关联对象及其应用场景

          本文链接:https://www.haomeiwen.com/subject/oekzsktx.html