IBOutlet是否需要成为财产&合成的?

时间:2009-08-03 10:00:04

标签: iphone interface-builder

在大多数示例中,我看到了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公开为公共属性,因为它们不是这样使用的,它们仅用于控制器实现。在没有真正需要的情况下将它定义在三个地方并不会让我觉得非常干(不要重复自己)。

4 个答案:

答案 0 :(得分:96)

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

  1. 寻找名为set< OutletName>的方法:。如果存在则调用它。
  2. 如果不存在任何方法,请查找名为< OutletName>的实例变量,将其设置为而不保留
  3. 在iPhone OS上,IBOutlets连接如下:

    1. call [object setValue:outletValue forKey:@“< OutletName>”]
    2. key的设置值的行为是这样的:

      1. 寻找名为set< OutletName>的方法:。如果存在则调用它。
      2. 如果不存在任何方法,请查找名为< OutletName>的实例变量,设置它并保留
      3. 如果你使用一个属性,你将在两个平台上都进入“寻找一个名为set< OutletName>:... ”的方法。如果您只使用实例变量,那么您将在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

答案 1 :(得分:12)

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

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

在第一个示例中,您始终必须使用dealloc方法释放插座。在第二个示例中,您必须仅使用iPhone OS释放插座。

答案 2 :(得分:4)

最终结果完全相同,但您必须记住以下几点:

  • 将实例字段用作插座时,不应在 dealloc 中释放它们。

  • 使用具有(保留)属性的属性时,您必须在 dealloc 中释放该属性(使用 self.property=nil 或释放支持变量)。这使得它更加透明。

实际上,这一切都归结为同样的旧规则:“你将释放你分配/保留的内容”。因此,如果您使用实例字段作为插座,则不会分配/保留它,因此您不应该将其释放。

答案 3 :(得分:1)

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

当开发人员在界面生成器中“分配”UILabel(按钮,视图等)时,通过将IBOulet拖动到Label或其他View组件,使用IBOutlet的第二个示例(无属性且无合成) 。在我看来,前面的拖放操作(标签到视图)也将子视图,标签添加到视图 - 依此类推。标签由View保留;一个视图由Window保留;窗口由文件所有者保留。文件的所有者通常是您在main中启动的文档。

当您单步执行程序时(通过添加awakeFromNib

),您将注意到
- (void)awakeFromNib
{
    [fooLabel blahblah];
}

fooLabel已经有一个内存地址。

这是因为Label是使用非init而不是initWithCoder从文件包(nib文件)初始化的。这基本上将文件流反序列化为对象 - 然后设置IBOutlet变量。 (我们还在讨论IBOutlet方法)。

另请注意,上述iOS方法使用键值方法

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

是Observer / Observable模式。该模式要求Observable对象引用Set / Array中的每个Observer。值的更改将迭代Set / Array并同样更新所有Observers。该Set WILL已经保留了每个Observer,因此iOS中缺少保留。

此外,其余的是猜测。

似乎您使用Interface Builder然后

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

应该可以改为

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

或     @property(非原子,分配)IBOutlet UILabel * fooLabel;

然后它不需要以dealloc方法发布。此外,它还将满足OSX和iOS的要求。

这是基于逻辑的,我可能会遗漏一些碎片。

然而,如果视图在程序的生命周期中持续存在可能无关紧要。而模态对话框中的标签(打开,关闭,打开,关闭)实际上可能每个周期都有过度保留和泄漏。这是因为(再次推测)每个关闭的对话框被序列化为一个文件系统,因此持续x,y位置和大小,以及它的子视图等。然后反序列化...在下一个会话打开(反对说最小化或隐藏。)