【问题标题】:Does an IBOutlet needs to be a property & synthesized?IBOutlet 是否需要是属性和合成的?
【发布时间】:2010-11-16 07:55:39
【问题描述】:

在大多数示例中,我看到以下 IBOutlets 设置:



(Example A)

FooController.h:

@interface FooController : UIViewController {
    UILabel *fooLabel;
}

@property (nonatomic, retain) IBOutlet UILabel *fooLabel;

@end

FooController.m:

@implementation FooController

@synthesize fooLabel;

@end

但这也很好用(注意:没有属性也没有合成):



(Example B)

FooController.h:

@interface FooController : UIViewController {
    IBOutlet UILabel *fooLabel;
}

@end

FooController.m:

@implementation FooController

@end

在示例 B 中定义 IBOutlets 有什么缺点吗?比如内存泄漏?似乎工作正常,我更喜欢不将 IBOutlets 公开为公共属性,因为它们不是这样使用的,它们仅在控制器实现中使用。在没有真正需要的情况下在三个地方定义它并不会让我觉得非常干燥(不要重复自己)。

【问题讨论】:

    标签: iphone interface-builder


    【解决方案1】:

    在 Mac OS X 上,IBOutlets 的连接方式如下:

    1. 查找名为 set: 的方法。如果存在,请调用它。
    2. 如果不存在方法,则查找名为 的实例变量,设置它不保留

    在 iPhone OS 上,IBOutlets 的连接方式如下:

    1. 调用 [object setValue:outletValue forKey:@""]

    key 的 set value 的行为是这样的:

    1. 查找名为 set: 的方法。如果存在,请调用它。
    2. 如果不存在方法,则查找名为 的实例变量,设置它并保留它。

    如果您使用属性,您将在两个平台上陷入“寻找名为 set:... 的方法”的情况。如果您只使用实例变量,那么您将在 Mac OS X VS iPhone OS 上拥有不同的保留/释放行为。使用实例变量并没有错,您只需要在平台之间切换时处理这种行为差异。

    这里是关于这个主题的完整文档的链接。 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051i-CH4-SW6

    【讨论】:

    • 您好乔恩,感谢您的详细回答!很有帮助
    • 如果变量名与属性名不同会怎样?不同有关系吗?
    • 上面的“OutletName”名称被定义为源代码中“IBOutlet”关键字旁边的名称。如果 IBOutlet 在 @property 中,那么实例变量的名称并不重要,因为会找到一个 setter。如果由于某种原因不存在 setter,则在连接插座时会引发异常。如果 IBOutlet 关键字在实例变量上,并且存在名称不匹配的 setter,则不会调用 setter。
    • stackoverflow.com/questions/10308792/… 建议在 Mac 上使用 ARC 时也显式使用弱引用。
    • 链接已失效。
    【解决方案2】:

    在 Mac OS X 上,默认情况下不保留 IBOutlets。这与 iPhone OS 上的行为相反:在 iPhone OS 上,如果您不声明一个属性,它将被保留,并且您必须在 dealloc 方法中释放此属性。此外,64 位运行时可以使用属性声明来合成实例变量。这意味着有一天实例变量(带有IBOutlet)可能会被省略。

    由于这些原因,始终创建一个属性并仅在该属性中使用IBOutlet 更加同质和兼容。不幸的是,它也更加冗长。

    在您的第一个示例中,您始终必须在 dealloc 方法中释放出口。在您的第二个示例中,您必须仅在 iPhone OS 上发布插座。

    【讨论】:

      【解决方案3】:

      最终的结果是完全一样的,但是你必须记住一些事情:

      • 当使用实例字段作为出口时,您不应该在 dealloc 中释放它们。

      • 当使用具有 (retain) 属性的属性时,您必须在 dealloc 中释放该属性(使用 self.property=nil 或通过释放支持变量)。这使得它对正在发生的事情更加透明。

      实际上,这一切都归结为同样的旧规则:“你应该释放你分配/保留的东西”。所以如果你使用实例字段作为出口,你没有分配/保留它,所以你不应该释放它。

      【讨论】:

      • 这个建议适用于 Mac OS X,但不适用于 iPhone OS。请参阅下面的答案。
      • 在 dealloc 方法中调用“self.property=nil”并不是一个好习惯。您不应该从 init 或 dealloc 调用方法,如果您是子类的子类,您的子类可能不希望这些 setter 在它被释放之后或在它被初始化之前被调用。
      • 这是释放使用实例变量综合的自动保留属性的唯一方法(没有任何支持字段的显式声明)。你别无选择,不管是不是好习惯。
      • 当然你有选择,如果你不使用垃圾回收,声明一个支持实例变量。
      • 这就是重点:当您使用实例变量综合时,没有支持的实例变量。所以如果你选择使用自动实例变量综合,释放它的唯一方法就是将属性设置为nil。
      【解决方案4】:

      这些示例可能使用了保留,因为示例代码以编程方式分配和初始化 UILabel,然后将其添加到 UIView。许多示例都是这种情况,因为学习如何使用 Interface Builder 通常不是他们的重点。

      当开发人员在 Interface Builder 中“分配”UILabel(按钮、视图等)时,使用 IBOutlet 的第二个示例(无属性且无合成)——通过将 IBOUlet 拖到标签或其他视图组件.在我看来,前面的拖放操作(Label 到 View)还添加了子视图、Label 到 View 等等。标签由视图保留;一个视图被 Window 保留;窗口由文件所有者保留。 File's Owner 通常是在 main 中启动的 Document。

      您会注意到,当您逐步执行程序时(通过添加 awakeFromNib

      - (void)awakeFromNib
      {
          [fooLabel blahblah];
      }
      

      那个 fooLabel 已经有一个内存地址了。

      那是因为标签不是使用 init 而是 initWithCoder 从文件包(nib 文件)初始化的。它本质上将文件流反序列化为一个对象 - 然后设置 IBOutlet 变量。 (我们仍在谈论 IBOutlet 方法)。

      还要注意前面提到的iOS方法使用的是Key Value方法

        call [object setValue:outletValue forKey:@"<OutletName>"]
      

      这是 Observer/Observable 模式。该模式要求 Observable 对象引用 Set/Array 中的每个 Observer。值的变化将迭代 Set/Array 并同样更新所有观察者。该 Set 将已经保留每个观察者,因此在 iOS 中缺少保留。

      进一步,剩下的就是猜测。

      似乎你确实使用Interface Builder的情况

       @property (nonatomic, retain) IBOutlet UILabel *fooLabel;
      

      应该改成

      @property (nonatomic, weak) IBOutlet UILabel *fooLabel;
      

      或 @property (nonatomic, assign) IBOutlet UILabel *fooLabel;

      然后它不需要在 dealloc 方法中释放。此外,它将满足 OSX 和 iOS 的要求。

      这是基于逻辑的,我可能会在这里遗漏一些内容。

      尽管如此,视图是否在程序的生命周期中保持不变可能并不重要。而模式对话框中的标签(打开、关闭、打开、关闭)实际上可能在每个周期都有过度保留和泄漏。那是因为(再次推测)每个关闭的对话框都被序列化到一个文件系统中,因此保留了 x,y 位置和大小,以及它的子视图等。随后反序列化......在下一个会话打开时(反对说 minimiz或隐藏。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-21
        • 2011-03-26
        • 1970-01-01
        • 2011-12-19
        • 2011-08-20
        • 2015-09-20
        • 2012-04-18
        • 2016-10-17
        相关资源
        最近更新 更多