Does compiler saves argument of a function? - ios

I have a function which is loading image by index in another thread using GCD .
So lets assume this :
-(void)loadMainImageToIndex:(long)index
{
NSDictionary *dic=[mainData objectAtIndex:index];
NSString *userImageUrl=[dic objectForKey:#"url"];
NSURL *userUrl=[NSURL URLWithString:userImageUrl];
[self downloadImageWithURL:userUrl completionBlock:^(BOOL succeeded, NSData *tdata)
{
if (succeeded)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^
{
//do something here
dispatch_async(dispatch_get_main_queue(), ^
{
//do something here that uses the index argument
And i call this function 3 times :
[self loadMainImageToIndex:0];
[self loadMainImageToIndex:1];
[self loadMainImageToIndex:2];
Question is, when the first call will end the thread operation , and will got to the point :
dispatch_async(dispatch_get_main_queue()
Will he see in there index=0 , or will he see the last index was called (==2) ?
Question is, does he copy the whole function so when he finishes he can remember the argument that started the method ?
Another thing, does calling it 3 times at the same time, is a bad practice ?
Thanks.

Short version: there's no problem here. You're doing fine and it'll behave like you would expect (each block will have the correct value).
Longer version:
-(void)loadMainImageToIndex:(long)index
Every call to this method will push a fresh copy of index onto the stack. It will be popped (destroyed) when the method returns. Even if this method is called many times in parallel (on different threads), each will have its own copy of index on its own stack. Local variables and arguments are private to a each call of a method or function.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^
The creation of a block here "captures" (copies) all the variables that it discovers within the block. That includes index because it's used by:
dispatch_async(dispatch_get_main_queue(), ^
So, there's no problem here. Your block that is sent to the main queue will have its own copy of index, which it made at the time the block was created. The enclosing block also made a copy of index at the time it was created, and the method made a copy of the value that was passed to it as index.
Note that "when a block is created" is the point at which the ^{} is evaluated, not the point that dispatch_ functions are called. Those functions accept a block, they don't create the block. It is completely legal (and common) to create a block and store it in a variable and later pass that block to something else. The block will capture (copy) its variables at the point that it's created. This concept is called a closure.

Related

iOS - Deal with Async task that point to a deallocated variable

I'm calling an asynchronous function from my controller and I pass to it a reference to an error variable, let's say:
NSError *err=nil;
[self myAsyncTask:&err];
if the controller get deallocated, the err variable does not exist anymore, so the app crash with a BAD_ACCESS error (because the function try to change the value of the err variable). How can I deal with that?
Notice that none of the built-in framework methods use pass by reference error reporting with asynchronous calls - they all use delegation or blocks to communicate error status. Using blocks, your API could be written to be used like:
[self doAsyncTaskWithErrorHandler: ^(NSError *error) {
//Handle error
}];
The method signature could look like
- (void)doAsyncTaskWithErrorHandler:(void (^)(NSError *error))errorHandler;
And in the implementation, where you used to do *error = someError; do something like:
NSError *error = ...;
if (errorHandler) {
errorHandler(error);
}
If I'm not mistaken this isn't really an issue with object lifetime though - if the stack frame is popped before the error is set then the pattern in the question would likely cause a crash as well.
How can I deal with that?
If you have an asynchronous function in process, you have to ensure that any variables that it uses remain valid until that function completes. Objective-C's memory management lends itself nicely to this -- a method like your -myAsyncTask can retain its arguments until it no longer needs them. That way, even if the controller (to use your word) is deallocated, the objects referred to by the variables will remain valid.
Another way to do it is to use a block for the async functionality. Blocks automatically retain the resources that they use, so they effectively solve this problem for you.

iOS: method returns a block then the block is stored in array, when should I copy it?

I have a method that returns a block and another method which uses the method:
- (MyBlock)blockForParameter:(id)param
{
MyBlock theBlock = ^(){NSLog(#"Param: %#", param);};
return theBlock;
}
- (void)methodUser
{
MyBlock theBlock = [self blockForParameter:something];
[self.allBlocks addObject:theBlock];
}
The question is, when should I copy the block? Should I copy the block when I return it in blockForParameter, or should I copy it when I add it to the array?
Thanks
Blocks must be copied if they "outlive the scope in which they were created", so you must copy the block in your blockForParameter method, before returning it.
Edit: With ARC, this is no longer true. See for example Objective-C: blocks in ARC and the references therein:
Returning a block from a function does not require to copy the block.
Adding a block to a collection does require a block copy.
Look at each method separately.
In -blockForParameter:, the block literal is returned from the method. The block must be copied in order to outlive the scope of the literal. In MRC, you would need to return the block copied and autoreleased. In ARC, you don't need to do anything.
In -methodUser, the block is not defined in that method; it is received from a method call. Thus, it must have already been copied (i.e. it is not a stack block). Thus, you do not need to do anything with it when you do addObject: to it.

void(^)(NSData*) what does it mean?

Hi I am using this library and I found the function:
- (void) queueRequest:(NSString*)urlPath completion:(void(^)(NSData*))completionWithDownloadedData;
I try to pass a simple NSData *data; and it throw an error, what really mean (void(^)(NSData*))? Is the first time that I see it.
Thanks a lot.
(void(^)(NSData*)) declares a code block.
You can call your function this way.
[obj queueRequest:urlPath completion:^(NSData* data){
/* some code */
}];
data is a parameter to your block, which you can work with. The block will be called when the queueRequest will finish, asynchronously.
The interface is asynchronous, meaning that the data will only be available sometime later. This means that the method can’t simply return the NSData* (without blocking for all the time, which is impractical). The problem is nowadays often solved with blocks, and the completion argument here is a block that takes an NSData* argument and returns void. This is how you call such a method:
[foo queueRequest:path completion:^(NSData *receivedData) {
NSLog(#"Received data: %#", receivedData);
}];
The call will return immediately and the block will be executed sometime later, when the data is available.
It's a block that accepts a NSData object as it's only argument and returns nothing.
See Apple's Blocks Programming Topics.

Variable returning null after block execution

I am dispatching a queue to download some flickr photos on a separate thread (in viewWillAppear). When I log the contents of the array inside the block, it shows everything perfectly:
dispatch_queue_t photoDowonload=dispatch_queue_create("photoDownload", NULL);
dispatch_async(photoDowonload, ^{
NSArray *photoList=[FlickrFetcher topPlaces]; //downloads flickr data
self.listOfCities=photoList;
NSLog(#"inside block: %#", self.listOfCities); //shows contents
});
but when I try to log the array that was set inside the block outside the block, it returns null.
dispatch_queue_t photoDowonload=dispatch_queue_create("photoDownload", NULL);
dispatch_async(photoDowonload, ^{
NSArray *photoList=[FlickrFetcher topPlaces];
self.listOfCities=photoList;
});
NSLog(#"after block: %#", self.listOfCities); //returns null
What's the problem here? self.listOfCities is set up as NSArray property so once it's set in the block, it should be accessible outside of it.
The code in the block is run asynchronously. So the code after the block is run before the code in the block has had a chance to run (or certainly complete at least).
I've just started learning Objective-c, and I can be blind for some kind of issues but I'm wondering what is the impact of the _dispatch_asynch_ on executing block of code shown above.
Docs says
The dispatch_async() and dispatch_sync() functions schedule blocks for concurrent execution within the dispatch framework.
Maybe NSLog is called before execution of code block and variable is not initialized yet.
#rmaddy You was faster.
Ok I figured this out. My goal was to update the tableView with the info returned by block.
The block execution was changing the array variable but that change was not getting shown.
The trick was to detect this change in the getter for the array as follows:
-(void) setListOfCities:(NSArray *)listOfCities
{
if (_listOfCities!=listOfCities)
{
_listOfCities=listOfCities;
[self.tableView reloadData]; //<-- reloads table after change
}
}

iOS - Grand Central Dispatch getting value from block in dispatch_async

I'm using below code to download some data from the web. Am I right that I need to retain the data like I have done? Also the NSLog statement from inside the block shows that the array has been populated, but when I run the NSLog outside the block the arrays show as (null). How would I save the data outside dispatch_async method?
__block NSArray *downloadedCareerIds;
__block NSArray *diskCareerIds;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/* Download stuff */
downloadedCareerIds = [[CareersParser idsFrom:#"web"] retain];
diskCareerIds = [[CareersParser idsFrom:#"disk"] retain];
DLog(#"downloadedCareerIds: %#", downloadedCareerIds);
DLog(#"diskCareerIds: %#", diskCareerIds);
});
DLog(#"downloadedCareerIds: %#", downloadedCareerIds);
DLog(#"diskCareerIds: %#", diskCareerIds);
The idea of dispatch_async is that you give it a block of code to execute asynchronously, therefore giving up any control of when that code gets executed. The call to dispatch_async returns once the block has been enqueued, NOT once the block has finished executing (hence async). Therefore, the log statements inside of the block you're passing to dispatch_async will get executed, almost always, after the log statements below your call to dispatch_async.
dispatch_async is a non blocking method so it will return immediately. So when the DLog statements outside the block are called, they will mostly not have been set. Hence you don't see the values you get from the internal log statements.
If you want to act on the data within the same method, you will have to either send a blocking dispatch_sync which is pointless or you can call the methods within the block.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
....
[self doStuffWithTheArrays];
});
Once the block is executed the objects will be available provided they are instance variables or you will lose the references.

Resources