it-roy-ru.com

Всегда передавать слабую ссылку на себя в блок в ARC?

Я немного запутался в использовании блока в Objective-C. В настоящее время я использую ARC, и в моем приложении довольно много блоков, в настоящее время я всегда ссылаюсь на self вместо его слабой ссылки. Может ли это быть причиной того, что эти блоки сохранили self и не допустили его освобождения? Вопрос в том, должен ли я всегда использовать weak ссылку на self в блоке?

-(void)handleNewerData:(NSArray *)arr
{
    ProcessOperation *operation =
    [[ProcessOperation alloc] initWithDataToProcess:arr
                                         completion:^(NSMutableArray *rows) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateFeed:arr rows:rows];
        });
    }];
    [dataProcessQueue addOperation:operation];
}

ProcessOperation.h

@interface ProcessOperation : NSOperation
{
    NSMutableArray *dataArr;
    NSMutableArray *rowHeightsArr;
    void (^callback)(NSMutableArray *rows);
}

ProcessOperation.m

-(id)initWithDataToProcess:(NSArray *)data completion:(void (^)(NSMutableArray *rows))cb{

    if(self =[super init]){
        dataArr = [NSMutableArray arrayWithArray:data];
        rowHeightsArr = [NSMutableArray new];
        callback = cb;
    }
    return self;
}

- (void)main {
    @autoreleasepool {
        ...
        callback(rowHeightsArr);
    }
}
244
the_critic

Это помогает не сосредоточиться на части обсуждения strong или weak. Вместо этого сосредоточьтесь на циклической части.

Цикл сохранения - это цикл, который происходит, когда объект A сохраняет объект B, и Объект B сохраняет Объект A. В этой ситуации, если любой объект освобожден:

  • Объект A не будет освобожден, поскольку объект B содержит ссылку на него.
  • Но Объект B никогда не будет освобожден, пока Объект A имеет ссылку на него.
  • Но Объект A никогда не будет освобожден, потому что Объект B содержит ссылку на него.
  • до бесконечности

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

Итак, нас беспокоит то, что мы сохраняем циклы , и сами по себе блоки не создают эти циклы. Это не проблема, например:

[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
   [self doSomethingWithObject:obj];
}];

Блок сохраняет self, но self не сохраняет блок. Если один или другой освобождается, цикл не создается, и все освобождается, как и должно быть.

Где вы попали в беду это что-то вроде:

//In the interface:
@property (strong) void(^myBlock)(id obj, NSUInteger idx, BOOL *stop);

//In the implementation:
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  [self doSomethingWithObj:obj];     
}];

Теперь ваш объект (self) имеет явную ссылку strong на блок. И у блока есть неявная сильная ссылка на self. Это цикл, и теперь ни один объект не будет освобожден должным образом.

Поскольку в такой ситуации self по определению уже имеет ссылку strong на блок, обычно проще всего решить, сделав явно слабую ссылку на self для использования блока:

__weak MyObject *weakSelf = self;
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  [weakSelf doSomethingWithObj:obj];     
}];

Но это не должен быть шаблон по умолчанию, которому вы следуете при работе с блоками, которые вызывают self! Это следует использовать только для разрыва того, что в противном случае было бы циклом сохранения между собой и блоком. Если бы вы использовали этот шаблон повсеместно, вы бы рискнули передать блок чему-то, что было выполнено после освобождения self.

//SUSPICIOUS EXAMPLE:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
  //By the time this gets called, "weakSelf" might be nil because it's not retained!
  [weakSelf doSomething];
}];
687
jemmons

Вам не нужно всегда использовать слабую ссылку. Если ваш блок не сохраняется, но выполняется и затем отбрасывается, вы можете сильно захватить себя, так как он не создаст цикл сохранения. В некоторых случаях вы даже хотите, чтобы блок удерживал себя до завершения блока, чтобы он не освобождался преждевременно. Однако, если вы захватите блок сильно, а внутри себя захватите, это создаст цикл сохранения.

25
Leo Natan

Я полностью согласен с @jemmons.

"Но это не должно быть шаблоном по умолчанию, который вы применяете при работе с блоками, вызывающими self! Это следует использовать только для того, чтобы разорвать то, что в противном случае было бы циклом сохранения между self и block. Если вы будете применять этот шаблон везде, вы ' я бы рискнул передать блок чему-то, что было выполнено после того, как self было освобождено ".

//SUSPICIOUS EXAMPLE:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
  //By the time this gets called, "weakSelf" might be nil because it's not  retained!
  [weakSelf doSomething];
}];

Чтобы преодолеть эту проблему, можно определить сильную ссылку на слабый элемент внутри блока.

__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
   MyObject *strongSelf = weakSelf;
  [strongSelf doSomething];
}];
23
Ilker Baltaci

Как указывает Лео, код, который вы добавили в свой вопрос, не предлагал бы сильный ссылочный цикл (например, сохранить цикл). Одна из проблем, связанных с операцией, которая может привести к сильному ссылочному циклу, может произойти, если операция не освобождается. Хотя ваш фрагмент кода предполагает, что вы не определили свою операцию как параллельную, но если она у вас есть, она не будет выпущена, если вы никогда не публиковали isFinished, или если у вас были циклические зависимости или что-то в этом роде. И если операция не будет освобождена, контроллер представления также не будет освобожден. Я бы предложил добавить точку останова или NSLog в метод dealloc вашей операции и подтвердить, что он вызывается.

Вы сказали:

Я понимаю понятие сохранения циклов, но я не совсем уверен, что происходит в блоках, так что это немного смущает меня

Проблемы с циклом сохранения (сильный ссылочный цикл), возникающие с блоками, аналогичны проблемам с циклом сохранения, с которыми вы знакомы. Блок будет поддерживать сильные ссылки на любые объекты, которые появляются внутри блока, и он не будет освобождать эти сильные ссылки, пока сам блок не будет освобожден. Таким образом, если блок ссылается на self или даже просто ссылается на переменную экземпляра self, которая будет поддерживать сильную ссылку на себя, что не разрешается до тех пор, пока блок не будет освобожден (или в этом случае, пока не освободится подкласс NSOperation).

Для получения дополнительной информации см. Раздел Избегайте сильных циклов ссылок при захвате себя Программирование в Objective-C: Работа с блоками документ.

Если ваш контроллер представления все еще не освобождается, вам просто нужно определить, где находится неразрешенная сильная ссылка (при условии, что вы подтвердили, что NSOperation освобождается). Типичным примером является использование повторяющегося NSTimer. Или некоторый пользовательский delegate или другой объект, который ошибочно поддерживает ссылку strong. Вы можете часто использовать Инструменты, чтобы отследить, где объекты получают свои сильные ссылки, например:

record reference counts in Xcode 6

Или в Xcode 5:

record reference counts in Xcode 5

19
Rob

В некоторых объяснениях игнорируется условие о цикле сохранения [Если группа объектов связана кругом сильных отношений, они поддерживают друг друга, даже если за пределами группы нет сильных ссылок.] Для получения дополнительной информации прочитайте - документ

0
Danyun Liu