When to use associated objects in objective-c

2020-03-24 03:05发布

I have recently learned about associated objects in objective-c and how to implement them. From my understanding, they are helpful if you have a property that you want only a single instance of an object to have.

I can't think of any specific use cases for associated objects in objective-c (meaning use cases that I can't do using some other means).

Does anyone have specific examples of when to use associated objects?

标签: objective-c
4条回答
Luminary・发光体
2楼-- · 2020-03-24 03:12

You can use associated objects to add block handlers to classes that don't currently include them. For example, you can add block handling to UIButton so that instead of using addTarget:action:forControlEvents:, you could have addBlockHandler:forControlEvents: that takes a block to execute instead. I've also used this to add block handling to UIAlertView in place of using a delegate.

You can use a similar pattern to add block handling to pretty much any class that currently uses a delegate or dataSource.

查看更多
贼婆χ
3楼-- · 2020-03-24 03:21

It is useful when Apple has designed a class incorrectly, such that you can't subclass it.

A good example is NSURLSessionDownloadTask. Let's suppose that when you create the task you want to attach a piece of data to it, to be retrieved when the final task delegate message arrives.

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
    // retrieve data from downloadTask here
}

But you can't subclass NSURLSessionDownloadTask; you have to accept the task that NSURLSession gives you. So you can't attach any data to it by normal means. One solution in this situation is an associated object.

Personally I wish every object behaved like CALayer and CAAnimation, where you can attach as much data to it as you like in the form of key-value pairs, as if the object were secondarily a kind of NSDictionary.

查看更多
Explosion°爆炸
4楼-- · 2020-03-24 03:22

There are several situations when using an associated object is possible. Sometimes they are used when for some reason the object cannot be subclassed. However, there are more interesting scenarios:

  1. If the class of the object is unknown. For example, when you want to associate data with some black box objects. Imagine you are creating a new container class (a linked list) and you want to associate some data with every object added to the container.

  2. You want to add a property using a category. Normally, only methods can be added with a category. Using associated objects you can create categories that can store additional data.

查看更多
三岁会撩人
5楼-- · 2020-03-24 03:30

The common use case for an associated object is when you need to attach your own data to some object but you can't modify the class or create a subclass, either because it's not designed for subclassing or because you have no control over how the object is created. This is also a common reason to create a category, so it's common to use associated objects in a category.

For example, suppose you want to create a drawing app that supports drawing with multiple touches simultaneously. You want each touch to create an independent curve on the screen. It would be nice if you could add two properties to each touch: a path property (for constructing the curve as the touch moves) and a shapeLayer property (for displaying the curve on the screen).

But you have no control over the creation of UITouch objects. UIKit creates each UITouch object without any hook for you to use a subclass that adds your properties.

Instead, you can create a category on UITouch that adds your properties:

UITouch+Rob_Sketch.h

#import <UIKit/UIKit.h>

@interface UITouch (Rob_Sketch)

@property (nonatomic, readwrite, setter=Rob_setPath:) UIBezierPath *Rob_path;
@property (nonatomic, readwrite, setter=Rob_setShapeLayer:) CAShapeLayer *Rob_shapeLayer;

@end

UITouch+Rob_Sketch.m

#import "UITouch+Rob_Sketch.h"
#import <objc/runtime.h>

static const char *const UITouch_Rob_Sketch_PathKey = "UITouch_Rob_Sketch_PathKey";
static const char *const UITouch_Rob_Sketch_ShapeLayerKey = "UITouch_Rob_Sketch_ShapeLayerKey";

@implementation UITouch (Rob_Sketch)

- (UIBezierPath *)Rob_path {
    return objc_getAssociatedObject(self, UITouch_Rob_Sketch_PathKey);
}

- (void)Rob_setPath:(UIBezierPath *)path {
    objc_setAssociatedObject(self, UITouch_Rob_Sketch_PathKey, path,
        OBJC_ASSOCIATION_RETAIN);
}

- (CAShapeLayer *)Rob_shapeLayer {
    return objc_getAssociatedObject(self, UITouch_Rob_Sketch_ShapeLayerKey);
}

- (void)Rob_setShapeLayer:(CAShapeLayer *)layer {
    objc_setAssociatedObject(self, UITouch_Rob_Sketch_ShapeLayerKey, layer,
        OBJC_ASSOCIATION_RETAIN);
}

@end

Then, in your controller, you can use the category to access the path and shape layer of each touch:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    for (UITouch *touch in touches) {
        touch.Rob_path = [UIBezierPath bezierPath];
        [touch.Rob_path moveToPoint:[touch locationInView:touch.view]];
        touch.Rob_shapeLayer = [self newShapeLayer];
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    for (UITouch *touch in touches) {
        [touch.Rob_path addLineToPoint:[touch locationInView:touch.view]];
        touch.Rob_shapeLayer.path = touch.Rob_path.CGPath;
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    for (UITouch *touch in touches) {
        [touch.Rob_shapeLayer removeFromSuperlayer];
        [self addPathToDrawing:touch.Rob_path];
    }
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    for (UITouch *touch in touches) {
        [touch.Rob_shapeLayer removeFromSuperlayer];
    }
}
查看更多
登录 后发表回答