在iOS类扩展中定义属性

12

我想在一个类扩展中给UITableView添加一个属性:

@interface UITableViewController ()

@property NSString *entityString;

@end

然后我导入了这个扩展,然后在UITableViewController的子类中使用了entityString属性:

@implementation CustomerTableViewController

- (void)viewDidLoad {
    self.entityString = @"Customer";
    ...
    [super viewDidLoad];
}
...

苹果文档中写道:

编译器将会自动生成相关的访问器方法 (...) 在主类实现内部。

但是当我尝试执行它时,我得到了这个错误:

-[CustomerTableViewController setEntityString:]: unrecognized selector sent to instance 0x737b670

我做错什么了吗?也许这个属性不能被子类访问?

3个回答

17

尝试使用具有关联引用的类别(category),这样更加清晰,并且可以在所有 UIButton 实例上工作。

UIButton+Property.h

#import <Foundation/Foundation.h>

@interface UIButton(Property)

@property (nonatomic, retain) NSObject *property;

@end


UIButton+Property.m

#import "UIButton+Property.h"
#import <objc/runtime.h>

@implementation UIButton(Property)

static char UIB_PROPERTY_KEY;

@dynamic property;

-(void)setProperty:(NSObject *)property
{
  objc_setAssociatedObject(self, &UIB_PROPERTY_KEY, property, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(NSObject*)property
{
   return (NSObject*)objc_getAssociatedObject(self, &UIB_PROPERTY_KEY);
}

@end

//示例用法

#import "UIButton+Property.h"


UIButton *button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button1.property = @"HELLO";
NSLog(@"Property %@", button1.property);
button1.property = nil;
NSLog(@"Property %@", button1.property);

关联引用如何比类扩展更加干净? - vikingosegundo
5
它们更干净是因为它们有效(类扩展不行,因为当编译类的实现时,扩展不存在),但这并不意味着它们是干净的。向现有框架类添加状态是设计不良和高脆性的标志。虽然Prashant的答案有效,但这样做通常表明您应该重新考虑您的体系结构。 - bbum

8
一个 类扩展 用于声明额外的接口 -- 方法和属性 -- 其实现契约将在类的主要 @implementation 中得到满足。
这正是为什么你不能通过类扩展添加存储 -- 添加实例变量。类扩展只是一个接口,不多也不少。 @synthesize 是为@property声明创建存储空间的方式,但是@property@synthesize只能出现在类的@implementation中(无论是显式还是编译器的默认行为)。
由于您无法重新编译框架类,因此无法向其添加实例变量。
@prashat的答案是向现有类添加存储的一种方法。然而,采用这种方法通常是不可取的;随意将状态挂在框架类上是设计不良的标志,并且会使您的应用程序难以维护。
更好的方法是重新审视您的设计,了解为什么当前需要将状态附加到无法直接包含它的对象,并重构该要求。

类扩展中不能添加ivars,这取决于所使用的编译器吗? - tiguero
4
你可以在类扩展中声明实例变量和属性,但是除非编译器在编译类的@implementation之前看到了这些扩展,否则不会为其创建存储空间。 - bbum
谢谢澄清 - 我没有注意到alazaro实际上正在使用框架类的类扩展。 - tiguero

5
文档说明:
类扩展类似于匿名类别,不同之处在于它们声明的方法必须在相应类的主要@implementation块中实现。
当您使用@property时,它大致相当于声明访问器方法。因此,这意味着只有在您也是类的“主”@implementation块的作者时,才能执行此操作,而对于UITableViewController,您不是。您唯一的选择是类别,但它不能添加实例变量。
请参阅文档链接,并注意该页面的最后一行:
setValue:方法的实现必须出现在类的主@implementation块中(您无法在类别中实现它)。如果不是这种情况,则编译器发出警告,表示找不到setValue:方法定义。

我认为这不是我的情况,我没有使用ivar +访问器。然而,编译器对我的代码没有抱怨。错误出现在运行时。 - alazaro
4
一个@property是一个实例变量加上访问器。 - Chris Trahey
是的,我知道,但编译器会为您完成这项工作。我认为它可能会在主类中添加访问器,无论它是否属于我。 - alazaro
当使用@synthesize时,is一个ivar加上访问器(最新编译器默认情况下会使用),是的... - bbum
1
我认为文档不够清晰,或者可能是错误的,因为编译器没有生成任何访问器,正如它所说:“编译器将自动合成相关的访问器方法(…)在主类实现内部。”原因必须是UITableViewController是预编译的,无法添加它们。 - alazaro
同意;文档在这个问题上应该更清晰明了。请通过http://bugreporter.apple.com/提交一个bug,并在此处发布#。 - bbum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接