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

问题描述:

好的,这对于那里的专业人员来说应该是一件容易的事情(免责声明,我是Obj-C中的noob,主要用于C#)。我有一个UIViewController子类;它的一个属性是AVAudioPlayer对象。当我写这个代码:为什么我必须分配一个对象并在Obj-C中设置我的实例变量=该对象?

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

如果可能使用ARC,则不存在保留/释放或自动释放语句。还有一些编译器可以进行的优化。还有一些便利的方法,比如:'NSURL * url = [NSURL URLWithString:path];'这可以让你的生活更轻松。 – zaph 2012-02-13 00:03:41

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

而且你还没有使用init *的返回值。由于init经常在实例本身上工作,如果你已经完成了alloc并将其分配给了播放器,这可能会在很多情况下起作用,但是你不能依赖这个,并且它的约定使用返回值与Class *var = [[Class alloc] init]表单。

如果你想更短的工作代码,你可以用

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

最初,您的player实例变量被设置为nil(的null C#中的等价物),就像当构造在C#的对象引用字段被自动设置为null。但是,与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和其他几个实例。)

我还建议您打开目标的自动保留计数功能-C语言,因为它可以让您免去大部分内存管理例程(主要是retain/release)。

+0

对于C#的详细信息 – 2012-02-12 23:44:27

更换

[player initWithContentsOfURL:url error:NULL]; 

你必须创建该类的对象。

这个人和你的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。