【问题标题】:Defining a property in iOS class extension在 iOS 类扩展中定义一个属性
【发布时间】:2012-08-28 11:28:25
【问题描述】:

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

@interface UITableViewController ()

@property NSString *entityString;

@end

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

@implementation CustomerTableViewController

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

Apple documentation 说:

编译器会自动合成相关的访问器 主类中的方法(...) 实施。

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

-[CustomerTableViewController setEntityString:]:无法识别的选择器发送到实例 0x737b670

我做错了什么?也许子类无法访问该属性?

【问题讨论】:

    标签: objective-c automatic-ref-counting ios5.1


    【解决方案1】:

    文档状态:

    类扩展类似于匿名类别,只是它们声明的方法必须在相应类的主@implementation 块中实现。

    当你使用@property时,大致相当于声明访问器方法。所以这意味着你只能做这样的事情,如果你也是该类的“主要”@implementation 块的作者,而 UITableViewController,你不是。

    您在这里唯一的选择是类别,它不能添加实例变量。

    The docs link,并注意该页面的最后一行:

    setValue: 方法的实现必须出现在该类的主 @implementation 块中(您不能在类别中实现它)。如果不是这种情况,编译器会发出一条警告,指出它找不到 setValue: 的方法定义。

    【讨论】:

    • 我认为这不是我的情况,我没有使用 ivar + 访问器。但是,编译器不会抱怨我的代码。错误在运行时。
    • a @property 一个 ivar plus 访问器。
    • 是的,我知道,但是编译器会为您完成这项工作。我认为它也可能在主类中添加访问器,无论它是否属于我。
    • 在使用 @synthesize 时是一个 ivar plus 访问器(这是最新编译器的默认设置),是的....
    • 我认为文档不够清晰,或者可能是错误的,因为编译器没有生成任何访问器,因为它说:“编译器将自动在内部合成相关的访问器方法(...)主要类实现。”原因一定是UITableViewController是预编译的,无法添加。
    【解决方案2】:

    尝试使用具有关联引用的类别。它更简洁,适用于所有 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);
    

    【讨论】:

    • 关联引用比类扩展更干净吗?
    • 它们更干净,因为它们可以工作(类扩展不是因为在编译类的@implementation 时扩展不存在),但这并不能使它们变得干净。向现有框架类添加状态是糟糕设计和高度脆弱性的标志。虽然 prashant 的回答有效,但这样做通常表明您应该重新考虑您的架构。
    【解决方案3】:

    类扩展用于声明额外的接口——方法和属性——其实现契约将在类的主要@implementation中得到满足。

    这正是您无法通过类扩展添加存储(添加 ivars)的原因。类扩展是一个接口,不多也不少。 @synthesize 是为 @property 声明创建存储的原因,但 @property@synthesize 只能出现在类的 @implementation 中(无论是显式还是作为编译器的默认行为)。

    由于无法重新编译框架类,因此无法向其中添加 ivars。

    @prashat 的答案是向现有类添加存储的一种方法。然而,走这条路通常是不可取的。随意挂起框架类的状态是设计不佳的标志,并且随着时间的推移会使您的应用程序变得更加难以维护。

    最好重新审视您的设计,了解您目前需要将状态附加到不能直接包含它的对象的原因,然后重构该要求。

    【讨论】:

    • 不能将 ivars 添加到类扩展中还是这取决于所使用的编译器?
    • 您可以在类扩展中声明 ivars 和属性,但除非在编译类的 @implementation 之前编译器看到扩展,否则不会创建此类的存储。
    • 感谢您的澄清 - 我没有注意到 alazaro 实际上正在使用框架类的类扩展
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-03
    • 2014-03-27
    • 1970-01-01
    • 2021-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多