iOS 关联对象及其应用场景

在初学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 

@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 关联对象及其应用场景)