Iphone 在Objective-C中alloc/init实例变量的正确方法?

Iphone 在Objective-C中alloc/init实例变量的正确方法?,iphone,objective-c,Iphone,Objective C,我在看Jeff LaMarche的优秀作品的一些示例代码时,发现了以下内容: - (void)applicationDidFinishLaunching:(UIApplication*)application { CGRect rect = [[UIScreen mainScreen] bounds]; window = [[UIWindow alloc] initWithFrame:rect]; GLViewController *theController = [

我在看Jeff LaMarche的优秀作品的一些示例代码时,发现了以下内容:

- (void)applicationDidFinishLaunching:(UIApplication*)application { CGRect rect = [[UIScreen mainScreen] bounds]; window = [[UIWindow alloc] initWithFrame:rect]; GLViewController *theController = [[GLViewController alloc] init]; self.controller = theController; [theController release]; // ... } -(无效)应用程序IDFinishLaunching:(UIApplication*)应用程序 { CGRect rect=[[UIScreen mainScreen]边界]; window=[[UIWindow alloc]initWithFrame:rect]; GLViewController*控制器=[[GLViewController alloc]init]; self.controller=控制器; [控制器释放]; // ... } 在.h中,我们看到“window”和“controller”被声明为IVAR:

@interface OpenGLTestAppDelegate : NSObject { UIWindow *window; GLViewController *controller; } @property (nonatomic, retain) IBOutlet UIWindow *window; @property (nonatomic, retain) IBOutlet GLViewController *controller; @end @接口OpenGLTestAppDelegate:NSObject { UIWindow*窗口; GLViewController*控制器; } @属性(非原子,保留)IBUIWindow*window; @属性(非原子,保留)IBOUTLGLVIEWCONTROLLER*controller; @结束 我的问题是:为什么“窗口”和“控制器”的分配方式不同

我想我理解为什么每种任务都有效(跟踪保留计数),但为什么它们以不同的方式分配?具体地说,为什么不以与window相同的方式分配控制器,而不通过setter使用这样的单行:

controller = [[GLViewController alloc] init]; 控制器=[[GLViewController alloc]init]; 一般来说,何时使用单线法,何时使用多线法


谢谢。

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

即便如此,单线解决方案似乎也同样有效:

self.controller = [[[GLViewController alloc] init] autorelease];
与显式消息发送相同的行(不带点语法)也可以工作:

[self setController:[[[GLViewController alloc] init] autorelease]];
这两种方法都将使新控制器保留适当的保留计数,并且仍然根据需要使用setter属性

(注意:相关代码链接在的末尾。)


编辑:

对不起,有什么困惑。代码在
\uuuuuuuuuuu PROJECTNAMEASIDENTIFIER\uuuuuuu AppDelegate.m
GLView.m
中都有一个“GLViewController*controller”ivar和属性,我正在查看后者。(在前者中,setter实际上是合成的,它将保留控制器。在第77-81行,您可以看到我提到的代码,而他实际上并不保留控制器,只有AppDelegate保留它。)


在app委托代码中,合成setter将保留GLViewController,因此我的单行替换建议仍然有效。关于可读性,我们可以从两个方面进行争论,但对于那些很好地理解retainrelease习惯用法的人来说,我认为单行版本更具可读性。它简洁地传达了意图,甚至提供了setter将保留控制器的隐式提示。额外的局部变量实际上只是多余的绒毛。

他是否为
控制器
实例变量创建了自定义setter

如果是这样,当通过setter更改
控制器
变量时,可能会调用一些代码。仅通过以下方式设置
控制器
变量:

controller = [[GLViewController alloc] init];
不会调用setter方法;但是,将新分配的对象分配给一个局部变量,然后将其设置为:

self.controller = theController;
将调用setter方法,因为它是一种简写方式:

[self setController:theController];
然后执行setter中的额外代码。这通常是两种方法的区别所在

编辑:

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

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

[[self controller] release]

然而,这是一种奇怪的方法,因为setter的合成实现将保留实例变量,但一旦它被设置为实例变量,他就会释放它,并且由于
release
调用取消了
retain
调用,使用单行法设置变量更有意义。

正如奎因所指出的,控制器ivar的赋值可以使用自动释放法写入单行。使用更详细版本的原因正是为了避免自动释放,而是使用手动释放。这是因为苹果建议尽量减少在iPhone上使用自动释放池。因此,必须将对新分配对象的引用存储在局部变量中,以便在调用setter后释放它

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

ivar设置有两种类型:

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


    刚刚在另一个帖子中找到了关于这个问题的好讨论:。

    如果你看一下从博客帖子链接的代码,你会发现他是这样做的。即便如此,第一行和第三行仍然是不必要的样板。添加自动转发器