覆盖子类中的readonly属性

wuj*_*jek 18 properties objective-c

有一个类看起来像这样(我为了简洁省略了导入):

Base.h:

@interface Base : NSObject
@property (strong, readonly) NSString *something;
- (id)initWithSomething:(NSString *)something;
@end
Run Code Online (Sandbox Code Playgroud)

Base.m:

@implementation Base
- (id)initWithSomething:(NSString *)something {
    self = [super init];
    if (self) _something = something;
    return self;
}
@end
Run Code Online (Sandbox Code Playgroud)

如你所见,'something'属性是readonly.现在我想创建一个子类来覆盖该属性也是可写的:

Sub.h:

@interface Sub : Base
@property (strong) NSString *something;
@end
Run Code Online (Sandbox Code Playgroud)

Sub.m:

@implementation Sub
@end
Run Code Online (Sandbox Code Playgroud)

和代码:

main.c中:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Sub *o = [Sub new];
        o.something = @"foo";
        NSLog(@"%@", o.something);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

此代码导致:

    2013-09-07 13:58:36.970 ClilTest[3094:303] *** Terminating app due to uncaught
    exception 'NSInvalidArgumentException', reason: '-[Sub setSomething:]: unrecognized
    selector sent to instance 0x100109ff0'
Run Code Online (Sandbox Code Playgroud)

这是为什么?为什么找不到setSelector?

当我在子类中执行此操作时:

Sub.m:

@implementation Sub
@synthesize something = _something;
@end
Run Code Online (Sandbox Code Playgroud)

一切正常.这是否意味着子类属性默认情况下不合成的,即使它被定义为@property@interface?编译是否以某种方式'看到'从Base自动生成的getter并且不生成setter?为什么,我认为应该生成setter,因为它还不存在.我正在使用Xcode 4.6.2,该项目是一个Cli工具(类型基础),但在我的实际项目中也是如此,这是一个iPhone应用程序.

背景:我有一个重型对象(Base的实例),需要蓝牙连接到某些设备,我应该为某些功能创建一个视图控制器.为了便于测试,我不想连接到BT(实际上,我需要一个物理设备并在其上测试代码),我希望能够在模拟器中测试它.我想到的是我只是创建了一个子类(Sub)来存根一些方法/属性并使用它代替,当代码准备就绪时我只需删除子类的代码,用正确的代码替换它的实例,用设备测试,提交和推送.它实际上工作正常,除了@property上面的奇怪之处.

有人可以告诉我房地产压倒怎么回事?

Mar*_*n R 28

对于readonly属性,只合成一个getter方法,但没有setter方法.

在编译子类时,编译器不知道如何在基类中实现该属性(它可以是自定义getter而不是后备实例变量).所以它不能只在子类中创建一个setter方法.


如果你想从子类中获得对同一个实例变量的写访问权限,你必须@protected在基类中声明它(以便它可以在子类中访问),在子类中将该属性重新声明为read-write,并提供一个setter方法:

Base.h:

@interface Base : NSObject {
@protected
    NSString *_something;
}
@property (strong, readonly) NSString *something;
- (id)initWithSomething:(NSString *)something;
@end
Run Code Online (Sandbox Code Playgroud)

Sub.h:

@interface Sub : Base
@property (strong, readwrite) NSString *something;
@end
Run Code Online (Sandbox Code Playgroud)

Sub.m:

@implementation Sub
-(void)setSomething:(NSString *)something
{
    _something = something;
}
@end
Run Code Online (Sandbox Code Playgroud)

你的解决方案

@synthesize something = _something;
Run Code Online (Sandbox Code Playgroud)

使用子类中的单独实例变量 _something(_something与基类不同)在子类中生成getter和setter方法.

这也适用,你应该知道它self.something引用了基类和子类中的不同实例变量.为了使其更加明显,您可以在子类中使用不同的实例变量:

@synthesize something = _somethingElse;
Run Code Online (Sandbox Code Playgroud)


Fab*_* PH 5

给定的答案工作得很好。这是另一种答案,显然 Apple更喜欢.

您可以定义私有扩展类的,一个Base+Protected.h文件,它需要被纳入Base.mSub.m

然后,在这个新文件中,您将属性重新定义为readwrite.

@interface Base ()
@property (strong, readwrite) NSString *something;
@end
Run Code Online (Sandbox Code Playgroud)

这种替代方法允许您使用访问器self.something而不是 ivar _something

注意:您仍然需要保留的定义something在你的Base.h原样。