如果子类引用超类ivar,则合成不相关的属性将失败

时间:2011-07-25 21:20:32

标签: objective-c ios gcc properties llvm

编辑:我刚注意到其他Stack Overflow问题提出了同样的问题:Why does a subclass @property with no corresponding ivar hide superclass ivars?

这是一些有趣的行为,我无法在官方或非官方(博客,推文,SO问题等)中找到任何记录。我把它归结为它的本质,并在一个新的Xcode项目中进行了测试,但我无法解释它。

MyBaseClass有一个实例变量:

@interface MyBaseClass : NSObject {
    NSObject *fooInstanceVar;
}
@end

MySubclass扩展了MyBaseClass,并声明了一个完全不相关的属性(也就是说,该属性不是由实例变量支持的):

#import "MyBaseClass.h"
@interface MySubclass : MyBaseClass { }
@property (nonatomic, retain) NSObject *barProperty;
@end

如果MySubclass的实现合成属性但实现了访问器方法,一切都很好(没有编译器错误):

#import "MySubclass.h"
@implementation MySubclass

- (NSObject*)barProperty {
    return [[NSObject alloc] init]; // pls ignore flagrant violation of memory rules.
}

- (void)setBarProperty:(NSObject *)obj { /* no-op */ }

- (void)doSomethingWithProperty {
    NSArray *array = [NSArray arrayWithObjects:self.barProperty, fooInstanceVar, nil];
    NSLog(@"%@", array);
}
@end

但是如果删除属性访问器方法并用属性的synthesize声明替换它们,我会收到编译器错误:'fooInstanceVar' undeclared (first use in this function)

#import "MySubclass.h"
@implementation MySubclass
@synthesize barProperty;

- (void)doSomethingWithProperty {
    NSArray *array = [NSArray arrayWithObjects:self.barProperty, fooInstanceVar, nil];
    NSLog(@"%@", array);
}
@end

如果我删除synthesize声明,或者如果我没有从MySubclass.m中引用fooInstanceVar实例变量,或者我将所有接口和实现定义放入一个文件。 GCC 4.2和GCC / LLVM构建设置中似乎也会发生此错误。

有谁能解释这里发生了什么?

3 个答案:

答案 0 :(得分:5)

在此问题中回答:objective c xcode 4.0.2: subclass can't access superclass variables "was not declared in this scope"

来自doc:Apple Objective-C编程语言: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocDefiningClasses.html#//apple_ref/doc/uid/TP30001163-CH12-TPXREF125

  

实例变量可以在声明它的类中以及在继承它的类中访问。没有显式范围指令的所有实例变量都具有@protected范围。

     

但是,可以在任何地方访问公共实例变量,就好像它是C结构中的字段一样。例如:

     

Worker * ceo = [[Worker alloc] init];

     

ceo-> boss = nil;

我使用LLVM GCC 4.2(对于iOS项目,在设备上)有编译错误:

  

错误:'fooInstanceVar'未声明(首次使用此功能)   和使用GCC 4.2的同一个:   错误:'fooInstanceVar'未声明(首次在此函数中使用)

我可以使用LLVM Compiler 2.0进行编译而不会出错。

使用自我>编译LLVM GCC 4.2和GCC 4.2。 :

  

[NSArray arrayWithObjects:self.barProperty,self-> fooInstanceVar,nil];   在doSomethingWithProperty方法中。

答案 1 :(得分:0)

编译器表现正常;使用超类中的存储在子类中进行合成是禁止的。

在某些时候,有一个关于llvm的错误。它可能位于可公开访问的错误数据库中。

在任何情况下,请提交一份错误消息,要求澄清此特定规则。


我刚试过这个,它在没有警告的情况下编译。我不做什么?

@interface MyBaseClass : NSObject {
    NSObject *fooInstanceVar;
}
@end

@interface MySubclass : MyBaseClass { }
@property (nonatomic, retain) NSObject *barProperty;
@end

@implementation MyBaseClass
@end

@implementation MySubclass
@synthesize barProperty;

- (void)doSomethingWithProperty {
    NSArray *array = [NSArray arrayWithObjects:self.barProperty, fooInstanceVar, nil];
    NSLog(@"%@", array);
}
@end

目前尚不清楚您要解决的问题。除了32位Mac OS X外,所有实例变量都是非脆弱的。

答案 2 :(得分:0)

我也无法重现你的错误。您是否设置了非默认编译器标志?你能提供一份项目副本吗?它似乎是编译器中的一个错误。

查看此article here以获得@ property / @ synthesize的最佳用途。快速摘要是从对象中删除所有ivars(除非您出于某种原因需要使用32位运行时)。然后只使用你的getter和setter,而不是直接访问合成的ivars。接下来将避免此bug的任何未来问题。