有一个类看起来像这样(我为了简洁省略了导入):
Base.h:
@interface Base : NSObject
@property (strong, readonly) NSString *something;
- (id)initWithSomething:(NSString *)something;
@end
Base.m:
@implementation Base
- (id)initWithSomething:(NSString *)something {
self = [super init];
if (self) _something = something;
return self;
}
@end
如您所见,'某事'属性是只读的。现在我想创建一个子类来覆盖该属性也是可写的:
Sub.h:
@interface Sub : Base
@property (strong) NSString *something;
@end
Sub.m:
@implementation Sub
@end
代码:
main.c中:
int main(int argc, const char * argv[]) {
@autoreleasepool {
Sub *o = [Sub new];
o.something = @"foo";
NSLog(@"%@", o.something);
}
return 0;
}
此代码导致:
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'
为什么?为什么找不到setSelector?
当我在子类中执行此操作时:
Sub.m:
@implementation Sub
@synthesize something = _something;
@end
一切正常。这是否意味着即使在@property
中定义为@interface
,子类'属性也不会默认合成?编译是否以某种方式'看到'从Base自动生成的getter并且不生成setter?为什么,我认为应该生成setter,因为它还不存在。我正在使用Xcode 4.6.2,该项目是一个Cli工具(类型基础),但在我的实际项目中也是如此,这是一个iPhone应用程序。
背景:我有一个重型对象(Base的实例)需要蓝牙连接到某些设备,我应该为某些功能创建一个视图控制器。为了便于测试,我不想连接到BT(实际上,我需要一个物理设备并在其上测试代码),我希望能够在模拟器中测试它。
我想到的是我只是创建了一个子类(Sub)来存根一些方法/属性并使用它代替,当代码准备就绪时我只需删除子类的代码,用正确的代码替换它的实例,用设备测试,提交和推送。它实际上工作正常,除了上面@property
的奇怪之处。
有人可以告诉我房产重叠是怎么回事吗?
答案 0 :(得分:28)
对于readonly属性,只合成一个getter方法,但没有setter方法。
编译子类时,编译器不知道 属性是如何实现的 在基类中(它可以是自定义getter而不是后备实例变量)。 所以它不能只在子类中创建一个setter方法。
如果您希望对子类中的相同实例变量具有写入权限,
你必须在基类中将它声明为@protected
(以便可以在子类中访问),重新声明属性
作为子类中的读写,并提供一个setter方法:
Base.h:
@interface Base : NSObject {
@protected
NSString *_something;
}
@property (strong, readonly) NSString *something;
- (id)initWithSomething:(NSString *)something;
@end
Sub.h:
@interface Sub : Base
@property (strong, readwrite) NSString *something;
@end
Sub.m:
@implementation Sub
-(void)setSomething:(NSString *)something
{
_something = something;
}
@end
您的解决方案
@synthesize something = _something;
使用单独的实例在子类中生成getter和setter方法
子类中的变量 _something
(不同
来自基类中的_something
。
这也有效,你应该知道self.something
指的是
基类和子类中的不同实例变量。为此
更明显的是,你可以在子类中使用不同的实例变量:
@synthesize something = _somethingElse;
答案 1 :(得分:4)
给出的答案完全正常。这是一个替代答案,显然是Apple likes a bit more。
您可以定义一个private extension的班级Base+Protected.h
文件,该文件需要包含在Base.m
和Sub.m
中。
然后,在此新文件中,您将该属性重新定义为readwrite
。
@interface Base ()
@property (strong, readwrite) NSString *something;
@end
此备选方案允许您使用访问者self.something
而不是ivar _something
。
注意:您仍然需要保留something
中Base.h
的定义。
答案 2 :(得分:-1)
我猜想,当子类中没有合成属性时,后备变量是相同的。所以在运行时,程序试图调用超类中的setSomething。但由于它不存在,因此会抛出异常。