Trying to understand how blocks working in objective-c. Got next question while reading apple's docs (link)
Here is an example how we should no use blocks:
void dontDoThis() {
void (^blockArray[3])(void); // an array of 3 block references
for (int i = 0; i < 3; ++i) {
blockArray[i] = ^{ printf("hello, %d\n", i); };
// WRONG: The block literal scope is the "for" loop.
}
}
But how we could get 3 different blocks that will print "hello, 0", "hello, 1" and "hello, 2"? I tried many different ways but every time I got "hello, 2" three times.
A block starts out life on the stack and, thus, a block's lifespan is only as long as the scope it is declared in.
The body of a for() loop -- the body of the loop in the {}s -- is a scope in and of itself. Thus, your code is putting a reference to something on the stack [the block] into a variable in the surrounding scope [the language array].
You need to copy the block to the heap to have it survive:
void dontDoThis() {
void (^blockArray[3])(void); // an array of 3 block references
for (int i = 0; i < 3; ++i) {
blockArray[i] = [^{ printf("hello, %d\n", i); } copy];
}
}
If not using ARC, you would also need to -release the copied blocks at some point.
You might find this weblog post handy (I wrote it shortly after Blocks were made public). This one goes into a few tips, tricks, and gotchas.
Wait -- yeah -- you're correct. There is magic going on in the ARC compiler that is causing the blocks to seemingly be on the heap magically. However, I can't find anything in the LLVM documentation that explicitly documents this behavior. If you turn off ARC, you'll see the output be something like 2,2,2 instead of 0,1,2.
This is somewhat new behavior. I wouldn't rely on this behavior until someone can find the explicit note in the compiler that defines exactly how this is supported.
#autoreleasepool {
void (^blockArray[3])(void); // an array of 3 block references
for (int i = 0; i < 3; ++i) {
void (^block)(void) = ^{ printf("hello, %d\n", i); };
NSLog(#"%p", block);
blockArray[i] = block;
NSLog(#"%p", blockArray[i]);
}
for (int i = 0; i < 3; ++i) blockArray[i]();
}
Outputs:
2012-12-24 16:15:36.752 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.755 jkdfjkfdjkdfjk[70708:303] 0x100108160
2012-12-24 16:15:36.758 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.759 jkdfjkfdjkdfjk[70708:303] 0x100108000
2012-12-24 16:15:36.760 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.760 jkdfjkfdjkdfjk[70708:303] 0x100102e70
hello, 0
hello, 1
hello, 2
Thus, the block is created on the stack and copied to the heap automatically on the assignment outside of the scope of the for() loop.
A similar test also reveals that the block will be copied when passed as an argument to NSArray's addObject:.
If you really wanted to get this to work you could use an NSMutableArray instead of a C array.
NSMutableArray *blocks = [NSMutableArray array];
for (int i = 0; i <= 3; i++) {
blocks[i] = ^{ printf("hello %d\n", i); };
}
By adding them to an NSMutableArray they will be copied off of the stack and onto the heap allowing them to outlive the scope of the for loop.
As #bbum points out the above does not work, I took the idea that blocks just work with ARC too far.
You would need to actively copy the blocks for them to work... so the following should work
NSMutableArray *blocks = [NSMutableArray array];
for (int i = 0; i <= 3; i++) {
blocks[i] = [^{ printf("hello %d\n", i); } copy];
}
Related
What are the semantics of capturing a variable by a block in objective-C?
#import <Foundation/Foundation.h>
#include <stdio.h>
int main()
{
NSMutableArray *arr = [NSMutableArray array];
for (int i = 0; i < 100; ++i) {
int j = i;
[arr addObject:^(void) {printf("%d %d\n", i, j); }];
}
for (void (^blk)(void) in arr) {
blk();
}
}
I was expecing this to print something like:
100 0
100 1
...
100 99
Instead, it prints:
99 99
99 99
...
99 99
How is it even possible that it's interpreting j as equal to 99 ? j isn't even alive outside the for loop.
Because you're not using ARC! Without it, your block isn't being copied. You're just getting lucky and running the very last block every single time.
The reason you're seeing 99 99 many times is simply due to undefined behaviour.
Let's take the first for-loop:
for (int i = 0; i < 100; ++i) {
int j = i;
dispatch_block_t block = ^(void) {printf("%d %d\n", i, j); };
[arr addObject:block];
}
[I've pulled out the block for clarity.]
Inside this for-loop, the block is created. It is created on the stack and never moved to the heap because there is no copy of the block to do so.
Each time around the for-loop, it's extremely likely (well, certain really) that the same stack space is used for the block. And then the address of the block (which is on the stack) is added to arr. The same address each time. But it's a new implementation of the block each time.
Once out of the first for-loop, arr contains the same value 100 times. And that value points to the last created block, which is still on the stack. But it's pointing to a block on the stack that can no longer be accessed safely because it's out of scope.
In the case of this example however, the stack space occupied by the block by sheer luck (OK, simple code) hasn't been reused. So when you go and use the block, it "works".
The correct solution is to copy the block when it is added to the array. Either by calling copy or letting ARC do that for you. That way, the block is copied to the heap and you have a reference counted block that will live as long as needed by the array and the scope in which it is created.
If you want to learn more about how blocks work and understand this answer more deeply, then I suggest my explanations here:
http://www.galloway.me.uk/2012/10/a-look-inside-blocks-episode-1/
http://www.galloway.me.uk/2012/10/a-look-inside-blocks-episode-2/
http://www.galloway.me.uk/2013/05/a-look-inside-blocks-episode-3-block-copy/
When I've set a breakpoint to the first for loop, I've noticed that it's being executed but the value is not being updated to the string "Viki". Instead it's just being NULL.
Also, the last line of code NSLog(#"Mahal"); is not executed. Why?
NSMutableArray *arr1;
for(int i=0; i<3; i++)
{
[arr1 addObject:#"Viki"];
}
NSLog(#"Hello");
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"Taj");
for(int i=0; i<3; i++)
{
NSLog(#"%#", [arr1 objectAtIndex:i]);
}
dispatch_sync(dispatch_get_main_queue(), ^{ // 2
NSLog(#"Mahal"); // 3
});
});
You didn't initialize arr1 to point to an actual array object. You've only declared the pointer variable, you've never made it point to anything. It defaults to nil and all messages to nil return nil (or other zero-valued result appropriate for the type).
You could have discovered this had you ever logged the value of arr1.
I know that a block descriptor is passed on the stack to a block function when it is invoked. Is there a variable name I can use to refer to this in my code (like self or _cmd for methods)
(^{
// how can I access the block descriptor here?
})();
edit
I actually want the block object, not the block descriptor...
In short, you can't. At least not directly (there is nothing akin to self within a block -- we thought long and hard about that, but couldn't come up with something both elegant nor enough need for it in light of the following pattern to justify adding such syntax).
If you want to refer to the block, you need to do something like:
__block void(^strawberryFields)();
strawberryFields = ^{ strawberryFields(); };
strawberryFields();
Note that the above will run forever. Note also that you might want to copy that block upon assignment if you plan on using the block later.
Consider:
NSMutableArray *array = [NSMutableArray array];
int i;
for(i = 0; i<5; i++) {
[array addObject:[^{ return i*i; } copy]];
}
You'll end up with an array with 5 blocks, each capturing a different value of i.
It may help to create a method to initialize each block for you. Here's a quick test that demonstrates each block has its own variable:
-(void (^)(void))intAddingBlock:(NSString *)name {
__block int intForThisBlock = 0;
return ^{
NSLog(#"%# before: %d", name, intForThisBlock);
intForThisBlock += 5;
NSLog(#"%# after: %d", name, intForThisBlock);
};
}
-(void)testTheBlock {
void(^block1)(void) = [self intAddingBlock:#"block 1"];
void(^block2)(void) = [self intAddingBlock:#"block 2"];
block1();
block1();
block2();
block1();
block2();
}
Output:
block 1 before: 0
block 1 after: 5
block 1 before: 5
block 1 after: 10
block 2 before: 0
block 2 after: 5
block 1 before: 10
block 1 after: 15
block 2 before: 5
block 2 after: 10
I have seen this over and over, why exactly is it faster to use fast enumeration in loops rather than an NSEnumerator using nextObject:.
NSEnumerator is the old way to enumerate over collections. It involves creating an object to represent the enumeration, then calling a method on it for every single iteration. While this was perfectly serviceable for many years, it's not terribly efficient, as it involves at least one message send for every iteration of the loop. NSFastEnumeration is the more modern approach, which leverages native language support to provide a much more efficient enumeration. The way it works under the hood is it creates a struct that represents the current enumeration state and repeatedly calls -countByEnumeratingWithState:objects:count: on the collection. This method returns a C array of objects in the objects out-param as well as a counter in the count out-param. This allows the caller to then iterate over the C array. In essence, this means one message call per chunk of objects, which, depending on the collection, could be as efficient as a single message call to get all objects.
If you have a bit of code that looks like
for (id obj in myArray) {
[obj doSomething];
}
This gets translated by the compiler into something roughly equivalent to
NSFastEnumerationState __enumState = {0};
id __objects[MAX_STACKBUFF_SIZE];
NSUInteger __count;
while ((__count = [myArray countByEnumeratingWithState:&__enumState objects:__objects count:MAX_STACKBUFF_SIZE]) > 0) {
for (NSUInteger i = 0; i < __count; i++) {
id obj = __objects[i];
[obj doSomething];
}
}
The actual variables used are hidden, and the maximum size of the object buffer is also implementation-dependent, but the basic idea is there. It translates iteration over an obj-c collection into iteration over a C array.
GCC 8.9.4 Fast enumeration
protocol
GNUstep libs/base/trunk/Source/NSEnumerator.m countByEnumeratingWithState:objects:count:
It is not same as Apple's implementation but it is helpful to understand.
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState*)state
objects: (id*)stackbuf
count: (NSUInteger)len
{
IMP nextObject = [self methodForSelector: #selector(nextObject)];
int i;
state->itemsPtr = stackbuf;
state->mutationsPtr = (unsigned long*)self;
for (i = 0; i < len; i++)
{
id next = nextObject(self, #selector(nextObject));
if (nil == next)
{
return i;
}
*(stackbuf+i) = next;
}
return len;
}
NSArray *array = something;
array = { {1,2}, {2,3}, {3,4} }
that means array is an array of array. so how can you access all the arrays and their values.
we can use for loop like this
for (int i = 0; i < array.count; i++)
{
NSArray x = [array objectAtIndex:i];
}
or a fast enum works like this
for(NSArray array2 in array)
{
// do what ever you want with this new array2.
}
this is a sample example.
PS. I forgot how the array looks in console.
Where should I use the for loop and where should I use the for in loop?
I would like to know the difference between them.
The traditional for loop in Objective-C is inherited from standard C and takes the following form:
for (/* Instantiate local variables*/ ; /* Condition to keep looping. */ ; /* End of loop expressions */)
{
// Do something.
}
For example, to print the numbers from 1 to 10, you could use the for loop:
for (int i = 1; i <= 10; i++)
{
NSLog(#"%d", i);
}
On the other hand, the for in loop was introduced in Objective-C 2.0, and is used to loop through objects in a collection, such as an NSArray instance. For example, to loop through a collection of NSString objects in an NSArray and print them all out, you could use the following format.
for (NSString* currentString in myArrayOfStrings)
{
NSLog(#"%#", currentString);
}
This is logically equivilant to the following traditional for loop:
for (int i = 0; i < [myArrayOfStrings count]; i++)
{
NSLog(#"%#", [myArrayOfStrings objectAtIndex:i]);
}
The advantage of using the for in loop is firstly that it's a lot cleaner code to look at. Secondly, the Objective-C compiler can optimize the for in loop so as the code runs faster than doing the same thing with a traditional for loop.
You mean fast enumeration? You question is very unclear.
A normal for loop would look a bit like this:
unsigned int i, cnt = [someArray count];
for(i = 0; i < cnt; i++)
{
// do loop stuff
id someObject = [someArray objectAtIndex:i];
}
And a loop with fast enumeration, which is optimized by the compiler, would look like this:
for(id someObject in someArray)
{
// do stuff with object
}
Keep in mind that you cannot change the array you are using in fast enumeration, thus no deleting nor adding when using fast enumeration