NSMutableArray instance used in a block

2019-07-18 08:16发布

问题:

The following code snippet uses GCD to compute a set of integers and store them in an array.

factArray = {1!, 2!, ... n!} where k! designates factorial(k)=k*(k-1)*...*2*1.

I wonder why I can add objects to the factArray variable (instance of NSMutableArray) inside the block though factArray isn't declared with the __block qualifier.

NSUInteger n = 10;
NSMutableArray *factArray = [[NSMutableArray alloc] initWithCapacity:n];
__block NSUInteger temp = 1;
dispatch_apply(n,
               dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
               ^(size_t s) {
                   NSUInteger w;
                   temp *= floor(s) + 1;
                   w = temp;
                   [factArray addObject:@(w)];
               });

__block NSArray *sortedArray;
double delayInSeconds = 0.1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    sortedArray = [factArray sortedArrayUsingComparator:^NSComparisonResult(NSNumber *n1, NSNumber *n2) {
        return [n1 compare:n2];
    }];
    NSLog(@"%@", sortedArray);
});

Of course, this code still works if we add the __block qualifier.

回答1:

factArray is a variable, like temp. It's a pointer; it holds the memory address of something else. The Block gets a copy of that pointer, just like it gets a copy of temp. The object that's pointed to, though, isn't copied. It stays in the same place, and remains an NSMutableArray. You can send messages to it through the pointer; that doesn't change the pointer's value. It still points to the same place, and the contents of an object can be modified without changing the pointer.

If you wanted to get rid of that existing array and make a new one, and call that array factArray:

dispatch_apply(n, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
               ^(size_t s) {
                   factArray = [NSMutableArray array];
});

then you would be changing the pointer's value, and then you would need the __block specifier.

Colloquially, since ObjC objects can only be accessed via pointers, we often refer to the pointer as if it were the object. The distinction is largely unimportant; this is one of the times it is.



回答2:

You're confusing variables and objects. Variables point to objects, but a variable is distinct from the object it points to. The __block storage specifier affects variable storage, but doesn't really have any effect on the object pointed to by the variable. The object could be pointed to by any number of variables in addition to that one, and it wouldn't care what their storage looked like — it's just floating out in the heap while they point to it.

So, in short, you are not changing the variable — you're mutating the object.