8

У меня есть метод, называемый в разных местах, называемый «cancelAllPendingDownloads». Это общий метод, который отменяет различные задания и обновляет внутренние счетчики.Как я могу ссылаться на __weak self в методе dealloc

Проблема происходит, когда она вызывается в методе dealloc

-(void)dealloc 
{ 
    [self cancelAllPendingDownloads]; // want to cancel some jobs 
} 

-(void)cancelAllPendingDownloads // updates some internals 
{ 
    __weak __typeof__(self) weakSelf = self; // This line gets a EXC_BAD_INSTRUCTION error in runtime 
    for(Download *dl in self.downloads) 
    { 
     dl.completionHandler = ^{ // want to replace the previous block 
     weakSelf.dlcounter--; 
     } 
     [dl cancel]; 
    } 
} 

Не знаю, почему он не в методе dealloc, как «я» до сих пор существует

Когда я изменить код

__typeof__(self) strongSelf = self; //everything works fine 
__weak __typeof__(self) weakSelf = strongSelf; (or "self") BAD_INSTRUCTION error 

Ошибка во второй строке

+2

Если ваш объект (именуемый 'self') освобождается, зачем вам вообще уменьшать' dlcounter'? Он скоро даже не существует - вам нужно использовать его позже в методе 'dealloc'? –

+2

Вы действительно не должны делать эту работу в dealloc.Вызовите 'cancelAllPendingDownloads' перед деаллоцированием. – zaph

+0

Из-за темы, почему в этом примере рекомендуется использовать ссылку '__weak' для' self'? 'dl' является экземпляром в объеме метода, не сохраняемым' self', поэтому должно быть хорошо использовать 'self' внутри блока, правильно? Это только для предотвращения запуска 'self.dlcounter -', если 'self' уже освобожден? В этом случае блок не сохраняет self, поэтому он сохраняет его в живых (в случае использования 'self' внутри блока). Спасибо –

ответ

16

Просто, чтобы сделать «вы не должны» или «Вы не можете» часть других хороших ответов более точным:

Функция выполнения для сохранения слабой ссылки является objc_storeWeak(), и Clang/ARC documentation состояния:

id objc_storeWeak(id *object, id value);

... Если значение является нулевым указателем или объект, на который он указывает, начался освобождение объекта, объект присваивается null и незарегистрирован как объект __weak . В противном случае объект регистрируется как объект __weak или обновляется его , чтобы указать значение.

Поскольку self объект уже начал открепление, weakSelf должен быть установлен в NULL (и, следовательно, не является никакой пользы).

Однако, кажется, что это ошибка (как описано здесь http://www.cocoabuilder.com/archive/cocoa/312530-cannot-form-weak-reference-to.html) что objc_storeWeak() аварии в этом случае, вместо того чтобы вернуться NULL.

+0

Проблема в том, что теперь будет два метода, делающих по существу одно и то же, за исключением того, что ссылается на «weakSelf», а другой будет продолжаться без него –

4

Если обход ct находится в состоянии dealloc, вы не должны создавать какие-либо новые ссылки на него. Рассмотрите объект как уже уничтоженный. Не используйте его в callback/delegate.

Отметьте, что dlcounter никогда не будет прочитан. Просто отмените соединения, не читая результаты.

TL; DR
- Как я могу ссылаться __weak самостоятельно в dealloc методе?
- Не ссылайтесь на него.

4

Вы не можете инициализировать неделю (или сильную) ссылку на себя в методе dealloc и использовать ее в другом месте - уже слишком поздно, объект будет неизбежно уничтожен.

Однако, вы можете попробовать это:

-(void)dealloc 
{ 
    NSArray* localDownloads = self.downloads; 
    for(Download* dl in localDownloads) { 
     [dl cancel]; 
    } 
} 

Должно быть ясно, что есть лучшие места, чтобы вызвать отмену, например, в контроллере представления, вы можете переопределить viewWillDisappear:.

0

Предполагаю, что вы используете ARC для своего проекта.

Прямо от Apple: Apple Talked about Weak and Strong

__strong is the default. An object remains “alive” as long as 
there is a strong pointer to it. 
__weak specifies a reference that does not keep the referenced object alive. 
A weak reference is set to nil when there are no strong references to the object. 

Это статья Объясняя Dealloc: Dealloc Method Explained and More

This method will be called after the final release of the object 
but before it is deallocated or any of its instance variables are destroyed. 
The superclass’s implementation of dealloc will be called automatically when 
the method returns. 

После этого на который указывает ... Я настоятельно рекомендую вам пересмотреть свой дизайн кода, потому что нет причин для того, чтобы вы позвонили слабым типам (самостоятельно), чтобы решить вашу проблему отмены этих загрузок в dealloc или любом тип deallocing, который включает в себя _ слабый _typeof__ сам по себе.

Что я могу порекомендовать, так это то, что этот класс, который вы пытаетесь отменить, загружает frin, отслеживает эти загрузки с помощью Download UniqueID и просто останавливает их или удаляет их на dealloc. Его проще и проще управлять, а не странный призыв к __weak я и ко всему, что вы делаете.

0

Короче говоря: вы можете использовать __strong ссылку на себя в dealloc вместо __weak для ваших целей, но если и только если эта сильная ссылка не переживет конец dealloc. В противном случае я бы посоветовал использовать __unsafe_unretained, который по-прежнему небезопасен, если он переживает dealloc, но более читается.

Дольше: у меня была аналогичная ситуация, когда объект (контроллер просмотра) во время dealloc должен отписаться от уведомлений. Это настраиваемая система уведомлений, и отмена подписки требует создания объекта со ссылкой на сущность, которая не подписана. я в конечном итоге с той же ситуацией: в dealloc нет никакого способа, чтобы создать этот объект, поскольку он требует слабой ссылки, который вызвал аварию (вот какой-то глупый демо-код, а не то, что вы бы в производстве):

@interface Dummy : NSObject 

@property(nonatomic, weak) id weakProperty; 
@property(nonatomic, strong) id strongProperty; 
@property(nonatomic, unsafe_unretained) id unsafeProperty; 

- (instancetype)initWithWeakStuff:(id)stuff; 
- (instancetype)initWithStrongStuff:(id)stuff; 
- (instancetype)initWithUnsafeStuff:(id)stuff; 

@end 

@implementation Dummy 

- (instancetype)initWithWeakStuff:(id)stuff { 
    self = [super init]; 
    if (self) { 
    _weakProperty = stuff; 
    } 
    return self; 
} 

- (instancetype)initWithStrongStuff:(id)stuff { 
    self = [super init]; 
    if (self) { 
    _strongProperty = stuff; 
    } 
    return self; 
} 

- (instancetype)initWithUnsafeStuff:(id)stuff { 
    self = [super init]; 
    if (self) { 
    _unsafeProperty = stuff; 
    } 
    return self; 
} 

- (void)dealloc { 
} 

@end 

@interface ViewController() 

@end 

@implementation ViewController 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    // Do any additional setup after loading the view, typically from a nib. 
} 

- (void)didReceiveMemoryWarning { 
    [super didReceiveMemoryWarning]; 
    // Dispose of any resources that can be recreated. 
} 

- (void)dealloc { 
    Dummy *dummy = [[Dummy alloc] initWithStrongStuff:self]; 
    [[NSNotificationCenter defaultCenter] 
     postNotificationName:@"some notification" 
        object:dummy]; // do something with it 
} 

@end 

Если, с другой стороны, ссылка была сильной, все, похоже, хорошо работает (во время dealloc). Проблема возникнет, если что вновь созданный объект переживет себя:

- (void)dealloc { 
    Dummy *dummy = [[Dummy alloc] initWithStrongStuff:self]; 

    dispatch_async(dispatch_get_main_queue(), ^{ 
    [[NSNotificationCenter defaultCenter] 
     postNotificationName:@"some notification" 
         object:dummy]; // do something with it 

    }); //Crash at the end of the block during dummy's dealloc 
} 

Это означало бы, что всякий раз, когда dummy объект должен был бы dealloc он будет пытаться уменьшить количество реф его strongProperty. И в этот момент ViewController был освобожден и выпущен уже. Тем не менее, ИМХО является «самым безопасным» способом для использования в этом случае unsafe_unretained. Технически это то же самое, что и использование assign: указатель будет назначен независимо от управления памятью, и эта ссылка не будет выпущена, когда она выходит за рамки. Но с помощью unsafe_unretained рассказывают читателям вашего кода (или будущего вас), что вы знали об этом риске, и, должно быть, были причины делать то, что вы делали.