【问题标题】:Adding properties to UIViewController and all it's subclasses向 UIViewController 及其所有子类添加属性
【发布时间】:2012-01-15 14:01:59
【问题描述】:

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

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

你会怎么做?

【问题讨论】:

  • 为什么不继承UIViewController,然后让所有其他子类都继承自这个新类?你有多确定这些 ivars 实际上是所有UIViewController 的共同点? - 很可能事实证明它们不是,你最终会无缘无故地使用UIViewController 作为桶来装所有东西,包括厨房水槽。
  • 因为我无法更改例如“UITableViewControler”的超类。虽然我可以将每个 Apple 自己的 UIViewController 子类子类化,但这感觉不对。我会更新我关于共性的问题。

标签: objective-c uiviewcontroller categories subclassing


【解决方案1】:

可以使用 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。您应该知道使用关联对象会降低性能(速度和内存)。它们是一个巧妙的技巧,但您可能想问自己是否有更好的方法来做您想做的任何事情。 (如果您问我,技巧中最巧妙的部分是运行时将根据您指定的策略为您处理-dealloc 上的-releases;有关更多信息,请参阅文档。)

现在是宏:

#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 个关联存储支持的 iVar,我看到性能设置和获取速度降低了约 30%。恕我直言,这真的不是那么可怕。我期待更糟。与集合操作相关的内存管理(保留、复制)所花费的时间使关联查找的开销相形见绌。

【讨论】:

  • 是我遗漏了什么还是宏版本更难阅读?
  • 我认为这是主观的。当然,如果人们发现它更难阅读,则不必使用它。
猜你喜欢
  • 2013-07-01
  • 2011-07-04
  • 1970-01-01
  • 2013-08-25
  • 1970-01-01
  • 2014-06-09
  • 2011-05-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多