【问题标题】:Objective-C: Private variables VS private propertiesObjective-C:私有变量 VS 私有属性
【发布时间】:2013-11-27 18:19:16
【问题描述】:

对我来说,作为 Objective-C 开发人员的过去很简单。一个类需要公开的每个字段都是一个属性,每个私有字段都是一个没有 getter 或 setter 的实例变量。但更多时候我看到人们在实现文件中使用私有接口来声明私有属性。有人告诉我这就是现在做事的方式。

虽然这很好用,但我很难看出它的优点。只要我不需要 getter 或 setter 中的一些逻辑,我就会继续对所有不公开的东西使用实例变量。我不得不承认使用属性然后使用关键字 self 使代码更具可读性。您可以查看一个属性是否属于该类,或者它是否只是方法中的一个局部变量,但这不是唯一的原因。

为什么或为什么不使用私有属性?

【问题讨论】:

标签: objective-c


【解决方案1】:

在 ivars 上使用(私有)属性有几个原因。

  • 正如您所说,使用属性可以让您轻松使用访问器方法,这些方法除了访问变量之外还可以进行一些额外的编码。
  • KVO 不适用于 ivars。
  • 只为实现创建一个公共只读属性 readwrite,以便在 getter 之外合成一个 setter(正如 Brad 所指出的那样)
  • 个人偏好、习惯或懒惰(财产是公开的,但已更改为私人财产)。

【讨论】:

  • +1 我要添加到这个列表中,使用属性也避免了担心内存语义的需要,因为它是由 setter 决定的(例如,如果你有一个 copy 属性,每次你调用 setter,它会自动为您复制该对象,而不需要记住每次设置实例变量时显式调用 copy)。此外,这是您的 KVC 评论的必然结果,但我认为它简化了与 NSCoding(例如,归档时很有用)和 NSCopying 的一致性(但显然,您不需要 需要 属性来实现这些协议)。
  • 我同意你关于 KVO 的看法,但是你为什么说 KVC 不适用于 ivars?
【解决方案2】:

objective-c 中的属性不是简单的实例变量。它们是由实例变量支持的 getter 和 setter 方法的组合。

通常当人们在实现文件(私有类扩展)中添加属性时,将公开的readonly 属性设为readwrite 属性,以便他们可以设置它并保留所有属性语义,如键值编码。属性还为您处理复制保留。

它只是将公共接口与实现分开的一种方法。

【讨论】:

  • 我认为 OP 明白这一点。
  • 你可以有一个只读的公共属性,并在实现中直接设置ivar。你不需要私有的读/写属性。
  • @rmaddy 如果您希望保留我明确提到的 KVO 等内容,请执行此操作。
【解决方案3】:

IMO OP 询问为什么将一些 ivars 放在接口中,而将一些 ivars 放在实现中。一个附带问题是为什么要使用属性。

  1. 关键是封装。仅将那些打算在类外设计使用的东西放在公共接口 (.h) 文件中。将那些设计为仅用于实现 (.m) 文件中的实现。也就是说,任何未公开公开的内容都可以在以后更改,而不会影响课程的用户。

  2. 属性为关联的 ivars 提供 setter/getter 方法。在当前的objective-c 编译器中,如果没有@synthesize 语句,编译器将自动生成与带有下划线(_) 前缀的属性同名的ivar。 getter/setter 也支持 KVO,这取决于你的使用情况。请注意,是否可以在接口文件中声明属性为只读,在实现文件类扩展中为读写。

因此,当前的最佳实践是为所有 ivars 使用属性,如果它们被设计为公共,则将属性语句放在接口文件中,如果它们被设计为私有,则将它们放在类扩展的实现文件中。这提供了一个干净的接口文件,它只公开公共方法和属性。

【讨论】:

    【解决方案4】:

    使用属性可能更“方便”(至少因为您不必担心在 KVO 属性的情况下忘记调用 willChange / didChange),毫无疑问,使用 ivars 代替属性更快。所以我建议使用属性,除非你真的关心每秒帧数。如果是这种情况,请查看this awesome post

    【讨论】:

      【解决方案5】:

      这只是一个封装问题,您可以拥有许多不同级别的隐私......

      最初在 Objective-C 中,运行时不支持在运行时向对象添加实际字段,因此必须在类中列出每个 iVar:

      @interface MyClass : NSObject
      {
      //every ivar that MyClass adds to NSObject must be here
      }
      

      这在一段时间内是一个简单且足够好的系统......

      即使是私有 iVar 也必须声明,尽管编译器不允许您在错误的范围内访问它们。

      @interface MyClass : NSObject
      {
       @private
       id someObj;
      }
      

      此可见性说明符限制访问,例如:

      //someotherclass.m

       + (void)doSomething
       {
           MyCLass * mc = [MyClass new];
           mc->someObj = [SomeOtherClass new]; // error cant access private variable...
       }
      

      但你可能会通过指针算术得到它,所以为了混淆你会在类中看到这样的东西:

      @interface MyClass : NSObject
      {
       @private
       void * __reserved1;
       void * __private1;
      }
      

      这是一个很好的混淆......

      但是等等.... 必须有更好的方法!

      输入非易碎ABI

      现在类只需要导出它们的超类和公共接口。

      @interface MyClass : NSObject
      @property (readonly,retain) id someIVar;
      

      其余的类内容可以包含在类扩展中:

      @interface MyClass ()
      {
       id someObj;
      }
      @property (readwrite,retain) id someIVar;
      

      请注意,这仅适用于 iPhone 和 64 位 OS X 32 位 OS X 仍然是旧的 ABI,需要在那里工作的库将需要使用旧的方式。

      【讨论】:

      • 这与运行时创建的ivars无关,它们是由编译器在编译时创建的。
      • 它们可能在编译时创建,但它们是以动态方式创建的,因此它们的偏移量无法确定。
      【解决方案6】:

      真的很简单。

      如果您想使用 private @property,请将此属性添加到您的 .m 中:

      @interface MyViewController ()
      
      @property (nonatomic, strong) NSString *myPrivateStringProperty;
      
      @end
      
      @implementation MyViewController
      
      @end
      

      这里,该属性只能在您的 .m. 中访问。

      如果您想使用 public @property,请将此属性添加到您的 .h 中:

      @interface MyViewController : UIViewController
      
      @property (nonatomic, strong) NSString *myPublicStringProperty;
      
      @end
      

      在这里,您可以在课堂外访问该属性。

      【讨论】:

      • 我认为问题不是“如何处理私有与公共属性?”,而是“为什么选择私有属性而不是私有实例变量?”。
      • 所以,我不明白对不起:(
      • 您似乎在描述如何将财产设为私有而不是公开。但我不认为这是问题所在。问题是使用私有属性是否有任何优势,而不仅仅是定义一个私有实例变量(没有关联的属性)并直接使用该私有实例变量。
      【解决方案7】:

      首先,如果您不需要私有属性,请不要使用属性:除非您想以声明方式从编译器中获取某些特定行为,例如在赋值时强制NSString 复制,否则使用属性没有任何好处.

      此外,您不一定需要使用self 来访问这些属性:当为您自动或使用@synthesize 关键字合成属性时,您将获得一个“支持”该属性的变量。分配该变量是访问属性的一种完全合法的方式,例如,当您希望将其显示为只读时。

      但是,将这些变量设为私有是一个优势,即在类扩展中声明它们,如下所示:

      @interface MyClass() { // <<== Note the () -- it's a class extension
         SomeOtherClass *privateIvar;
      }
      

      如果您在类扩展中声明一个变量(在 .m 文件中而不是在标题中),您将能够隐藏 "SomeOtherClass" 的标题,这在您开发单个应用程序时并不重要,但是当您开始使用自己的类库进行开发时会变得非常有用。

      【讨论】:

        猜你喜欢
        • 2011-08-04
        • 2011-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-19
        • 2013-10-06
        • 2010-10-17
        相关资源
        最近更新 更多