【问题标题】:How to make a real private instance variable?如何制作真正的私有实例变量?
【发布时间】:2014-08-19 08:45:12
【问题描述】:

我想创建一个无法从外部访问的实例变量。在objective-c中可能有这样的事情吗?我记得 Apple 有私有变量之类的东西,但如果人们知道它们,他们就可以使用它们。 Apple 称其为“私有 API”,但显然其他人如果发现其中的内容就可以访问这些内容。

直到现在我相信这样的事情会创建一个私有实例变量:

@interface MyClass : NSObject {
    CGFloat weight;
}

没有@property,没有@synthesize,只有上面的声明。

我也知道 Apple 添加了一个 _inFrontOfTheirPrivateInstanceVariables,但他们在某处表示他们不希望看到其他人这样做,因为他们可能会在这样做时覆盖意外隐藏的实例变量。

这里有什么诀窍?

【问题讨论】:

    标签: objective-c private visibility instance-variables


    【解决方案1】:

    您可以在{} 中使用@private 关键字将所有后续变量声明设为私有。默认可见性为@protected(类似于Java 中的protected),通常效果很好。您必须专门将变量声明为 @public 才能在类外部直接访问。

    This Apple documentation 有关于变量范围和可见性的更多详细信息。

    “私有 API”和私有变量之间也有区别。在 Objective-C 中,你不能将方法设为私有——任何人都可以调用任何方法。有几种方法可以创建“秘密”方法,但这有点超出了这个问题的范围。以下是一些相关的 SO 问题:

    至于变量前面的前导 _,请注意 Apple 还为“私有”方法保留此前缀。保证您避免问题的最佳方法是对您自己的变量和方法使用正常的命名约定。但是,除非您从 Cocoa(NSObject 除外)子类化某些东西,否则您可以相当自信地不会遇到问题。

    【讨论】:

    • “有点像 Java” 不,在 Java 中,默认访问与“受保护”非常不同
    • 他的意思是ObjC的“protected”就像java的“protected”一样。
    • 对不起,我的回答稍微澄清了一点。您是正确的,Java 的“默认”访问与“受保护”非常不同。我并不是有意将两者混为一谈。
    • @QuinnTaylor 是的,默认的包保护可见性不应该应用于 Objective-C,因为没有包。
    【解决方案2】:

    默认情况下,Objective-C 中的所有 iVar 都受到保护。如果您不编写访问器方法,那么其他类将无法看到变量。

    两个例外是类别和子类。

    【讨论】:

    • 他们将能够通过 valueForKey: 和 setValue:forKey: 与他们交互。
    • 我不知道 KVC 是这样工作的,谢谢发帖。事实证明,您可以通过覆盖 + (BOOL)accessInstanceVariablesDirectly 来关闭此行为以返回 NO。
    【解决方案3】:

    用于命名实例变量的 Apple 文档没有像私有方法文档那样明确警告不要在实例变量的名称中使用下划线。

    Naming Instance Variables and Data Types

    我还记得 Wil Shipley 和其他一些 OS X 开发人员之间关于下划线的对话。由于 Obj-C 编译器的工作方式,如果 Apple 将新的实例变量添加到其框架中的类中,将导致所有使用这些框架的应用程序都需要重新编译。就预先存在的实例变量而言,当您踩到某个实例变量时应该会收到警告。

    【讨论】:

    • Objective-C 现在允许在不影响子类的情况下将 IVar 添加到类中(使用 Leopard 和 iPhone OS 2.0 附带的 2.0 语言更新)
    【解决方案4】:

    借助 XCode 4 及更高版本中可用的新 LLVM 编译器,您可以在实现 (.m) 文件中的默认类别中声明 @private 变量:

    @interface ClassName()
    {
    @private
    // private variables here
    }
    @end
    
    @implementation ClassName
    // you can use private variables here
    @end
    

    我觉得这很方便,因为我讨厌私有变量带入我的头文件中。

    【讨论】:

    • 这不适用于针对 iOS 4.2 的 LLVM GCC 4.2 和 Xcode 4.0.2。您的意思是“LLVM 2.0 编译器”吗?
    • LLVM GCC 不是 LLVM 编译器,它仍然是 GCC,但带有 L​​LVM 解析器。我的意思是 LLVM 2.0 或 LLVM 3.0(iOS 5 Beta,XCode 4.2)。
    • 对不起。意识到这一点可能很重要,但只能使用这个编译器,尤其是对于与其他人一起进行项目的人。
    • 您可以使用@protocol 向客户端“隐藏”您的实例变量。
    • @RaffiKhatchadourian 这不是@protocol 的目的。并且无助于从课堂上隐藏 ivar。它只是隐藏了整个班级。如果您想提供答案,您应该自己填写答案条目。
    【解决方案5】:

    您可以通过将私有方法仅放在@implementation 中而不是@interface 中来定义它们。

    同样,您可以在 @implementation 开始时在匿名块内定义私有实例变量 - 就像在 @interface 内为公共 ivars 所做的那样。

    请看下面的例子。

    @interface EXClass : NSObject
    {
    uint8_t publicInteger;
    float publicFloat;
    }
    
    -(void)publicMethod;
    @end
    
    @implementation EXClass
    {
    uint8_t privateInteger;
    float privatefloat;
    }
    
    -(BOOL)privateMethod {
    return FALSE;
    }
    

    请记住,Objective-C 方法在运行时作为消息发送(而不是 C++ 的编译时绑定),因此 respondsToSelector: 仍将返回 true 并且 performSelector: 仍将调用该方法。 ivars 将是完全私有的。

    但是,如果您正在创建一个库,理论上没有人会知道您没有在头文件中声明的任何方法。

    【讨论】:

    • “理论上没有人会知道你没有在头文件中声明的任何方法。” - 不是这样。尽管 Apple 缺乏关于其数百个私有方法的文档,但我已经能够编写一个非常具有侵入性的 Xcode 插件,它通过使用 objc_runtime.h 中的函数来准确地告诉您一个类具有哪些方法。
    【解决方案6】:

    我在 Apple 的示例应用 (PaintGL) 中看到了以下用法

    在 .m 文件中

    @interface MyClass (private)
      - (void) privateMethod();
      @property(...) myProperty;
    @end
    

    免责声明:示例应用只有方法声明,我看到了私有属性声明in this SO thread

    【讨论】:

      【解决方案7】:

      您可以创建一个真正的私有实例变量。 Objective-C 是一种动态语言,因此可以访问任何变量(甚至是@private)。

      我最好的方法:

      在 .m 文件的实现块中使用它。然后就是不可见并阻塞KVC,这样KVC就不起作用了

      @implementation ClassName {
          // default to @protected
          // but the subclasses can't see ivars created in the implementation block
          float number;
      }
      
      + (BOOL)accessInstanceVariablesDirectly {
          return NO; // no KVC
      }
      
      @end
      

      【讨论】:

        猜你喜欢
        • 2019-05-15
        • 2011-01-23
        • 2012-04-10
        • 2013-07-07
        • 1970-01-01
        • 2012-12-03
        • 1970-01-01
        • 2012-02-08
        相关资源
        最近更新 更多