在xcode 4.4中使用类扩展

Rem*_*ver 1 iphone ios xcode4.4

由于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
Run Code Online (Sandbox Code Playgroud)

如果我将所有内容移动到一个文件而不是相应的接口和实现的4个单独文件,则编译器会抱怨它_aVar是私有的.但_aVar应该已经自动合成了我的ViewControllerSubclass.

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

@interface ViewController ()
@property (assign, nonatomic) int aVar;
@end
Run Code Online (Sandbox Code Playgroud)

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

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

如果我然后运行代码:

[[[ViewControllerSubclass alloc] init] view];
Run Code Online (Sandbox Code Playgroud)

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

超值:0

子类值:5

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

这一切都很奇怪.

Jod*_*ins 6

你混淆了几个不同的问题,当你谈到在文件之间跳转而你没有指定错误发生的位置时,我有点困惑.

无论如何,存在实例变量可见性的问题.如果在界面范围内声明iVar,则默认情况下它们受到保护.

@interface Foo : NSObject {
    int protectedInt;
@private
    int privateInt;
@public
    int publicInt;
}
@end
Run Code Online (Sandbox Code Playgroud)

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

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

现在,当你打电话给这个......

[[[ViewControllerSubclass alloc] init] view];
Run Code Online (Sandbox Code Playgroud)

您将分配子类,初始化并导致加载视图.这段代码将执行...

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

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

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

当然,它称之为超级,但这部分并不重要.下一行为self.iVar分配5.但是,哪个iVar?它在此对象上调用属性setter 方法.这个实例是什么类型的?这是一个ViewControllerSubclass.由于您已经为您的基类和它的子类赋予了相同的名称(并将该属性声明为类扩展的一部分),因此它们每个都有自己的私有范围实例变量.

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

当您使用NSLog该值时,您将访问基类的私有实例变量,该变量尚未更改.

现在,在基类viewDidLoad完成后,我们将获得为子类运行的代码.它记录其私有实例变量的值,该变量由于调用属性setter的基类而更改.因此,它现在将打印它的值,即5.


Car*_*zey 5

当您将超类声明设为public时,编译器将不会尝试重新合成该属性; 它假设已经在超类中得到了解决.因此,_aVar不在子类中的任何位置.无论如何它都是私有的,所以即使你把它们全部放在同一个文件中,也就是你看到这些错误的原因.

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

希望这可以帮助!