覆盖Objective-C中的@property声明

时间:2011-08-16 22:36:24

标签: objective-c

我经常发现我知道基类的某个属性永远是子类中的某个类型。例如,在下面的示例中,属性obj将始终是Derived中的NSString对象。但是,我需要此属性为类Base中更通用的id类型。

@interface Base
@property (strong, nonatomic) id obj;
@end

@implementation Base
//@synthesize obj = obj_;
@dynamic obj;
@end


@interface Derived : Base
@property (strong, nonatomic) NSString *obj;
@end

@implementation Derived
@synthesize obj = obj_;
@end

这段代码是否正确?我担心@synthesize会出现两次。这是创建两个属性,还是Derived中的@synthesize声明覆盖Base中的一个?

编辑:在Base中将@synthesize更改为@dynamic更有意义。

修改:这需要iOS SDK 5.

2 个答案:

答案 0 :(得分:48)

子类可以更改与方法关联的类型。通常,子类可以专门化返回类型,并且可以使参数类型更通用。实际上有一个名字,但我不记得它是什么。无论如何,这是理性的:

返回类型

如果我有课

@interface A
- (id)foo;
@end

和另一个班级

@interface B : A
- (NSString *)foo;
@end

我有一个实例B* b,我可以将其转换为A*并仍然符合方法-[A foo]的类型签名,因为任何NSString*也是id

然而,我不能使这更加笼统。如果相反我有

@interface A
- (NSString *)foo;
@end

@interface B : A
- (id)foo;
@end

我有一个实例B* b,我将其转发到A*,然后[(A*)b foo]的类型为NSString *,但实际值可能是id 1}},因为那是我声明-[B foo]的类型。这违反了类型系统。

参数

如果我有课

@interface A
- (void)foo:(NSString *)obj;
@end

和另一个班级

@interface B : A
- (void)foo:(id)obj;
@end

我有一个实例B* b并将其转发到A*,然后[(A*)b foo:obj]的任何有效参数也符合-[B foo:]的类型,因为任何{ {1}}也是NSString *

但是,如果我有以下

id

我有一个实例@interface A - (void)foo:(id)obj; @end @interface B : A - (void)foo:(NSString *)obj; @end ,我将其转发到B* b,然后我可以将A*传递给id,但基础类[(A*)b foo:obj]只需要B s。因此我违反了类型系统。

属性

这是一个棘手的问题。声明属性的类型时,您将声明getter 的返回类型和 setter的参数类型。根据上述规则,这意味着您无法更改属性的类型,因为在这两种情况之一中您将违反类型系统。


以上是理论。在实践中,我不知道GCC或Clang是否强制执行这些约束。他们可能认为程序员最了解,并且不正确地推广或专门化类型会默默地破坏你背后的类型系统。你必须要做实验。但是如果编译器确实是正确的,那么它将禁止泛化返回类型和专门化参数。这意味着它将禁止改变财产的类型。

即使编译器允许,您也可能不应该这样做。默默地打破类型系统是引入错误的好方法,也是糟糕架构的一个指标。

答案 1 :(得分:1)

你实际上不能这样做。对我来说,我收到错误

property 'obj' attempting to use ivar 'obj_' declared in super class of 'Derived'

所以我认为很明显。即使您没有使用@synthesize并自己定义函数,也只调用Derived版本(在ObjC中没有像函数重载或虚函数这样的东西)。

您必须检查您的班级设计。