1

我正在学习内存管理一段时间。阅读 Apple 的内存管理指南以及其他一些作者的书/博客/文章/帖子。

仍然让我困惑的是我是否应该写:

nameOfMySynthesizedProperty = [[NSObject alloc] init]; // version 1

或者

nameOfMySynthesizedProperty = [[[NSObject alloc] init] autorelease]; // version 2

在一个示例项目中,使用手动内存管理并且没有任何dealloc方法,并且版本 1 用于所有类属性/ivars。有时甚至没有合成某些属性,但使用了它的吸气剂,

我读过的那些教程/指南中没有教授这些内容,但是该示例项目运行良好,没有崩溃......

任何人都可以给我一些光...

更新 1

示例项目中使用手动内存管理。

更新 2

另一个例子

AppDelegate.h

#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
{
}
@property (nonatomic, retain) UIWindow *window;
@property (nonatomic, retain) UIViewController *viewController;
@end

AppDelegate.m

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // To be inserted with the following choices of code
}
@end

AppDelegate.m->-(BOOL)application:application didFinishLaunchingWithOptions:方法中,以下哪项对于初始化是正确的self.window.rootViewController?(使用手动内存管理。使用 Xcode 5。)

版本 1

self.window.rootViewController = [[UIViewController alloc] init];
self.viewController = [[[UIViewControllerDrawer alloc]init] autorelease];
self.window.rootViewController = self.viewController;

版本 2

self.window.rootViewController = [[[UIViewController alloc] init] autorelease];
self.viewController = [[[UIViewControllerDrawer alloc]init] autorelease];
self.window.rootViewController = self.viewController;

版本 3

self.viewController = [[[UIViewControllerDrawer alloc]init] autorelease];
self.window.rootViewController = self.viewController;

这里我知道window是一个属性(它的实例变量也被命名为window)。但是是window.rootViewController实例变量吗?实验结果表明版本 3 正常工作,版本 1 和版本 2 都崩溃了。

4

3 回答 3

6

两者都不。您应该使用 ARC。今天很少有理由使用手动内存管理。(了解ARC 如何应用内存管理规则很有用,但直接使用它是个坏主意。)

如果您绝对必须使用手动内存管理,那么您通常也不应该以这种方式直接访问 ivars。除了 ininitdealloc,你应该这样做:

self.property = [[[NSObject alloc] init] autorelease];

init中,您应该这样做:

_property = [[NSObject alloc] init];

在任何一种情况下(但前提是您不应该使用手动内存管理),您将需要以下内容dealloc

[_property release];
_property = nil;

精确规则的最佳参考是内存管理策略

但我怎么强调都不为过:如果你甚至问这些问题,你应该使用 ARC。如果您不必问这些问题,您应该已经知道为什么要使用 ARC。如果您有某种排除 ARC 的非常专业的问题(例如在 32 位 OS X 上运行),那么您可能已经知道规则以及如何正确应用它们。

如果您遇到手动内存管理的现有代码,您应该使用“Edit>Refactor>Convert to Objective-C ARC...”这将为您修复它。

于 2013-10-28T17:53:02.630 回答
4

您在这里有几个不同的问题,我将尝试解决所有问题。

迪洛克

总是有一个 dealloc 方法,你可能不会重写它,但它至少是由 NSObject 实现的。你几乎总是需要在你的子类中重写这个方法来释放对象 ivars。使用 ARC,您只需将它们设置为 nil,但在 MRC 中,例如您需要向任何保留或分配的变量发送释放消息。

当对象保留计数达到 0 时,会自动调用 dealloc 方法。

使用自动释放

您发布的代码存在问题,您将自动释放的对象分配给 ivar,并且在自动释放池耗尽后(通常是事件循环的开始),该对象将不再有效。

当您不想保留对象时使用自动释放,但它需要为方法的调用者继续存在。但是,当一个方法返回一个对象时,该对象应始终根据Objective-C 所有权规则自动释放,但这并不意味着您总是需要自己发送自动释放消息。如果您要返回的对象是自动释放的,那么您可以简单地返回该对象。

任何你 alloc-init 的保留计数都为 1,你需要在某个时候释放它,否则你有泄漏。

@合成

您不需要显式合成属性。编译器将为您合成它们。您必须注意属性是如何声明的(如果您自己进行实现则如何实现),以了解分配给该属性是否会增加保留计数、复制对象或执行简单的分配。我在您更新的问题中看到您打算这样做self.property = [[[NSObject alloc] init] autorelease],因为这些属性被声明为保留。您可以改为这样做_property = [[NSObject alloc] init],这是相当典型的用法。在直接使用 ivar 之前,您需要了解属性的语义(例如,分配给 ivar 时不会触发 KVO 通知)。

ARC 与 MRC

只是关于MRC vs ARC的旁白。许多人声称您应该始终使用 ARC(如果可以的话)而不是 MRC。重要的是您要了解不正确地使用 ARC 会导致代码显着变慢。我已经看到“ARC”代码在循环中使用隐式强引用作为临时值,导致大量不必要的保留释放调用。了解 MRC 对于编写好的 ARC 代码 IMO 是必不可少的,不要让这些人劝阻你学习内存管理的来龙去脉。

于 2013-10-28T17:46:46.700 回答
2

如果您从以 alloc/init、copy、mutableCopy 或 new 开头的方法获取对象实例,则您拥有它并负责在完成后释放它。否则,您不负责发布它。如果你想保留一个非拥有的对象,你必须保留它,然后在完成后释放它。出于这些规则的目的,自动释放对象与释放对象相同。一个自动释放的对象将在未来的某个时候被释放(在大多数情况下是当前运行循环的结束)。

在您的问题中,您直接设置了一个 ivar。您不希望 ivar 指向的对象从您下方消失,因此您不应该自动释放它。除非有问题的行在您的类的-init方法中,否则您应该改用 setter 并在将对象传递给 setter ( self.nameOfMySynthesizedProperty = [[[NSObject alloc] init] autorelease];) 之前自动释放该对象。这样,setter(假设属性是strong/retain处理保留对象,并且如果您稍后将其再次设置为不同的对象,也会处理释放它。

最后,我很欣赏和钦佩您学习手动内存管理。我认为Objective-C程序员理解它很重要。因此,如果这只是一个学习练习,请继续按照自己的方式进行。但是,对于编写真正的代码,您可能应该使用 ARC,这使得整个问题基本上没有意义。

于 2013-10-28T17:50:56.970 回答