1

从 xcode 4.4 开始,您不再需要@synthesize属性(请参阅此处),编译器会为您完成。那么,为什么编译器会抱怨

使用未声明的标识符 _aVar

在我的viewDidLoad方法中ViewControllerSubclass

@interface ViewController : UIViewController
@property (assign, nonatomic) int aVar;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.aVar = 5;
    NSLog(@"Super value: %d", _aVar);
}
@end

@interface ViewControllerSubclass : ViewController
@end

@interface ViewControllerSubclass ()
@property (assign, nonatomic) int aVar;
@end

@implementation ViewControllerSubclass
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"Subclass value: %d", _aVar);
}
@end

如果我将所有内容都移动到一个文件而不是 4 个单独的文件用于各自的接口和实现,编译器反而会抱怨这_aVar是私有的。但是 _aVar 应该已经在我的ViewControllerSubclass.

如果我将初始属性声明移至类扩展名,则仍将所有内容保存在 1 个文件中:

@interface ViewController ()
@property (assign, nonatomic) int aVar;
@end

构建仍然失败,说这_aVar是私有的。

如果我回到 4 个文件设置,为相应的接口和实现 xcode 构建甚至没有警告。

如果我然后运行代码:

[[[ViewControllerSubclass alloc] init] view];

上述示例中的日志语句打印出以下内容:

超值:0

子类值:5

NSLog(@"Super value: %d", _aVar);产生结果是有道理的,0因为这个变量应该是超类私有的。但是,为什么会NSLog(@"Subclass value: %d", _aVar);产生结果5??

这一切都很奇怪。

4

3 回答 3

6

您混淆了几个不同的问题,当您谈论在文件之间跳转并且您没有指定错误发生的位置时,我有些困惑。

无论如何,存在实例变量可见性的问题。如果您在接口范围内声明您的 iVar,则默认情况下它们是受保护的。

@interface Foo : NSObject {
    int protectedInt;
@private
    int privateInt;
@public
    int publicInt;
}
@end

当您合成 iVar 时,实例变量本身是私有的,除非您明确指定它们。

方法将始终触发最派生的实现。

现在,当你调用这个...

[[[ViewControllerSubclass alloc] init] view];

您将分配一个子类、初始化并加载视图。此代码将执行...

@implementation ViewControllerSubclass
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"Subclass value: %d", _aVar);
}
@end

它做的第一件事是调用基类实现......

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.aVar = 5;
    NSLog(@"Super value: %d", _aVar);
}
@end

当然,它调用 super,但这部分在这里并不重要。下一行将 5 分配给 self.iVar。但是,哪个 iVar?它调用此对象的属性设置器方法。这个实例是什么类型的?这是一个ViewControllerSubclass。由于您已为基类及其子类指定了相同的名称(并将该属性声明为类扩展的一部分),因此它们每个都有自己的私有范围实例变量。

但是,在最派生的实现上调用了一个方法。因此,self.iVar 将设置子类的实例变量。基类的实例变量保持不变。

当您NSLog设置该值时,您正在访问未更改的基类的私有实例变量。

现在,在基类viewDidLoad完成后,我们开始为子类运行代码。它记录其私有实例变量的值,该变量因基类调用属性设置器而被更改。所以,它现在将打印它的值,即 5。

于 2012-08-26T14:26:36.907 回答
5

当您公开超类声明时,编译器不会尝试重新合成该属性;它假设已经在超类中得到了照顾。因此,_aVar不在子类的任何范围内。无论如何它都是私有的,所以即使你把它们都放在同一个文件中,这就是你看到这些错误的原因。

但是,当您在类扩展中进行超类属性声明时,编译器将自动合成超类和子类的属性。这最终导致两个类都具有私有实例变量 _aVar(具有两个不同的地址)。但是,当超类viewDidLoad方法设置属性时,该方法调用子类的访问器,它设置子类的私有 _aVar 变量的值,而不是超类的值。这就解释了为什么你看到超类值没有改变。

希望这可以帮助!

于 2012-08-26T14:25:12.900 回答
0

我刚刚测试了您的设置,并且可以复制您的错误。我得出以下结论:

您需要@property.h文件中声明您的。如果您想要一个私有变量,请.m在类别中声明它@interface(带括号的那个)。

于 2012-08-26T12:43:33.273 回答