【问题标题】:Using class extensions in xcode 4.4在 xcode 4.4 中使用类扩展
【发布时间】:2012-08-26 12:28:38
【问题描述】:

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

使用未声明的标识符 _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 的结果??

这一切都很奇怪。

【问题讨论】:

  • 据我了解,这是 Apple LLVM 4.0 编译器的一个功能,而不是 Xcode IDE。您在项目中使用什么编译器?
  • 可以,但是xcode4.4默认使用这个编译器。
  • 那么构建但生成奇怪日志语句的配置是您在超类的公共接口和子类的扩展中声明属性的配置?
  • 不,唯一构建的“配置”是在超类和子类的类扩展中声明两个属性的配置。

标签: iphone ios xcode4.4


【解决方案1】:

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

无论如何,存在实例变量可见性的问题。如果您在接口范围内声明您的 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?它调用该对象的属性设置器method。这个实例是什么类型的?这是ViewControllerSubclass。由于您为基类及其子类指定了相同的名称(并将属性声明为类扩展的一部分),因此它们都有自己的私有范围实例变量。

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

当你NSLog这个值时,你访问的是基类的私有实例变量,它没有被改变。

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

【讨论】:

    【解决方案2】:

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

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

    希望这会有所帮助!

    【讨论】:

    • 我的问题实际上是由许多不同的问题组成的,这回答了所有问题。
    • 很高兴能为您提供帮助!谢谢!
    【解决方案3】:

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

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

    【讨论】:

    • 就是这样。在上面的最后一个示例中,aVar 在 .m 中为两个类声明,那么为什么子类打印值 5?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多