¿Se necesita la palabra clave __block para escribir en bucle en el objetivo C cuando se muta una matriz o diccionario?

__block NSMutableArray *xxxx = [NSMutableArray new]; // is __block needed?
for (obj in objs) {
    [xxx addObject];
}
0
Developer Sheldon 29 abr. 2020 a las 21:56

3 respuestas

La mejor respuesta

No, no es. La palabra clave __block solo es necesaria cuando se cumplen varias condiciones:

  • Hay un "bloque" de Objective-C: un segmento de código capturado como un objeto, y
  • Hay una variable declarada fuera del bloque, y
  • El valor de la variable se cambia dentro del bloque.

Esto podría ser más claro por contraejemplo. ¿Dónde no necesita la palabra clave __block?

No lo necesita cuando su bucle es un bucle C simple, incluidos bucles simples for y bucles que usan NSFastEnumeration:

NSInteger sum = 0;
for (NSInteger i = 0; i < 10; i++) {
    sum += i; // okay! this is a plain C for loop
}

NSArray *numbers = @[@1, @2, @3];
for (NSNumber *i in numbers) {
    sum += [i integerValue]; // okay! this is an NSFastEnumeration object loop
}

Cuando tiene un bloque Objective-C real, no lo necesita si el cuerpo del bloque realmente no muta el valor de la variable. Este suele ser el caso si su bloque solo envía mensajes Objective-C a un objeto:

NSMutableArray *evenNumbers = [NSMutableArray array];
NSArray *numbers = @[@1, @2, @3];
[numbers enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    if ([obj integerValue] % 2 == 0) {
        [evenNumbers addObject:obj]; // okay! this is an ObjC message send, which does not mutate the pointer value of `evenNumbers`
    }
}];

El único caso que necesita es cuando su bloque intenta mutar el valor de una variable directamente.

__block NSString *match = nil;
__block NSUInteger matchIndex = NSNotFound;
NSArray *candidates = @[@"foo", @"bar"];
[candidates enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    if ([obj isEqual:@"foo"]) {
        // these must be __block, because we are mutating their values in this ObjC block
        match = obj;
        matchIndex = idx;
        *stop = YES;
    }
}];
2
Tim 30 abr. 2020 a las 06:28

No. __block es necesario si el valor de la variable se cambia dentro del bloque, por ejemplo.

__block NSUInteger sum = 0;
[objs enumerateObjectsUsingBlock:
^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    sum += obj.number; // just imaginary
}];

En su caso, el valor de la variable es puntero y no se modifica.

0
Asperi 30 abr. 2020 a las 06:21

No, en el fragmento de código que ha publicado no necesita el modificador __block.

0
Nik 30 abr. 2020 a las 06:23