向 UIViewController 及其所有子类添加属性

发布于 2024-12-27 11:31:52 字数 344 浏览 2 评论 0原文

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

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

你会怎么做?

As the title describes I'm looking to add custom properties for every UIViewController and every subclass (like UITableViewController). My first thought was to create a category, but then I realized one cannot add ivars in them, so I can't synthesize the properties. If I simply subclass UIViewController and add stuff there, It wouldn't affect other subclasses.

Basically my purpose with this is to add a custom toolbar to my ´UINavigationController´ subclass (No, I can't use the default for various reasons). It needs to ask each viewController if it wants a toolbar, and - in that case - for an array of toolbarItems. It's kind of like how the UIToolbar works I believe?

How would you do it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

岁月如刀 2025-01-03 11:31:52

使用 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

您可以找到 此处关联存储的文档。您应该意识到使用关联对象会带来性能(速度和内存)损失。它们是一个巧妙的技巧,但您可能想问自己是否有更好的方法来完成您想要做的事情。 (如果你问我的话,这个技巧最巧妙的部分是运行时将根据你指定的策略为你处理 -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%。恕我直言,这实际上并不是那么可怕。我原以为会更糟。与集合操作相关的内存管理(保留、复制)所花费的时间使关联查找的开销相形见绌。

Adding properties (with backing storage) is possible using Objective-C's associated object API. I even have a little macro I wrote for the purpose. But before the macro, here's what it would look like "unrolled":

#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

You can find the documentation for associated storage here. You should be aware that there is a performance (speed and memory) penalty for using associated objects. They're a neat trick, but you might want to ask yourself if there's a better way to do whatever it is you're trying to do. (The neatest part of the trick, if you ask me, is that the runtime will handle the -releases on -dealloc for you, according to the policy you specify; See the docs for more info.)

Now here's the macro:

#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

If you pop that in your header file, then the above example can be reduced to this:

#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

It should go without saying that the policy (i.e. retain/copy/assign, atomic/nonatomic) you declare in the @property declaration needs to match the policy that you use when using the macro (and/or calling the underlying API, if you're not using the macro) otherwise, you'll end up leaking memory (or crashing.)

Also, I'll say it again for emphasis. This trick isn't "free" so please be sure to measure performance and make sure that whatever benefits you're getting by using this are worth it.

EDIT: I'm going to backtrack a bit on my emphatic and dire warnings about the performance penalties of associated object storage. There is a penalty, but a quick investigation tells me that properties implemented this way are not all that much worse than their @synthesized and ivar-backed equivalents. The test is a bit contrived, but with 10,000,000 objects, each with 5 associative-storage-backed iVars, I'm seeing ~30% slower performance setting and getting. That's really not all that dire, IMHO. I was expecting much worse. The time spent doing the memory management (retains, copies) associated with the set operations is dwarfing the overhead from the associative lookups.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文