Почему я должен выделить объект и установить переменную моего экземпляра = этот объект в Obj-C? - PullRequest
0 голосов
/ 13 февраля 2012

Хорошо, это должно быть легко для профессионалов (отказ от ответственности, я вроде нуб в 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 ]

5 голосов
/ 13 февраля 2012

Это потому, что в первом примере вы не создаете AVAudioPlayer с помощью alloc.Это означает, что у вас нет действительного AVAudioPlayer в памяти.

А также вы не используете возвращаемое значение init *.Поскольку init часто работает с самим экземпляром, это, вероятно, сработает во многих случаях, если вы выполнили alloc и назначили его игроку, но вы не можете полагаться на это, и принято использовать возвращаемое значение с Class *var = [[Class alloc] init] form.

Если вы хотите более короткий рабочий код, вы можете заменить

[player initWithContentsOfURL:url error:NULL];

на

self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:NULL];
4 голосов
/ 13 февраля 2012

Изначально ваша переменная экземпляра player установлена ​​на nil (эквивалент null в C #), так же как поля ссылок автоматически устанавливаются на 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).

1 голос
/ 13 февраля 2012

Вы должны создать объект из класса.

Этот парень такой же, как ваш конструктор 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];

Если вам нужно держать этого игрока рядом или обращаться к нему где-то еще, вам нужно сохранить переменную вашего экземпляра "player".Ваш код должен выглядеть следующим образом:

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];

Тогда вам придется выпустить игрока в более поздний момент времени, как если бы он был освобожден из собственного класса.

...