Просто измените первую строку на:
__block MyVideoPlayer *videoPlayer = [[[MyVideoPlayer alloc] init] autorelease];
Причина, по которой это работает, заключается в следующем:
В среде с подсчетом ссылок по умолчанию, когда вы ссылаетесь на Objective-Объект C внутри блока, он сохраняется.Это верно, даже если вы просто ссылаетесь на переменную экземпляра объекта.Переменные объекта помечены блок __ модификатор типа хранилища, однако не сохраняются .
См. Документацию Apple по Переменные объектов и блоков
Но почему он не был выпущен для начала?
Простое объяснение состоит в том, что после выполнения completionBlock
он не освобождается.Он будет выпущен позже в dealloc
, поэтому все переменные, которые он сохраняет, все еще сохраняютсяЭто имеет смысл, поскольку можно выполнить блок столько раз, сколько вы пожелаете, пока он не будет освобожден.
Я видел такое поведение раньше, когда на объект, который владеет блоком, ссылаются в блоке и освобождает блок в dealloc, что предотвращает освобождение объекта. Решение состоит в том, чтобы слабо ссылаться на объект, которому принадлежит блок. Таким образом, тип владения, такой как MyVideoPlayer
, достигает dealloc
, что впоследствии освобождает блок (completionBlock
в этом примере).
Альтернативой использованию ключевого слова __block
во избежание сохранения типа является завершение объекта в NSValue
с использованием valueWithNonretainedObject:
и nonretainedObjectValue
методы.Например:
MyVideoPlayer *videoPlayer = [[[MyVideoPlayer alloc] init] autorelease];
NSValue *videoPlayerRef = [NSValue valueWithNonretainedObject:videoPlayer];
[videoPlayer canPlayAsset:(MyVideoAsset *)asset
completionBlock:^(BOOL isAssetPlayable) {
if (isAssetPlayable) {
MyVideoPlayer *tempVideoPlayer =
(MyVideoPlayer*)[videoPlayerRef nonretainedObjectValue];
[tempVideoPlayer playAsset:asset];
[self presentModalViewController:tempVideoPlayer animated:YES];
}
}];
Редактировать / Разговор
В этом разделе мы подробнее рассмотрим, почему блок не освобождается.Я собираюсь включить то, что я предполагаю, что происходит за MyVideoPlayer
.Было бы здорово увидеть реальный код, но этого должно быть достаточно.
Заголовочный файл ...
typedef void(^MyVideoPlayerCompletionBlock)(BOOL isAssetPlayable);
@interface MyVideoPlayer : NSObject
@property(nonatomic, copy) MyVideoPlayerCompletionBlock completion;
... // other property definitions (or ivars)
-(void)canPlayAsset:(MyVideoAsset*)asset
completionBlock:(MyVideoPlayerCompletionBlock)completion;
... // other methods defined
@end
Реализация ...
@implementation MyVideoPlayer
@synthesize completion = _completion;
-(void)dealloc {
// Note: Block is released in dealloc like any other property or variable
[_completion release], _completion = nil;
... // other variables and properties not shown here are released
[super dealloc];
}
-(void)canPlayAsset:(MyVideoAsset*)asset
completionBlock:(MyVideoPlayerCompletionBlock)completion {
... // Does something with asset. Plays it, stores it, whatever
// Saves completion block to call at a later time.
// Note that this code could alternatively look like
// _completion = [completion copy];
// Blocks are usually copied and not retained
self.completion = completion;
}
... В НЕКОТОРЫХ ТОЧКАХ ЗАВЕРШЕНИЯ БЛОКА ЗАВЕРШЕНИЯ ... (возможно, в обработчике событий или в каком-то другом виде)
-(void)SomeEventHandlerOrFuncThatCallsCompletionInMyVideoPlayer {
// Time to call completion!
if (self.completion) {
self.completion(YES); // OR no, doesn't matter
//
// WHOA! :: self.completion is not released
//
// i.e. self.completion is not nil, and all
// variables inside it are still retained
// because calling a block doesn't also
// release the block. To do that you would
// need to do: self.completion = nil;
// AFTER calling: self.completion(...);
//
// SO...
//
// Because the block was not released, it is
// still retaining variables (such as the current
// instance of MyViewPlayer). Note that the
// Block will never be released until dealloc
// is called. :( So..., if you want the block
// to retain the current MyViewPlayer until
// execution of the completion block is over, you
// will want to call: self.completion = nil;
// to release the block and all associated variables
// after calling the completion block.
}
}
@end