我经常发现我知道基类的某个属性永远是子类中的某个类型。例如,在下面的示例中,属性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.
答案 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中没有像函数重载或虚函数这样的东西)。
您必须检查您的班级设计。