【问题标题】:Understanding private instance variable了解私有实例变量
【发布时间】:2013-07-07 21:42:37
【问题描述】:

我很难通过示例来理解私有实例变量。在阅读了私有实例变量之后,我去了 Xcode 并尝试验证它们是如何工作的。

在我正在阅读的书中,它指出如果您在超类的实现文件中声明一个实例变量,则该实例变量将是私有的,子类无法访问。

我尝试证明它没有任何运气。

/** SuperClass interface file**/
#import <Foundation/Foundation.h>

@interface ClassA : NSObject

-(void) setX;
-(void) printX;

@end

/**SuperClass implementation file **/
#import "ClassA.h"

@implementation ClassA
{
    int x;
}

-(void) setX
{
    x = 100;
}

-(void) printX
{
    NSLog(@" x is equal to %i", x);
}

@end

/** interface file of subclass **/
#import "ClassA.h"

@interface ClassB : ClassA

@end



/**Main file **/
#import "ClassA.h"
#import "ClassB.h"


int main(int argc, const char * argv[])
{

    @autoreleasepool


    { 

        ClassA * a;
        a = [[ClassA alloc] init];

        ClassB * b;
        b = [[ClassB alloc] init];


        [b  setX];
        [b printX];



    }
    return 0;

}

程序打印以下内容: x 等于 100

“x”不是私有实例变量,不能被对象“b”访问,因为“x”是在超类“a”的实现文件中声明的,而“b”是一个子类?

书上说“子类直接访问的实例变量必须在接口部分声明,而不是在实现部分......在实现部分声明或合成的实例变量是私有实例变量,不是子类可以直接访问。”

真的被这个弄糊涂了。

【问题讨论】:

  • 我认为@implementation ClassA { int x; } 部分甚至不应该编译。你的代码真的是这样吗?
  • 有趣,它确实可以编译。我以前从未见过这种结构。
  • 是的,我只是直接从 Xcode 复制了它。你觉得那部分有什么问题?
  • 这样声明 ivars 完全没问题。从 Xcode 4 开始就是这样。
  • @somid,我用 cmets 做了一个要点给你解释这个 - gist.github.com/dunenkoff/5963677

标签: objective-c


【解决方案1】:

setXprintX 方法是公开的和可见的,因此可以在ClassB 的实例上调用。由于它们是公开的,因此它们也可以被 ClassB 调用,就像这样。

@implementation ClassB

- (void)fig {
   [self setX];
}

@end

不能ClassB 直接访问值x。像这样:

@implementation ClassB

- (void)foo {
   NSLog(@"x is now %i", x);
}

@end

ClassB 没有对x直接 访问权限,但它可以通过超类方法间接 访问x。这种间接访问是一种面向对象的编程概念,称为封装。

【讨论】:

  • 所以如果你在接口文件中声明一个实例变量,它是public的。如果你在实现文件中声明一个实例变量,它是私有的。但是当涉及到子类时有什么区别。显然,在我提供的示例中,子类仍然可以访问私有实例变量。
  • 您还可以使用 @KyrDunenkoff 的答案中的 @private 将实例变量设为私有。子类不能直接访问实例变量,但可以使用超类的方法间接访问。
  • classB中的对象“b”能否间接改变对象“a”看到的“x”的使用方法?例如,如果在超类 ClassA 的实现中提供了下面的这个方法,我们在主文件中写了 [classB setX: 15]。对象“a”会读取 15 作为“x”的新值吗? @implementation ClassA -(void) setX: (int) xVal { x = xVal; }
  • 子类仍然可以直接访问 ivars - 如果它们在 @interface 部分中声明并且已被设置为 @public@protected
  • @somid 是的,这种对私有实例变量的间接修改是允许的,甚至是鼓励的。它是封装的一部分。
【解决方案2】:

Ivars 默认有@protected 属性,意味着子类可以访问它们。要将 ivar 声明为私有,请在 ivar 声明之前使用 @private 属性:

@interface ClassA : NSObject
{
@private
    int x;
}

如果您在 @implementation 部分声明您的 ivars,子类可见它们的唯一方法是在您的子类中导入 .m 文件,但您不能使用它们,因为它们是私有的。

或者根本不使用 ivars,因为 Objective-C 属性现在会自动创建 ivars。如果您需要私有属性,可以通过 .m 文件中的匿名类别声明它,如下所示:

@interface MyClass ()

@property (nonatomic) NSInteger x;

@end

更新: 我想我明白什么让你感到困惑。公共和受保护的 ivars 由子类继承,可以作为子类的实例变量直接访问,无需使用子类的访问器方法。

【讨论】:

  • 感谢凯尔的回复。我对书中的文字以及我的示例与它的关系感到困惑。书中说“子类直接访问的实例变量必须在接口部分而不是在实现部分中声明......在实现部分中声明或合成的实例变量是私有实例变量,不能直接访问子类。”我的子类不是只访问一个私有实例变量吗?
  • @somid 您的子类没有直接访问私有实例变量。访问是间接的。
  • @thomasW 我还是新手。尝试直接访问实例变量的子类的示例是什么样的?
  • @somid 检查我的答案是否允许。
  • 这是不正确的。在类的接口中声明的实例变量默认是受保护的,但在类扩展接口或实现中声明的实例变量默认是私有的。
猜你喜欢
  • 2011-01-23
  • 2012-04-10
  • 1970-01-01
  • 2012-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-08
相关资源
最近更新 更多