Why do people always use reassignment for instance variables in Objective-C (namely iPhone)?
我总是在 viewDidLoad 方法中看到示例代码,而不是说,例如
1
| someInstanceVar = [[Classname alloc] init]; |
他们总是去
1 2 3
| Classname *tempVar = [[Classname alloc] init];
someInstanceVar = tempVar;
[tempVar release]; |
这是为什么?不是完全一样,只是更长吗?
- 我从未见过这样的构造,您在哪个示例代码中找到了它?能否请您更具体一点。 var 和 tempVar 是成员变量吗?
-
不仅你永远看不到它。这是不对的。您可能会看到类似的内容,中间线替换为 self.someInstanceVar = tempVar;
简短的回答:这种模式一直出现在 iPhone 代码中,因为它被认为是创建新对象并将其分配给成员变量的最佳方式,同时仍然尊重所有内存管理规则并调用适当的一面效果(如果有的话),同时也避免使用自动释放。
详情:
您的第二个示例将创建一个僵尸,因为 var 保留了一个指向已释放内存的指针。一个更可能的用例如下所示:
1 2 3
| tempVar = [[Classname alloc] init];
self.propertyVar = tempVar;
[tempVar release]; |
假设 propertyVar 是声明为 copy 或 retain 属性,此代码将新对象的所有权移交给类。
更新 1:以下代码是等效的,但在 iOS 上不推荐*,这可能是大多数 iPhone 程序改用第一种模式的原因。
1
| self.propertyVar = [[[Classname alloc] init] autorelease]; |
* 在 iOS 上不鼓励自动释放,因为过度使用它会导致问题。确保您永远不会过度使用它的最简单方法是永远不要全部使用它,因此您会经常看到使用 alloc/init 和 release 的 iOS 代码,即使 autorelease 是可以接受的。这是编码器偏好的问题。
更新 2:由于 Cocoa 在幕后自动执行的内存管理,这种模式起初看起来令人困惑。这一切的关键是用于设置成员变量的点表示法。为了帮助说明,请考虑以下两行代码是相同的:
1 2
| self.propertyVar = value;
[self setPropertyVar:value]; |
当您使用点符号时,Cocoa 将调用指定成员变量的属性访问器。如果该属性已被定义为 copy 或 retain 属性(这是该模式在不创建僵尸的情况下工作的唯一方法),那么会发生几件非常重要的事情:
之前存储在 propertyVar 中的任何值都会被释放
新值被保留或复制
自动处理任何副作用(例如 KVC/KVO 通知)
- 我的错,修复了代码示例。仍然不明白他们为什么这样做。
-
instanceVar = value 和 this.instanceVar = value 之间有一个关键的区别。使用点符号告诉编译器调用属性访问器,它会在后台自动处理内存管理。
-
好的,所以点符号调用属性访问器,没有点它只是设置实例变量?...我想念 java...
-
你通常不使用 self.[a€|] 吗? @marty: self.instanceVar 访问 .h 文件中设置的属性。这也将 retain instanceVar 的新值。
-
@marty:是的,那是正确的 :) 起初这是一个棘手的概念,但如果它咬到你一次,你将永远不会忘记。
-
@e.James @marty 不,这并不完全正确。您需要整个 self. 部分才能实际访问该属性。
-
哦,因为它使用了属性访问器,所以它被保留了。但我仍然不确定原来的问题。制作临时变量和直接初始化实例变量有什么区别?
-
@marty:我在答案中添加了更多细节,我认为这可能有助于解释。您可以像在第一个示例中那样直接分配给实例变量,但这会让您在以后遇到错误。一个潜在的问题是 instanceVar 中的旧值在分配新值之前没有释放。显然,如果 instanceVar 中没有值,这不是问题,但与其依赖该假设,更容易始终使用"分配、设置属性、释放"模式并轻松睡眠。
-
您解释了分配给属性的不同之处,但不是为什么在将其分配给属性之前将其分配给临时 var 的原因 - 为什么不简单地 this.propertyVar =[[SomeClass alloc] init]?
-
好问题。您必须使用中间变量,以便在完成后释放一些东西。如果你调用'self.propertyVar = [[Someclass alloc] init];',你可以很容易地泄漏新对象。例如,假设"propertyVar"是一个复制属性。然后属性访问器将尽职地复制该新对象,并且该副本将在您的"dealloc"方法中正确释放,但原始对象将被泄露。
-
我的荣幸。我很高兴你提出了最后一点。我做了这么久,有时我忘记解释一些细节。
-
你的意思是 self 而不是 this?
-
@Chuck:啊。是的,我当然愿意。感谢您发现这一点。
-
避免在 iPhone 上使用 autorelease 的建议可能是 Apple 网站上最容易被误解的建议。在这种情况下,例如,自动释放新创建的实例可能会产生最小的影响,因为它可能应该在当前运行循环事件结束后仍然存在。避免自动释放是过早优化的一种情况,因此部分是万恶之源。
-
@JeremyP:这是一个棘手的问题。即使在这个例子中,如果这个代码块只被调用了几次,自动释放也是完全无害的。但是,如果从循环中调用相同的代码块,则影响可能会非常严重。由于很难保证不会从循环中调用特定的代码块,因此最简单的安全方法是完全避免自动释放。
-
@JeremyP:我不会真正称其为优化问题。这更像是 goto 的使用:虽然使用它当然不会导致代码出现问题,但完全不使用它肯定更安全。
-
@e.James:如果,如果,如果...配置文件以找出问题并在您知道存在问题时进行优化。在任何情况下,将循环的每次迭代都package在一个自动释放池中可能比跳过循环以避免自动释放更好。
-
@JeremyP:我同意应该避免过早的优化,但我不认为 alloc/init, assign, release 模式是一种优化。对我来说,这更像是一个方便和安全的问题。如果你总是使用这种模式,你永远不必担心自动释放的潜在副作用。
-
@e.James:并非所有优化都与速度有关。内存占用也有优化。在大多数情况下,alloc/init 和 release 可能比 autorelease 更危险,因为内存泄漏的可能性更大。例如,-stringWithFormat: 为您提供了一个自动释放的对象,您可以在使用后忘记它,但 alloc/initWithFormat: 要求您分配一个临时变量并稍后释放该临时变量。代码更难看,如果你忘记了释放 - 内存泄漏。
-
@JeremyP:我熟悉不同类型的优化,我当然可以看到你对这个问题的看法:避免自动释放以降低临时内存使用量,这是一种内存占用优化。如果您的应用程序是设备上唯一运行的应用程序,并且您可以轻松地分析大多数用例,那么没有理由避免自动释放。
-
我对此的看法是,由于 iPhone 和其他 iDevices 内存有限,而且您永远不知道您将拥有多少空间,因此最安全的选择是避免自动释放。 Apple 的开发工具很好地发现了您可能通过临时变量引入的任何内存泄漏。