将属性添加到UIViewController及其所有子类

时间:2012-01-15 14:02:00

标签: objective-c uiviewcontroller categories subclassing

正如标题所描述的那样,我希望为每个UIViewController和每个子类(如UITableViewController)添加自定义属性。我的第一个想法是创建一个类别,但后来我意识到不能在其中添加ivars,所以我无法合成属性。如果我只是子类UIViewController并在那里添加东西,它不会影响其他子类。

基本上我的目的是为我的'UINavigationController'子类添加一个自定义工具栏(不,由于各种原因我不能使用默认值)。它需要询问每个viewController是否需要工具栏,以及 - 在这种情况下 - 是否为toolbarItems数组。这有点像我相信UIToolbar的工作方式吗?

你会怎么做?

1 个答案:

答案 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操作相关联的内存管理(保留,复制)所花费的时间使得关联查找的开销相形见绌。