我最近了解了objective-c中的关联对象以及如何实现它们。根据我的理解,如果您只拥有一个对象的单个实例,则它们会很有用。
我无法想到objective-c中关联对象的任何特定用例(意味着我不能使用其他方法做的用例)。
有没有人具有何时使用关联对象的具体示例?
答案 0 :(得分:3)
当Apple设计的类不正确时,它非常有用,因此您无法将其子类化。
一个很好的例子是NSURLSessionDownloadTask。假设您在创建任务时要将一段数据附加到该任务,以便在最终任务委托消息到达时进行检索。
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
// retrieve data from downloadTask here
}
但你不能继承NSURLSessionDownloadTask;你必须接受NSURLSession给你的任务。因此,您无法通过常规方式将任何数据附加到其中。在这种情况下,一种解决方案是相关对象。
我个人希望每个对象都表现得像CALayer和CAAnimation,你可以在其中以键值对的形式附加尽可能多的数据,就好像该对象其次是一种NSDictionary。
答案 1 :(得分:3)
关联对象的常见用例是当您需要将自己的数据附加到某个对象但不能修改该类或创建子类时,因为它不是为子类设计的,或者因为您无法控制如何创建对象。这也是创建类别的常见原因,因此在类别中使用关联对象是很常见的。
例如,假设您要创建一个支持同时进行多次触摸绘图的绘图应用程序。您希望每次触摸都在屏幕上创建独立曲线。如果你可以为每次触摸添加两个属性,那就太好了:path
属性(用于在触摸移动时构造曲线)和shapeLayer
属性(用于在屏幕上显示曲线)。 / p>
但是你无法控制UITouch
个对象的创建。 UIKit创建每个UITouch
对象,没有任何钩子,您可以使用添加属性的子类。
相反,您可以在UITouch
上创建一个添加属性的类别:
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
然后,在您的控制器中,您可以使用类别访问每次触摸的路径和形状图层:
- (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];
}
}
答案 2 :(得分:2)
您可以使用关联对象将块处理程序添加到当前不包含它们的类中。例如,您可以将块处理添加到UIButton
,这样您就可以使addTarget:action:forControlEvents:
代替使用addBlockHandler:forControlEvents:
来执行块。我还使用它来将块处理添加到UIAlertView
而不是使用委托。
您可以使用类似的模式将块处理添加到当前使用委托或数据源的任何类中。
答案 3 :(得分:1)
有几种情况可以使用关联对象。有时它们在出于某种原因使用时无法进行子类化。但是,有更多有趣的场景:
如果对象的类未知。例如,当您想要将数据与某些黑盒子对象相关联时。想象一下,您正在创建一个新的容器类(链接列表),并且您希望将一些数据与添加到容器中的每个对象相关联。
您想使用类别添加属性。通常,只能使用类别添加方法。使用关联对象,您可以创建可以存储其他数据的类别。