好的,对于那里的专业人士来说这应该是一个简单的(免责声明,我在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];
......一切都很美好。
为什么我需要分配一个对象,然后设置我的属性等于那个?如果这是一个太常见的问题,请道歉。
答案 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一样。