13

更新 - 许多人坚持我需要为该属性声明一个 iVar。有些人说不是这样,因为我使用的是现代运行时(64 位)。我可以确认几个月来我已经成功地使用了没有 iVars 的 @property。因此,我认为“正确”的答案是对为什么在 64 位上我突然必须在(且仅当)我要从子类访问它时显式声明 iVar 的解释。到目前为止,我看到的唯一一个可能是 GCC 错误(感谢 Yuji)。毕竟不是那么简单......为了澄清可能的错误是:当从基类继承时,如果孩子也碰巧在访问 iVar 之前使用 @synthesize 实现了一个 UNRELATED 访问器,则孩子无法访问父母的 iVar。

几个小时以来,我一直在为此挠头——我没有过多地使用继承。

在这里,我设置了一个简单的 Test B 类,它继承自 Test A,其中声明了 ivar。但是我得到了变量未声明的编译错误。这只发生在我添加属性和综合声明时 - 没有它们也能正常工作。

测试头:

#import <Cocoa/Cocoa.h>
@interface TestA : NSObject {
    NSString *testString;
}
@end

TestA 实现为空:

#import "TestA.h"
@implementation TestA  
@end

测试B标头:

#import <Cocoa/Cocoa.h>
#import "TestA.h"
@interface TestB : TestA {
}
@property (nonatomic, retain) NSString *testProp;
@end

TestB 实现(错误 - 'testString' 未声明)

#import "TestB.h"
@implementation TestB
@synthesize testProp;
- (void)testing{
    NSLog(@"test ivar is %@", testString);
}
@end
4

4 回答 4

10

我认为这是 GCC 4.2.1 的错误。我用foo.m内容制作了文件

#import <Foundation/Foundation.h>
@interface TestA : NSObject {
    NSString *testString;
}
@end

@implementation TestA  
@end

@interface TestB : TestA {
}
@property (retain) NSString *testProp;
@end

@implementation TestB
@synthesize testProp;
- (void)testing{
NSLog(@"test ivar is %@", testString);
}
@end

请注意,在 64 位模式下可以省略实例变量。我在 OS X 10.6.3 上的 GCC 4.2.1 给了我一个错误:

$ gcc -arch x86_64 -c foo.m
aho.m: In function ‘-[TestB testing]’:
aho.m:19: error: ‘testString’ undeclared (first use in this function)
aho.m:19: error: (Each undeclared identifier is reported only once
aho.m:19: error: for each function it appears in.)

这通过更改编译没有问题

NSLog(@"test ivar is %@", testString);

NSLog(@"test ivar is %@", self->testString);

Clang 编译它没有任何问题。

(在 32 位模式下,我得到了

$ gcc -arch i386 -c foo.m
aho.m:17: error: synthesized property ‘testProp’ must either be named 
the same as a compatible ivar or must explicitly name an ivar
aho.m: In function ‘-[TestB testing]’:
aho.m:19: error: ‘testString’ undeclared (first use in this function)
aho.m:19: error: (Each undeclared identifier is reported only once
aho.m:19: error: for each function it appears in.)

正如 Manjunath 所写,这是一个完全可以预料的行为。)

但是我认为访问超类的实例变量通常是一个相当糟糕的主意:当您实现超类的方法时,您不能对实例变量进行任何假设,因为它可能会被子类以最糟糕的方式进行调整。您至少需要写下允许或不允许对实例变量进行何种操作...请记住,您可能需要多年维护您的代码!我更愿意在方法和属性级别保持代码的各个部分之间的编程合同。

最后你应该改变

@property NSString *testProp;

@property (copy) NSString *testProp;

或至少

@property (retain) NSString *testProp;

如果您没有在 OS X 上使用 GC。否则 EXP_BAD_ACCESS 将等待您!

于 2010-05-06T04:54:58.757 回答
1

我认为你只是有一个错字 - 它应该是“testString”而不是“test”

于 2010-05-06T04:17:02.757 回答
1

我正在查看方法之前的error: 'testString' undeclared (first use in this function)时间。如果我将方法实现移到下面,错误就会消失。这可能是因为 TestB 类没有与声明的属性一起使用的字符串实例变量。(在旧版(32 位)运行时,您必须声明实例变量以用于属性 - 在现代运行时(64 位 Mac、iPhone)中,可以推断它们,因此声明它们是可选的。)您的意思是否可能改为命名属性?@synthesizetesting@synthesizetestProptestString


编辑:在 GCC 4.2 中,如果您将 TestB.h 更改为以下内容,它将起作用:

#import "TestA.h"

@interface TestB : TestA {
    NSString *testProp; // <-- Adding this fixes the errors
}
@property NSString *testProp;

@end

但是,使用 Clang-LLVM 编译器,代码无需修改即可工作。也许这是一个需要归档的错误。

于 2010-05-06T06:00:58.447 回答
1

我刚刚遇到了同样的问题,但来源很复杂,以至于我不太明白是什么让我使用 GCC 时无法访问父级的 iVar。我只确定在几个月前和我的代码更改之前它可以工作,并且它正在使用我已经使用了一段时间的 clang。突然间,我不得不使用 GCC 进行构建,但它不再需要了。

至少这篇文章给了我一个绕过(声明 iVars)。令我惊讶的是最新版本的 XCode 不包含固定编译器

于 2011-03-30T20:44:50.353 回答