I have a game written using the new SpriteKit in iOS7. I have a customised SKSpriteNode which would fetch and display a Facebook profile picture. However, since it may take some time to load the picture. I tried to load the picture in background when I initialised the node and display it only when the picture is loaded. Here is code snippet I wrote:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Code to load Facebook Profile picture
// ...
SKSpriteNode *fbFrame = [SKSpriteNode spriteNodeWithTexture:facebookPicTexture];
dispatch_async(dispatch_get_main_queue(), ^{
// self is my customised SKSpriteNode - so here I just add a sprite node of
// a facebook picture to myself as a child
[self addChild:fbFrame];
});
});
It works fine normally. However, if the loading of Facebook profile pic is slow, the user may have already switch to another screen when the picture is loaded. In such case, self will actually be removed from the scene hierarchy and no reference will be made to it.
When I read the block doc, I think the async block will retain self and so I presume it will still be valid when the main thread block is called. It turns out from time to time if the pic loading is really slow and the second dispatch_async is called when self is removed from the hierarchy, a bad access error will occur at the line [self addChild:fbFrame].
Am I understand the block memory management incorrectly? And is there a way to solve that kind of problem?
Your understanding of the memory management is correct, that the presence of self in this block will retain self until the dispatched block is completed. The solution is to not retain self while the block runs, by using a weak reference to self inside the block:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do some stuff in background here; when done, then:
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf addChild:fbFrame];
});
});
You should review that block for any references to self (either explicit or implicitly by referencing an ivar directly), and replace them with weakSelf.
Or, (unlikely in this case) sometimes you'll see the weakSelf/strongSelf pattern where you must ensure that self isn't released during the execution of that nested block:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do some stuff in background here; when done, then:
dispatch_async(dispatch_get_main_queue(), ^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
// stuff that requires that if `self` existed at the start of this block,
// that it won't be released during this block
}
});
});
By the way, the other approach is to cancel the network request when the view controller is dismissed. This requires a more significant change (using a NSOperation-based approach rather than GCD; or use NSURLSessionTask-based approach that allows you to cancel the request), but it is the other way to tackle this.
I think you need to write like this:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
objc_setAssociatedObject(self, #"yourTag", #"Alive", OBJC_ASSOCIATION_RETAIN);
dispatch_async(queue, ^{
// Code to load Facebook Profile picture
// ...
SKSpriteNode *fbFrame = [SKSpriteNode spriteNodeWithTexture:facebookPicTexture];
dispatch_async(dispatch_get_main_queue(), ^{
NSString *strAlive = (NSString *)objc_getAssociatedObject(self, #"yourTag");
if (strAlive)
{
// self is my customised SKSpriteNode - so here I just add a sprite node of
// a facebook picture to myself as a child
[self addChild:fbFrame];
}
});
});
dispatch_release(queue);
When you dont want the dispatch to continue procedure when the self (ViewController) is not visible anymore so write this:
objc_setAssociatedObject(self, #"yourTag", nil, OBJC_ASSOCIATION_RETAIN);
In:
- viewWillDisapear Or - viewDidDisapear
And renew calling the dispatch when you come back to than screen.
Related
I am a newbie. I am using Grand Central Dispatch to populate an array (student_temp) on another thread. That part is working fine. The problem is I cannot pass the array to a class property (student_Array) where it is used throughout the class. I can't get the array back on the main thread.
it works fine until I get back tot he main thread and I can't pass student_temp into student_Array (the property) either inside or outside of GCD.
What am I doing wrong, or is there a better to populate the array property using GCD?
Thank you for your help. And please try to explain in non-technical language if possible I am new at this.
- (void)viewDidLoad
{
[super viewDidLoad];
R2LFetcher *studentFetch = [[R2LFetcher alloc] init];
__block NSMutableArray *student_temp = [[NSMutableArray alloc] init];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
//long-running code goes here…
student_temp = [studentFetch fetchToStudentArray];
dispatch_async(dispatch_get_main_queue(), ^{
// code the updates the main thread (UI) here...
student_Array = student_temp;
});
});
student_Array = student_temp;
A couple of reactions:
In the last line of your code, you're setting student_Array to student_temp. Clearly that line makes no sense because you're populating student_temp asynchronously. And you're opening yourself up to synchronization issues if you're trying to simultaneously access the save variable in two queues. Don't bother to assign student_Array to student_temp at the end of viewDidLoad, but rather just do it inside the nested dispatch_async calls.
Inside the block, you're populating and setting student_temp. It probably makes more sense to make that variable scoped within that block, avoiding temptation to access it from outside that block as well as simplifying your code because the __block qualifier is no longer needed.
This block is running asynchronously, so when you update student_Array in the main queue, you might want to update your UI at the same time (e.g. reload the tableview or whatever). Perhaps you're doing that already and just removed it for the sake of brevity, but I just wanted to make sure.
Thus:
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
R2LFetcher *studentFetch = [[R2LFetcher alloc] init];
// long-running code goes here, for example ...
NSMutableArray *student_temp = [studentFetch fetchToStudentArray];
dispatch_async(dispatch_get_main_queue(), ^{
student_Array = student_temp;
// code the updates the main thread (UI) here, for example...
[self.tableView reloadData];
});
});
}
You should be able to add objects to student_Array directly from your block. Unlike stack variables, properties and ivars don't get copied when used inside a block. Instead, self gets retained in the block, and the property is referenced through it.
Of course, you need to be aware of concurrency issues, e.g. if you need to access the data from the main thread as well. For that, you probably still want to have this at the end of your async GCD block:
// done populating the data
dispatch_async(dispatch_get_main_queue(), ^{
// update the UI
}
I have an IOS project (ARC disabled) which has several view controllers. One particular controller initialises a member object of type MyClass, however when the view controller is dismissed, I'm calling a cleanup method for the object which uses a thread (using dispatch-async) to make some time consuming operations and then when these operations are done im executing a [self release] on the main queue for the object. Is this a good practise, will it cause any errors? Below is a similar example to what im doing:
#implementation ViewController
- (void)viewDidLoad
{
myObj = [[MyClass alloc] init];
}
-(void)viewWillDisappear
{
[myObj cleanup];
}
#end
//myClass
#implementation MyClass
- (void)cleanup()
{
dispatch_queue_t myQueue = dispatch_queue_create ("MyClassDeallocQueue", NULL);
dispatch_async(myQueue, ^{
//time consuming operations
dispatch_async(dispatch_get_main_queue(), ^{
[self release];
});
});
}
#end
Is this a good practise, will it cause any errors?
Currently, your code has an unbalanced retain/release. That is definitely an error (over release).
"Is it good practice?" - well, I don't know what you are trying to accomplish. But if your goal is to keep self alive, until after the block is executed, it is already accomplished purely through the fact that self will be captured. So, strictly a release is not needed.
However, if you NOT explicitly release self on the main thread, you introduce a subtle bug: it might happen that the block has the last reference to self, and since it may execute on some arbitrary thread, it will release self on this non-main thread. And this is forbidden: UIKit methods (including dealloc) MUST be called on the main thread!
Thus, it might make sense:
[self retain];
dispatch_async(myQueue, ^{
// time consuming operation, which captures `self`
[self doSomething];
...
// ensure that `dealloc` will be executed on the main thread, if
// last reference is held by the block:
dispatch_async(dispatch_get_main_queue(), ^{
[self release];
});
});
or shorter:
dispatch_async(myQueue, ^{
// time consuming operation, which captures `self`
[self doSomething];
...
// ensure that `dealloc` will be executed on the main thread, if
// last reference is held by the block:
dispatch_async(dispatch_get_main_queue(), ^{
[self self];
});
});
Edit:
It's an interesting question, whether the "short" version is actually tread-safe or has a race:
Suppose, self will be released in the block executed on myQueue, as the effect of capturing self before it will be retained in the same bock as an effect of capturing self for the block executed on the main queue. Then, we have an issue. Comments appreciated.
I am using the following code to asynchronously download an image and set it to an image view .
dispatch_queue_t callerQueue = dispatch_get_current_queue();
dispatch_queue_t downloadQueue = dispatch_queue_create("com.myapp.processsmagequeue", NULL);
dispatch_async(downloadQueue, ^{
NSData * imageData = [NSData dataWithContentsOfURL:url];
dispatch_async(callerQueue, ^{
self.imageView.image = [UIImage imageWithData:imageData];
[self.imageActivityIndicatorView setHidden:YES];
[self.imageView setHidden:NO];
});
});
dispatch_release(downloadQueue);
I am aware the blocks automatically retain all the values they reference and then release them . But can self get released in between moving to downloadQueue and then transferring back to the callerQueue ?
This should be fine.
dispatch_async(downloadQueue, ^{ // --> downloadQueue will add a retain on self when it's created
dispatch_async(callerQueue, ^{ // --> callerQueue will add a retain on self when it's created
...
}); // --> callerQueue will release it's retain when it gets dealloced just after returning from here
// --> downloadQueue will release it's retain when it gets dealloced just after returning from here
});
Here's how it will execute:
downloadQueue adds retain on self // +1
downloadQueue starts executing the block // +1
callerQueue adds retain on self // +2
downloadQueue releases it's retain // +1
callerQueue starts executing the inner block // +1
callerQueue releases it's retain. // +0
So, at any time, there will be a retainCount on self. Btw, you can check even check the retain count with -[NSObject retainCount] at any time.
As a side note, why not use dispatch_get_main_queue() instead of saving callerQueue. You should never do UI operations on any other thread. This is just safer in case your function is called from any other thread.
First what happens: The inner block cannot capture self from the method because that method could be long gone when the inner block is created. Therefore, it captures self from the outer block. That means "self" is used as a variable in the outer block, which means the outer block captures it. So as written, self will be there when the inner block is executed.
On the other hand, you might not want that. You might not want to keep that view alive just so that the downloaded image can be stored there. In that case you write
_weak WhatEverType* weakSelf = self;
in your method, and
WhatEverType* strongSelf = weakSelf;
if (strongSelf != nil)
{
}
in the inner block. The result: That inner block doesn't keep "self" retained; self won't stay around just because it is used in the block. If it gets dealloc'ed, weakSelf is set to nil, and you check that. Instead of storing the image, you just throw it away. But once strongSelf is set to a non-nil pointer, you know it is going to stay to the end of the block.
Its always better not to keep self inside the queue or in block. Use the following to make the 'self' a non retained object:
__unsafe_unretained ViewController *tempController = self;
and then call every objects as tempController.imageView.image and so on.
I'm looking for a common and elegant way to manage interfaces update.
I know that user interface code must be run in main thread, so when i need some computation o network task i use GDC with this pattern:
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(aQueue, ^() {
//Backgroud code
dispatch_sync(dispatch_get_main_queue(), ^{
//Update the UI
}
}
The problem with this code is that i need always check if user has changed view during my computation, so the code is like:
dispatch_sync(dispatch_get_main_queue(), ^{
if (mylabel != nil) && ([mylabel superview] != nil) {
mylabel.text = _result_from_computation_;
}
}
There is some best ways?
Thanks.
You pretty well have it. However, in case you want to do more reading or want a more thorough explanation of what's going on...
You should read the Apple Docs Grand Central Dispatch (GCD) Reference and watch the WWDC 2012 video, Session 712 - Asynchronous Design Patters with Blocks, GCD and XPC.
If you're working with iOS, you can disregard XPC (interprocess communication) as it's not supported by the current OS version (6.1 at the time of this writing).
Example: Load a large image in the background and set the image when completed.
#interface MyClass ()
#property (strong) dispatch_block_t task;
#end
#implementation MyClass
- (void)viewDidLoad {
self.task = ^{
// Background Thread, i.e., your task
NSImage *image = [[NSImage alloc] initWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
// Main Thread, setting the loaded image
[view setImage:image];
});
});
}
- (IBAction)cancelTaskButtonClick:(id)sender { // This can be -viewWillDisappear
self.task = nil; // Cancels this enqueued item in default global queue
}
- (IBAction)runTaskButtonClick:(id)sender {
// Main Thread
dispatch_queue_t queue;
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, self.task);
}
In order to cancel and reload the interface later, all you have to do is set the dispatch_block_t variable to nil.
Perhaps more specifically to your problem, this example piece of code deals with Reading Data from a Descriptor, i.e., either the disk or network.
Typically, you would use the Call-Callback pattern which essentially gets a background thread, executes a task, and when completed calls another block to get the main thread to update the UI.
Hope this helps!
You can check the view window property:
if (myLabel.window) {
// update label
}
this is redundant if (label != nil) since if label is nil, then all label properties will also be nil (or zero) and setting them will not raise an exception.
I'm new to GCD and blocks and am easing my way into it.
Background: I'm working on a lazy loading routine for a UIScrollView using the ALAssetsLibrary. When my UIScrollView loads I populate it with the aspectRatioThumbnails of my ALAssets and then as the user scrolls, I call the routine below to load the fullScreenImage of the ALAsset that is currently being displayed. It seems to work.
(if anyone has a better lazy loading routine please post a comment. I've looked at all I could find plus the WWDC video but they seem to deal more with tiling or have much more complexity than I need)
My question: I use a background thread to handle loading the fullScreenImage and when that is done I use the main thread to apply it to the UIImageView. Do I need to use the main thread? I've seen that all UIKit updates need to happen on the main thread but I am not sure if that applies to a UIImageView. I was thinking it does, since it is a screen element but then I realized that I simply didn't know.
- (void)loadFullSizeImageByIndex:(int)index
{
int arrayIndex = index;
int tagNumber = index+1;
ALAsset *asset = [self.assetsArray objectAtIndex:arrayIndex];
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage];
if ([weakSelf.scrollView viewWithTag:tagNumber] != nil){
dispatch_async(dispatch_get_main_queue(), ^{
if ([weakSelf.scrollView viewWithTag:tagNumber]!= nil){
UIImageView * tmpImageView = (UIImageView*)[weakSelf.scrollView viewWithTag:tagNumber];
tmpImageView.image = tmpImage;
}
});
}
});
}
Yes, you need to use the main thread whenever you're touching UIImageView, or any other UIKit class (unless otherwise noted, such as when constructing UIImages on background threads).
One commentary about your current code: you need to assign weakSelf into a strong local variable before using it. Otherwise your conditional could pass, but then weakSelf could be nilled out before you actually try to use it. It would look something like
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage];
__strong __typeof__(weakSelf) strongSelf = weakSelf;
if ([strongSelf.scrollView viewWithTag:tagNumber] != nil){
dispatch_async(dispatch_get_main_queue(), ^{
__strong __typeof__(weakSelf) strongSelf = weakSelf;
if ([strongSelf.scrollView viewWithTag:tagNumber]!= nil){
UIImageView * tmpImageView = (UIImageView*)[strongSelf.scrollView viewWithTag:tagNumber];
tmpImageView.image = tmpImage;
}
});
}
});
Technically you don't need to do this in the first conditional in the background queue, because you're only dereferencing it once there, but it's always a good idea to store your weak variable into a strong variable before touching it as a matter of course.
If you need to render the image in UIImageView, you need to do this in main thread. It will not work unless you do it in main queue as shown in your code. Same is the case with any UI rendering.
if ([weakSelf.scrollView viewWithTag:tagNumber]!= nil){
UIImageView * tmpImageView = (UIImageView*)[weakSelf.scrollView viewWithTag:tagNumber];
tmpImageView.image = tmpImage;
}
As per Apple documentation,
Threading Considerations: Manipulations to your application’s user interface must occur on the main thread. Thus, you should always call
the methods of the UIView class from code running in the main thread
of your application. The only time this may not be strictly necessary
is when creating the view object itself but all other manipulations
should occur on the main thread.
Yes you need to use the main thread, since any UI changes needs to be done in the main thread.
As for using the GCD it is used to take the advantage of the Multi Cores on the device.
As for the strong and weak self
strong self: you might want a strong self, for in you code
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
(<your class> *) *strongSelf = weakSelf;
UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage];
if ([strongSelf.scrollView viewWithTag:tagNumber] != nil){
dispatch_async(dispatch_get_main_queue(), ^{
if ([strongSelf.scrollView viewWithTag:tagNumber]!= nil){
UIImageView * tmpImageView = (UIImageView*)[strongSelf.scrollView viewWithTag:tagNumber];
tmpImageView.image = tmpImage;
}
});
}
});
say you have a view which make a API call and it takes time, so you switch back to another view but you still want the image to be downloaded then use strong since the block owns the self so the image is downloaded.
weak self: if in the above situation you dont want the image to download once you move to a different view then use weak self, since the block doesn't own any self.
If you will not use strongSelf in the code suggested by Kevin Ballard then it might lead to a crash because of weak getting nilled out.
Also a good practice would be to even check for strong being non nil at the point of creating
strongSelf = weakSelf
if(strongSelf)
{
// do your stuff here
}