在Objective-C中正确分配/初始化实例变量的方法?

时间:2009-06-12 14:51:42

标签: iphone objective-c

当我看到Jeff LaMarche的优秀blog时,我正在查看一些示例代码:

- (void)applicationDidFinishLaunching:(UIApplication*)application
{
    CGRect rect = [[UIScreen mainScreen] bounds];

    window = [[UIWindow alloc] initWithFrame:rect];

    GLViewController *theController = [[GLViewController alloc] init];
    self.controller = theController;
    [theController release];

    // ...
}

在.h中,我们看到“窗口”和“控制器”是如此声明的ivars:

@interface OpenGLTestAppDelegate : NSObject 
{
    UIWindow            *window;
    GLViewController    *controller;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet GLViewController *controller;
@end

我的问题是:为什么“窗口”和“控制器”以不同的方式分配?

我想我理解为什么每种作业都有效(跟踪保留计数),但为什么它们以不同的方式分配?具体来说,为什么不以相同的方式分配控制器窗口是这样的,如果没有通过设置器,那么窗口是:

    controller = [[GLViewController alloc] init];

一般来说,何时使用单行方法,何时使用多行方法?

感谢。

3 个答案:

答案 0 :(得分:3)

他是否为controller实例变量创建了自定义setter?

如果是这样,可能会有通过setter更改controller变量时调用的代码。仅将controller变量设置为:

controller = [[GLViewController alloc] init];

不会调用setter方法;但是,将新分配的对象分配给局部变量,然后使用:

进行设置
self.controller = theController;

会调用setter方法,因为它是一种写作的简写方式:

[self setController:theController];

将执行setter中的额外代码。这通常是您期望区分两种方法的地方。

修改

显然,在查看代码之后,他没有实现自定义setter方法,但是当使用时使用的方法仍然是最常用的,当实现自定义setter方法时

我对额外代码背后原因的猜测是他计划在分配后释放变量,如果分配给局部变量,他可以使用局部变量调用setter方法,然后调用release然后在局部变量上。这总体上比使用

更具可读性
[[self controller] release]

然而,这是一种奇怪的方法,因为setter的综合实现将保留实例变量,然后他在将其设置为实例变量后释放它,并作为release呼叫取消retain呼叫,使用单行方法设置变量更有意义。

答案 1 :(得分:2)

额外的代码似乎只是因为他特别想要使用属性(setter方法)。在他的实现(GLView.m)中,-setController还根据控制器是否响应(实现)-setupView:方法来设置布尔值ivar。

即便如此,单线解决方案似乎也会起作用:

self.controller = [[[GLViewController alloc] init] autorelease];

与显式消息发送相同的行(没有点语法)也可以使用:

[self setController:[[[GLViewController alloc] init] autorelease]];

任何一种方法都会使新控制器保留适当的保留计数,并仍然根据需要使用setter属性。

(注意:有问题的代码在this blog post末尾链接。)


修改

对不起有任何困惑。代码在___PROJECTNAMEASIDENTIFIER___AppDelegate.mGLView.m中都有一个“GLViewController * controller”ivar和属性,我正在查看后者。 (在前者中,setter确实是合成的,它将保留控制器。在第77-81行,你可以看到我提到的代码,他实际上并没有保留控制器 - 只有AppDelegate保留它。)< / p>

在app委托代码中,合成的setter将保留GLViewController,因此我的单行替换建议仍然存在。人们可以争论可读性的两种方式,但对于那些理解保留释放习语的人,我建议单行版本更具可读性。它简洁地传达了意图,甚至提供了一个隐含的暗示,即setter将保留控制器。额外的局部变量实际上只是不必要的毛病。

答案 2 :(得分:2)

正如Quinn指出的那样,可以使用autorelease方法将对控制器ivar的赋值写入一行。使用更详细版本的原因正是为了避免自动释放并使用手动释放。这是由于Apple建议尽量减少在iPhone上使用自动释放池。因此,您必须将对新分配的对象的引用存储在本地变量中,以便在调用setter后释放它。

考虑何时使用直接赋值给实例变量的问题(如window ivar的情况)以及何时使用setter方法(如controller ivar的情况),主要是风格问题,但你最好保持一致。

ivar设置有两种样式:

  1. 始终使用直接分配到ivar。切换到仅适用于ivars的setter方法,setter必须在分配之外执行一些额外的工作。
  2. 始终对所有ivars使用setter方法。
  3. 就个人而言,我认为使用第二种风格会产生更一致和可维护的代码。如果有一天你意识到你的setter必须执行更多工作,你应该只更改setter,而在使用第一个样式时你也应该更改所有出现的直接赋值给setter调用。

    刚刚在另一个帖子中找到了对该问题的良好讨论:instance variable/ method argument naming in Objective C