正如标题所描述的那样,我希望为每个UIViewController和每个子类(如UITableViewController)添加自定义属性。我的第一个想法是创建一个类别,但后来我意识到不能在其中添加ivars,所以我无法合成属性。如果我只是子类UIViewController并在那里添加东西,它不会影响其他子类。
基本上我的目的是为我的'UINavigationController'子类添加一个自定义工具栏(不,由于各种原因我不能使用默认值)。它需要询问每个viewController是否需要工具栏,以及 - 在这种情况下 - 是否为toolbarItems数组。这有点像我相信UIToolbar的工作方式吗?
你会怎么做?
答案 0 :(得分:14)
使用Objective-C的关联对象API可以添加属性(带有后备存储的)。我甚至为此目的写了一个小宏。但在宏之前,这就是“展开”的样子:
#import <objc/runtime.h>
@interface UIViewController (SOAdditions)
@property (atomic, readwrite, copy) NSString* myProperty;
@end
static void * const kMyPropertyAssociatedStorageKey = (void*)&kMyPropertyAssociatedStorageKey;
@implementation UIViewController (SOAdditions)
- (void)setMyProperty:(NSString *)myProperty
{
objc_setAssociatedObject(self, kMyPropertyAssociatedStorageKey, myProperty, OBJC_ASSOCIATION_COPY);
}
- (NSString*)myProperty
{
return objc_getAssociatedObject(self, kMyPropertyAssociatedStorageKey);
}
@end
您可以找到documentation for associated storage here。您应该知道使用关联对象会有性能(速度和内存)损失。他们是一个巧妙的伎俩,但你可能想问自己是否有更好的方法来做你想做的事情。 (如果你问我,这个技巧的最新部分是运行时将根据你指定的策略为-releases
处理-dealloc
;请参阅文档以获取更多信息。)< / p>
现在这是宏:
#ifndef ASSOCIATED_STORAGE_PROPERTY_IMP
#define THREE_WAY_PASTER_INNER(a, b, c) a ## b ## c
#define THREE_WAY_PASTER(x,y,z) THREE_WAY_PASTER_INNER(x,y,z)
#define ASSOCIATED_STORAGE_PROPERTY_IMP(type, setter, getter, policy) \
static void * const THREE_WAY_PASTER(__ASSOCIATED_STORAGE_KEY_, getter, __LINE__) = (void*)&THREE_WAY_PASTER(__ASSOCIATED_STORAGE_KEY_, getter,__LINE__); \
\
- (type)getter { return objc_getAssociatedObject(self, THREE_WAY_PASTER(__ASSOCIATED_STORAGE_KEY_, getter,__LINE__) ); } \
\
- (void)setter: (type)value { objc_setAssociatedObject(self, THREE_WAY_PASTER(__ASSOCIATED_STORAGE_KEY_, getter,__LINE__) , value, policy); } \
#endif
如果你在头文件中弹出它,那么上面的例子可以简化为:
#import <objc/runtime.h>
@interface UIViewController (SOAdditions)
@property (atomic, readwrite, copy) NSString* myProperty;
@end
@implementation UIViewController (SOAdditions)
ASSOCIATED_STORAGE_PROPERTY_IMP(NSString*, setMyProperty, myProperty, OBJC_ASSOCIATION_COPY)
@end
不言而喻,您在@property声明中声明的策略(即保留/复制/分配,原子/非原子)需要与使用宏时使用的策略匹配(和/或调用底层API ,如果你没有使用宏,否则,你最终会泄漏内存(或崩溃。)
另外,我会再次说明强调。这个技巧不是“免费的”所以请务必测量性能并确保使用它获得的任何好处都是值得的。
编辑:我将对有关对象存储的性能损失的强烈和可怕的警告进行回溯。有一个惩罚,但快速调查告诉我,以这种方式实施的属性并没有比他们的@synthesized和ivar支持的等价物差。该测试有点人为,但有10,000,000个对象,每个对象有5个关联存储支持的iVars,我看到性能设置和获取速度降低约30%。这真的不是那么可怕,恕我直言。我期待更糟糕。执行与set操作相关联的内存管理(保留,复制)所花费的时间使得关联查找的开销相形见绌。