【问题标题】:How to declare instance variables and methods not visible or usable outside of the class instance?如何声明在类实例之外不可见或不可用的实例变量和方法?
【发布时间】:2011-08-15 03:26:07
【问题描述】:

我浏览了很多关于这个主题的帖子。也许我没有遇到“那个”,有人会指出我那个方向。这个问题很简单,可能有一个简单的答案。

如果您有两个 ivars,例如“public_ivar”和“private_ivar”,您应该在哪里/如何声明它们,以便公开的内容是公开的,而私有的内容不会以任何方式暴露给查看头文件的任何人?

“public_method”和“private_method”的情况相同。

我喜欢干净的头文件(其他语言),它只公开我希望其他人看到的方法和 ivars。您应该能够发布您的头文件,并且不会遇到有人访问他们不应该访问的内容的危险。您如何在 Objective-C 中做到这一点。

例如,假设我决定需要使用 ivar 来跟踪需要访问此信息的各种类方法之间的某些数据、计数器或类似的东西。如果该 ivar 以常规方式在 @interface 下的标头中声明,则它的存在是公开的,并且任何创建该类实例的人都可以使用它。理想的情况是这个 ivar 在类实现之外根本不可见。

【问题讨论】:

    标签: iphone objective-c xcode ios


    【解决方案1】:

    您可以在类扩展中声明实例变量或声明的属性。由于类扩展是在实现文件中声明的(即,不是头文件),因此检查头文件的人将看不到它们。比如在头文件中:

    @interface SomeClass : NSObject
    @end
    

    在实现文件中:

    @interface SomeClass ()
    @property (nonatomic, assign) int privateInt;
    @end
    
    @implementation SomeClass
    
    @synthesize privateInt;
    …
    @end
    

    @interface SomeClass () {
        int privateInt;
    }
    @end
    
    @implementation SomeClass
    …
    @end
    

    请注意,在运行时没有什么可以阻止对私有/类扩展实例变量(或类扩展中声明的属性的访问器方法)的访问。我写了一篇相当详细的帖子作为对 Stack Overflow 上另一个问题的回答:Does a private @property create an @private instance variable?

    编辑:类扩展中的实例变量已在 WWDC 2010 会议 144 中提出。

    编辑:“使用 Clang/LLVM 2.0 编译器,您还可以在类扩展中声明属性和实例变量。”

    http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocCategories.html#//apple_ref/doc/uid/TP30001163-CH20-SW1

    【讨论】:

    • 好的,我看看这个。我正在做一些遗传算法和神经网络的工作。类最终会产生大量内部需要的变量。其中很少有真正需要头文件级别的可见性。您是 iOS4+ 实现文件的第一个示例(因为您没有在 @property 语句之前声明 int privateInt; )?
    • @Martin 我不确定允许自动创建支持实例变量的 Objective-C 2.0 何时可用于 iOS。
    • @Martin This page on Apple’s Web site 没有列出特定的 iOS 版本。
    • 您确定可以直接在扩展中添加 ivars 吗?参考资料没有说明任何内容。可以发个参考,还是个人经验?
    • @ugho 个人经历;我认为这是自 Clang 2.0 以来可用的。如果您认为由于没有记录,因此不应该依赖它,我同意您的看法。但我有一种强烈的感觉,这将成为 Apple 的 Objective-C 参考,因为这样做并没有明显的危害,而且现代运行时 ABI 支持非易碎实例变量。
    【解决方案2】:

    使用class extensions 在您的实现文件中添加一个类。类扩展基本上是一个未命名的类别,但有一些好处:其中声明的属性可以合成,并且其中声明的任何内容都必须在主实现中,因此编译器可以检查以确保您没有错过任何实现。您必须在实现之前放置类扩展。您不能直接在类扩展中添加实例变量,但可以添加属性。当您为没有相应实例变量的属性合成访问器时,新的运行时(我相信 os x 10.5 及更高版本以及所有 iOS 版本)将自动创建实例变量。但是,这意味着您不能创建自己的访问器,除非您将实例变量放在标题中。私有方法可以不受限制地添加到类扩展中,但正如 Anomie 所指出的,如果您知道它们的名称,从技术上讲是可以使用它们的,并且使用类转储,没有什么是安全的。

    类扩展的示例用法:

    @interface MyClass ()
    @property (retain) id privateIvar;
    @property (readwrite) id readonlyProperty; // bonus! class extensions can be used to make a property that is publicly readonly and privately readwrite
    - (void)privateMethod;
    @end
    @implementation MyClass
    @synthesize privateIvar; // the runtime will create the actual ivar, and we just access it through the property
    - (void)privateMethod {
        ...
    }
    ...
    

    另一种创建“实例变量”而不将它们放在标题中或使用属性的方法是使用associative references,它在运行时向对象添加数据。它们在技术上与实例变量不同,它们的语法更复杂。由于它们还需要新的运行时,因此您真正想要使用它们的原因只有两个:您想在一个类别中添加一个实例变量(在这个问题的范围之外),或者您需要它真的是 真的私人。关联引用不会在编译的代码中创建任何方法或添加到类的定义中,因此如果您不为它们创建包装器,则在添加数据后不询问对象就不可能找到它们。请参阅我链接的页面底部以获取完整的使用示例。

    【讨论】:

    • 所以...如果您需要为“隐藏”的 ivar 定制 setter 或 getter,这样您就不能在不暴露标头中的 ivar 的情况下编写一个?
    • @Martin:正如 Bavarious 所说,并且正如在 cmets 中所讨论的那样,类扩展现在支持 ivars,因此您可以在那里声明它而不是标题。您还可以使用关联引用并在它们周围编写包装器作为访问器。
    【解决方案3】:

    您可以使用@private 指定 ivars 是私有的。但是,没有办法将方法设为私有。即使该方法没有在头文件中列出,如果有人知道名称和参数,他们也可以调用它。

    【讨论】:

    • 据我了解,如果您有 @property 声明(setter 和 getter),@private 并没有真正做任何事情。我什至不希望 ivar 或私有成员函数出现在标题中。假设没有人知道他们,他们应该保持隐形(只是试图让诚实的人保持诚实,如果你想挖掘你可以发现任何东西)。我只是认为在标头中包含与应用程序的其他部分完全无关的所有这些内容是“肮脏的”。
    • @Martin:属性不是 ivar。至于在标头中隐藏 ivars,我可以想到两个选项:您可以让唯一的 ivar 是指向在其他地方声明的结构的指针,或者您可以让所有实例实际上都是私有子类(沿着 Apple 所做的类集群)。
    • 因语言懒惰而感到内疚。我知道 @property 只是创建了底层的 setter 和 getter 代码。它还有其他作用吗?
    • @Martin:@property 甚至不创建底层的 setter 和 getter。它告诉编译器foo.property 语法应该使用getter 和setter 方法,并允许@synthesize 工作。还有一点簿记,所以class_getProperty 和其他低级方法可以完成他们的工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2017-02-15
    • 2021-08-24
    • 2016-03-19
    • 2015-06-28
    • 1970-01-01
    相关资源
    最近更新 更多