Reputation: 5795
What is the difference in these examples? I can't figure out why are they different and provide different results. As I see, the code in block is to run in main thread sometime, and the result provided by the first code is either foo or bar, and not sure why and when.
NSString *myString = @"foo";
dispatch_async (dispatch_get_main_queue(), ^{
NSLog (@"%@", myString);
});
myString = @"bar";
Second:
NSMutableString *myString = [NSMutableString stringWithString:@"foo"];
dispatch_async (dispatch_get_main_queue(), ^{
NSLog (@"%@", myString);
});
[myString setString:@"bar"];
Upvotes: 1
Views: 121
Reputation: 27984
The difference is:
In the first case, you have two different objects, @"foo"
and @"bar"
. myString
first points to @"foo"
, then you change it to point to @"bar"
.
In the second case, you have one object, an NSMutableString
. myString
points to that object all the time. However, the contents of that NSMutableString
change, from "foo"
to "bar"
.
When you create a block, it captures the values of any local variables that it uses from the enclosing scope. So, in the first case, the block captures myString
when it was pointing to @"foo"
. The later change to make it point to @"bar"
does not affect the captured copy in the block.
In the second case, the block captures the value of myString
. But, it does not capture the state of the object that myString
points to. So, you can change the contents of the object, and the block will see the new contents when it runs.
By the way, this has nothing to do with dispatch_async
, GCD, or multithreading. You would see the same effect if you ran the block directly and synchronously.
NSString *myString = @"foo";
void (^myBlock)(void) = ^{
NSLog (@"%@", myString);
};
myBlock(); // prints "foo"
myString = @"bar";
myBlock(); // prints "foo"
NSMutableString *myString = [NSMutableString stringWithString:@"foo"];
void (^myBlock)(void) = ^{
NSLog (@"%@", myString);
};
myBlock(); // prints "foo"
[myString setString:@"bar"];
myBlock(); // prints "bar"
Upvotes: 3
Reputation: 25619
Here's how things look when you first create the block and call dispatch_async
:
--NSString-------
| "Foo" |
-----------------
^ ^
| |
myString GCD Block
And here's how it looks at the end of the function. You're assigning myString
to a whole new NSString instance, different from the one that was captured by the block:
--NSString------- --NSString-------
| "Foo" | | "Bar" |
----------------- -----------------
^ ^
| |
GCD Block myString
In this case, the block will always print "Foo" regardless of when the block is executed.
The second example starts out similarly:
--NSMutableString----
| "Foo" |
---------------------
^ ^
| |
myString GCD Block
Except you are modifying the same NSMutableString instance that was captured by the block:
--NSMutableString----
| "Bar" |
---------------------
^ ^
| |
myString GCD Block
The block could print either "Foo" or "Bar", depending on when it executes. If the block prints "Bar" it means that the block was executed after you modified the NSMutableString instance.
Upvotes: 2
Reputation: 41801
This is pretty subtle (i.e. it would probably be best to structure the code to avoid the need to answer this question :) )
In the first example, the block captures a const copy of a pointer to a literal constant NSString. The original pointer is then updated to point to a new string. The block should print "foo" at some arbitrary point later, from the main thread.
In the second example, the block captures a const copy of a pointer to a mutable NSString. The mutable NSString is then updated to have different contents. If the code is running on a non-main thread/queue, the block may print "foo", may print "bar", or may crash due to printing a string while it's changing. If the block is on the main thread/queue then it will print "bar".
Upvotes: 4