为什么我必须分配一个对象并在Obj-C中设置我的实例变量=该对象?

时间:2012-02-12 23:31:14

标签: iphone objective-c ios avaudioplayer

好的,对于那里的专业人士来说这应该是一个简单的(免责声明,我在Obj-C中有点像菜鸟,主要是在C#中使用)。我有一个UIViewController子类;它的一个属性是AVAudioPlayer对象。当我写这段代码时:

NSString *path = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"caf"];
NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
[player initWithContentsOfURL:url error:NULL];
[url release];
[player setDelegate:self];
[player prepareToPlay];
[player play];

什么都没发生。

当我这样做时,这显然是正确的做法:

NSString *path = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"caf"];
NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
AVAudioPlayer *ap = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:NULL];
self.player = ap;
[ap release];
[url release];
[player setDelegate:self];
[player prepareToPlay];
[player play];

......一切都很美好。

为什么我需要分配一个对象,然后设置我的属性等于那个?如果这是一个太常见的问题,请道歉。

3 个答案:

答案 0 :(得分:5)

这是因为在您的第一个示例中,您没有使用alloc创建AVAudioPlayer。这意味着你在内存中实际上没有有效的AVAudioPlayer。

而且你也没有使用init *的返回值。因为init经常在实例本身上工作,所以如果你已经完成了alloc并将它分配给了player,这可能会在许多情况下起作用,但你不能依赖于它,并且它的惯例是使用返回值{{{ 1}}形式。

如果您想要更短的工作代码,可以替换

Class *var = [[Class alloc] init]

[player initWithContentsOfURL:url error:NULL];

答案 1 :(得分:4)

最初,您的player实例变量设置为nil(相当于C#中的null),就像构建时参考字段自动设置为null一样C#中的一个对象。然而,与C#不同,在nil上调用某些内容不会做任何事情(与抛出NullReferenceException相反)。因此,您需要先为player指定一些内容才能执行某些操作;否则,呼叫会无声地失败。

在C#中,你有new Foo(),它实际上做了两件事:它为Foo对象分配内存,然后在该分配的内存上调用构造函数;一步到位。在Objective-C中,这两个步骤是截然不同的。 alloc分配内存,init是“构造函数”。在您的第一个代码段中,您跳过了分配步骤,因此没有任何反应。您应该使用以下结果获得正确的结果:

player = [AVAudioPlayer alloc];
player = [player initWithContentsOfURL:url error:NULL];

或者,更简洁:

player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:NULL];

另请注意,init方法实际返回一个值。之后使用它来引用对象非常重要

id foo = [Foo alloc];
foo = [foo init]; // correct

id foo = [[Foo alloc] init]; // correct too

id foo = [Foo alloc];
[foo init]; // incorrect

原因是允许init方法返回完全不同的对象;它甚至可以是一个不同的阶级。 Objective-C是一种“鸭型”语言,只要你返回的类型实现了你所宣传的类的相同方法,一切都很好。 (例如,在Mac OS上,您永远不会看到NSString的实际实例。相反,您会看到NSCFString和其他几个。)

我还建议你打开Objective-C语言的自动保留计数功能,因为它可以释放大多数内存管理例程(主要是retain / release)。

答案 2 :(得分:1)

您必须从类中创建对象。

这个人和你的C#构造函数一样。它为新对象分配内存,并使用参数url和NULL初始化它。

[[AVAudioPlayer alloc] initWithContentsOfURL:url error:NULL]

如果您只是在您正在使用的对象的生命周期中使用过一次播放器,那么您不需要将“self.player”作为实例变量。完全从头文件中删除它,并将代码更改为:

NSString *path = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"caf"];
NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
AVAudioPlayer *ap = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:NULL];    
[url release];
[ap setDelegate:self];
[ap prepareToPlay];
[ap play];
[ap release];

如果您需要保留该播放器或在其他地方访问它,那么您需要将您的实例变量保持为“播放器”。您的代码应如下所示:

NSString *path = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"caf"];
NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:NULL];
[url release];
[player setDelegate:self];
[player prepareToPlay];
[player play];

然后你必须在稍后的时间点释放玩家,就像自我类的dealloc一样。