【问题标题】:Why are instance variables defined in the header file in Objective-C为什么Objective-C的头文件中定义了实例变量
【发布时间】:2010-11-01 07:12:42
【问题描述】:

我可以理解在头文件的@interface中定义函数,但是为什么是实例变量呢?实例变量不应该是私有的,只能通过消息访问吗?

【问题讨论】:

    标签: objective-c


    【解决方案1】:

    原因是它可以计算子类的变量偏移量。

    @interface Bird : NSObject {
        int wingspan;
    }
    @end
    @interface Penguin : Bird {
        NSPoint nestLocation;
        Penguin *mate;
    }
    @end
    

    在不知道“Bird”类的结构的情况下,“Penguin”类无法计算其字段相对于结构开头的偏移量。企鹅结构看起来像这样:

    struct Penguin {
        int refcount; // from NSObject
        int wingspan; // from Bird
        NSPoint nestLocation; // from Penguin
        Penguin *mate; // from Penguin
    }
    

    这有一个副作用:如果您更改库中类的大小,则会破坏链接到该库的应用程序中的所有子类。新属性解决了这个问题。

    【讨论】:

      【解决方案2】:

      虽然它们是在头文件中声明的,但默认情况下,Objective-C 中的所有实例变量都具有@protected 访问权限。这意味着该变量可以在声明它的类以及从该类继承的任何类中访问。

      这是 Apple 关于定义 Objective-C 类的文档:Defining Classes

      请注意标题为“实例变量的范围”的部分。

      【讨论】:

        【解决方案3】:

        我认为这是一个技术问题。如果我理解正确的话,Objective-C 类只是一个花哨的 C 结构。对于要使用的结构,它的大小必须是已知的。 (就像 sizeof() 否则如何工作)

        【讨论】:

        • 它们只是 C 结构,甚至不是花哨的 :-) 所以像 anObject->anIVar 这样的东西就可以了。不幸的是,旧版运行时甚至在 @private 变量上也允许这种访问,并且只是呕吐编译器警告:-/
        【解决方案4】:

        如果有人偶然发现这个问题——从带有 LLVM 编译器的 XCode 4.2 开始,您可以使用以下大括号表示法在 @implementation 中声明实例变量:

        
        @interface SomeClass : NSObject
        @end
        
        @implementation SomeClass {
          NSString *myInstanceVariable_;
        }
        
        - (void)moreMethods {}
        @end
        

        实例变量通常不应该是你的类声明的公共接口的一部分——它们是 实现细节。

        但是,确保您在大括号内定义实例变量,否则您将定义一个与对象实例无关的全局变量:

        
        @implementation SomeClass
          NSString *whoopsGlobalVariable_;
        
        - (void)moreMethods {}
        
        @end
        

        【讨论】:

        • 我一直在研究这个。从我目前的测试来看,GCC 似乎不会这样做。它给出了一个错误:“不一致的实例变量规范”。它只能用 LLVM 编译。另请参阅Declaring Variables in @implementation
        • 对,它需要现代 LLVM 编译器。
        【解决方案5】:

        摘自 Apple 文档中关于定义 Objective-C 类的部分,The Role of the Interface

        虽然实例变量最自然地被视为类的实现而不是其接口,但它们仍然必须在接口文件中声明。这是因为编译器必须知道对象的结构在哪里使用,而不仅仅是在哪里定义。

        【讨论】:

          【解决方案6】:

          注意,在 Objective C 2.0 新的“Modern Runtime”下(在 iPhone 应用程序和 64 位 Mac OS X 10.5 应用程序中可用)您不需要指定 ivars,您可以指定属性然后使用 @synthesize 来生成 ivars。

          这是因为在 Modern Runtime 中,ivars 有一个全局间接符号来保存 ivar 的偏移量。这也解决了脆弱的基类问题,允许重新排序和添加 ivars 而无需重新编译子类(删除或重命名 ivars 仍可能导致链接错误)。

          但是您仍然必须在主界面中列出属性,因此似乎没有任何方法可以完全隐藏私有 ivars,这是不幸的。例如,您不能在类别中使用属性和 @synthesize。

          【讨论】:

          • 但是,您可以在延续中声明属性并在主@implementation 中@synthesize。
          猜你喜欢
          • 2011-01-07
          • 2012-07-08
          • 1970-01-01
          • 1970-01-01
          • 2013-03-01
          • 2013-02-02
          • 2023-04-04
          • 1970-01-01
          • 2011-10-30
          相关资源
          最近更新 更多