My AppDelegate maintains a list of active window controllers to avoid ARC deallocating them too early. So I have a notification handler like this:
- (void) windowWillClose: (NSNotification*) notification {
[self performSelectorOnMainThread: #selector(removeWindowControllerInMainThread:)
withObject: windowController
waitUntilDone: NO];
}
- (void) removeWindowControllerInMainThread: (id) windowController {
[windowControllers removeObject: windowController];
}
I use the main thread because doing the handling on the notification thread risks deallocating the controller before it's ready.
Now, this works pretty well — except when there are animators currently running. I use animators in some places, through NSAnimationContext. I have looked at this QA, and the answer just isn't acceptable. Waiting for a while, just to get animation done, is really shoddy and not guaranteed to work; indeed it doesn't. I tried using performSelector:withObject:afterDelay, even with a larger delay than the current animation duration, and it still results in the animator running against nil objects.
What is the preferred way of doing controller cleanup like this? Not use NSAnimationContext but using NSAnimation instead, which has a stopAnimation method?
First, if some of your animations run indefinitely -- or for a very long time -- you're going to have to have a way to stop them.
But for things like implicit animations on views, you could simply use a completion method.
self.animating=YES;
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
[[v animator] setAlphaValue: 1];
} completionHandler:^{
self.animating=NO;
}];
Now, you only need to poll whether your animation is running and, if it's not running, proceed to close your window.
One nice way to do the polling is to set a timer with a fixed delay. If the animation is still running, just reset the timer and wait another interval.
Alternatively, you could send a notificaton from the completion handler.
I haven't used NSAnimationContext (always did this with NSAnimation, but mostly for historical reasons). But the typical way I like to managed things similar to this is to create short-lived retain loops.
Mark's answer is exactly the right kind of idea, but the polling is not required. The fact that you reference self in the completion handler means that self cannot deallocate prior to the completion handler running. It doesn't actually matter whether you ever read animating. ARC has to keep you around until the completion block runs because the block made a reference to you.
Another similar technique is to attach yourself to the animation context using objc_setAssociatedObject. This will retain you until the completion block runs. In the completion block, remove self as an associated object, and then you'll be free to deallocate. The nice thing about that approach is that it doesn't require a bogus extra property like animating.
And of course the final, desperate measure that is occasionally appropriate is to create short-lived self-references. For instance:
- (void)setImmortal:(BOOL)imortal {
if (immortal) {
_immortalReference = self;
}
else {
_immortalReference = nil;
}
}
I'm not advocating this last option. But it's good to know that it exists, and more importantly to know why it works.
Related
Let's say I'm transitioning from an old SKScene of an SKView to a new one:
//In some controller somewhere...
-(void)swapScenes{
MySceneSubclass *newScene = [[MySceneSubclass alloc] initWithSize:CGSizeMake(1024, 768)];
SKTransition *transition = [SKTransition crossFadeWithDuration:REALLY_LONG_DURATION];
[[self mySKView] presentScene:newScene transition:transition];
}
Let's additionally say I want my new scene to perform some action or animation once the transition is completed, but not before. What would I use to trigger that?
At first I thought I'd be able to override didMoveToView: to do this, but it turns out this is called at the very begining of the transition (in hindsight, this makes sense. In a crossfade, the incoming scene is composited at the very beginning of the animation, even if its opacity is very low).
Next, as a hail mary, I tried inserting a call to the new scene right after presentScene:
-(void)swapScenes{
MySceneSubclass *newScene = [[MySceneSubclass alloc] initWithSize:CGSizeMake(1024, 768)];
SKTransition *transition = [SKTransition crossFadeWithDuration:REALLY_LONG_DURATION];
[[self mySKView] presentScene:newScene transition:transition];
[newScene doSomethingAfterTransition]; //<----
}
But presentScene: predictably returned immediately causing this method to be called long before the transition had completed.
As a last resort, I'm considering something like:
[newScene performSelector:#selector(doSomethingAfterTransition) afterDelay:REALLY_LONG_DURATION];
But I'd really like to avoid that if at all possible. It seems like there ought to be an delegate action or notification or something that knows when the transition is over, right?
The answer to this was staring me in the face. As I mentioned above, in a transition both scenes need to be present throughout the animation. Thus the incoming scene's didMoveToView: is called immediately at the beginning of the transition instead of at the end as I expected.
Of course, by this same logic, the outgoing scene's willMoveFromView: won't get called until the end of the transition. Which is what I was looking for in the first place.
So, you can override -willMoveFromView: of the outgoing scene (or, more likely, some shared superclass) to send a notification or call a delegate or whatever you like when transition completes. In my case, I have it call a block so I can keep everything local to my -swapScenes method, but YMMV.
Perform selector after delay with the same delay as the transition is perfectly reasonable.
This is a commonly desired need and (ideally) we shouldn't be programming around the tools. The two workaround provided by #jlemmons and #LearnCocos2D are both functional, but each have their fallbacks.
I would highly suggest going to the Apple Bug Reporter and requesting the addition of
- (void)[SKScene didFinishTransitionToView:(SKView *)view]
The more people request, the sooner it may appear.
I am looking for a better way to do this, if possible.
I have an asynchronous callback that updates a local sqlite database. I set a flag in a singleton variable (archiveUpdateComplete) when the update completes. I sleep in a do-while until the flag gets set to true, then I hydrate my tableview. Would like to remove sleep()! Thanks for any suggestions.
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
- (void)viewDidLoad
{
dispatch_async(kBgQueue, ^{
//Hydrate word archive table view
do {
sleep(1.0);
} while ([sharedManager archiveUpdateComplete]==NO);
[self performSelectorOnMainThread:#selector(hydrateWordArchive) withObject:nil waitUntilDone:YES];
//Run custom activity indicator
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});
}
If you need to poll
Polling/sleeping is rarely necessary or good. As an alternative:
You can attach an NSTimer to the main thread's run loop.
The selector the timer calls can test [sharedManager archiveUpdateComplete]
if YES is returned, then
invalidate the timer
call [MBProgressHUD hideHUDForView:self.view animated:YES];
If you don't need to poll
There are a few immediate alternatives. Which you choose depends on what knows about what:
If your manager knows who to message following completion, then the manager can simply message it. If that must occur on the main thread you can use -[NSObject performSelectorOnMainThread:withObject:waitUntilDone:] to forward to the main thread. You may also see this approach with delegates. In the case of a singleton, it doesn't make a lot of sense to take this route.
If your manager does not know who is interested in the change/completion, your manager can post a NSNotification after the task has finished (on the current thread or from the main thread).
Key Value Observing (KVO) is another option.
Perhaps I'm missing something, but why don't you just use a completion callback for this?
In other words, you change your computation to "think" in terms of nested blocks. The first async block (on some concurrent queue) does the work of updating the database, and when it's done it dispatches another async block (to the same concurrent queue) which hydrates the tableview. Finally, from that block you dispatch_async yet another block on the main queue which updates the UI, since that's the only bit that needs to execute on the main queue.
Rather than poll, in other words, you want to chain your async operations. See COMPLETION CALLBACKS section of the man page for dispatch_async().
I'm familiar with the delegate pattern and nilling my delegates, especially when doing asynchronous calls which are still in progress when my view controllers disappear. I nil the delegate, and the callback successfully returns on a nil object.
I'm now experimenting with using completion blocks to make my code a little easier to read.
I call a network service from my view controller, and pass a block which updates my UITableView. Under normal circumstances it works fine. However, if I leave the view before it completes, the completion handler block is executed - but the UITableView is now a zombie.
Whats the usual pattern for handling this?
UPDATE WITH CODE SAMPLE
This is an iPad app, I have two view controllers on screen at once, like a split view. One is the detail, and the other is a grid of images. I click an image and it tell the detail to load the info. However, if i click the images too fast before they have chance to do the network call - I have the problems. On changing images the code below is called which counts the favourites of a image....
So here is my dilemma, if I use the code below - it works fine but it leaks in instruments if you switch images before the network responds.
If I remove the __block and pass in self, then it crashes with zombies.
I can't win... I'm sure i'm missing something fundamental about using blocks.
__block UITableView *theTable = [self.table retain];
__block IndexedDictionary *tableData = [self.descriptionKeyValues retain];
FavouritesController *favourites = [Container controllerWithClass:FavouritesController.class];
[favourites countFavouritesForPhoto:self.photo
completion:^(int favesCount) {
[tableData insertObject:[NSString stringWithFormat:#"%i", favesCount]
forKey:#"Favourites:" atIndex:1];
[theTable reloadData];
[tableData release];
[theTable release];
}];
Any tips? Thanks
SECOND UPDATE
I changed the way I loaded the favourites. Instead of the favourites being a singleton, I create an instance on each photo change. By replacing this and killing the old one - the block has nowhere to callback (i guess it doesn't even exist) and my code now just looks like the below, and it appear to be working:
[self.favourites countFavouritesForPhoto:self.photo
completion:^(int favesCount) {
[self.descriptionKeyValues insertObject:[NSString stringWithFormat:#"%i", favesCount]
forKey:#"Favourites:" atIndex:1];
[self.table reloadData];
}];
It doesn't leak, and doesn't appear to be crashing either.
I recommend you test that the tableview is not nil at the start of the block. It sounds like the tableview is properly discarded when its parent view goes off-screen, so after that point, no tableview operations are valid.
Retaining the UITableView within the block is a bad idea, because datasource/tableview updates can result in implicit method calls and notifications that will not be relevant if the tableview is not on-screen.
Block will retain any object that it references, except for those annotated with __block. If you want not to execute completion blocks at all, just make some property like isCancelled and check whether it is YES before calling completion block.
So you have a background operation which has to call back another object after it finishes and the object can be destroyed in the meantime. The crashes you describe happen when you have non retained references. The problem as you see is that the referred object goes away and the pointer is invalid. Usually, what you do is unregister the delegate inside the dealloc method so that the background task continues, and whenever it is ready to communicate the results back it says "Shoot, my callback object is nil", and at least it doesn't crash.
Still, handling manually weak references is tedious and error prone. You can forget to nil a delegate inside a dealloc method and it may go without notice for months before you encounter a situation where the code crashes.
If you are targeting iOS 5.0 I would read up upon ARC and the weak references it provides. If you don't want to use ARC, or need to target pre 5.x devices, I would recommend using zeroing weak reference libraries like MAZeroingWeakRef which work also for 3.x devices.
With either ARC's weak references or MAZeroingWeakRef, you would implement the background task with one of these fancy weak reference objects pointing back to your table. Now if the pointed object goes away, the weak pointer will nil itself and your background task won't crash.
I'm trying to write this down as concisely as possible, but it's not easy to describe -- so thanks for reading =)
I'm the main developer of the Open Source iPhone Framework Sparrow. Sparrow is modeled after the Flash AS3 Library, and thus has an event system just like AS3. Currently, that system works by specifying selectors - but I would love to expand that system by allowing the use of blocks for event listeners. However, I'm stumbling over memory management issues.
I will show you a typical use-case of events - as they are handled now.
// init-method of a display object, inheriting from
// the base event dispatcher class
- (id)init
{
if (self = [super init])
{
// the method 'addEventListener...' is defined in the base class
[self addEventListener:#selector(onAddedToStage:)
atObject:self
forType:SP_EVENT_TYPE_ADDED_TO_STAGE];
}
return self;
}
// the corresponding event listener
- (void)onAddedToStage:(SPEvent *)event
{
[self startAnimations]; // call some method of self
}
That's quite straight-forward: When an object is added to the display list, it receives an event. Currently, the base class records the event listeners in an array of NSInvocation-objects. The NSInvocation is created in a way that it does not retain its target and arguments. (The user can make it do that, but in 99% of the cases, it's not necessary).
That these objects are not retained was a conscious choice: otherwise, the code above would cause a memory leek, even if the user removed the event listener in the dealloc-method! Here is why:
- (id)init
{
if (self = [super init])
{
// [self addEventListener: ...] would somehow cause:
[self retain]; // (A)
}
return self;
}
// the corresponding event listener
- (void)dealloc
{
// [self removeEventListener...] would cause:
[self release]; // (B)
[super dealloc];
}
On first sight, that seems fine: the retain in the init-method is paired by a release in the dealloc method. However, that does not work, since the dealloc method will never be called, because the retain count never reaches zero!
As I said, the 'addEventListener...'-method does, for exactly this reason, not retain anything in its default version. Because of the way events work (they are almost always dispatched by 'self' or child objects, which are retained anyway), that is not a problem.
However, and now we come to the central part of the question: I cannot do that with blocks. Look at the block-variant of event handling, as I would like it to have:
- (id)init
{
if (self = [super init])
{
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
[self startAnimations];
}];
}
return self;
}
That looks great and would be very easy to use. However: when the user calls a method on 'self' or uses a member variable within the block -- which will be, well, almost always -- the block will automatically retain 'self', and the object will never be dealloc'ed.
Now, I know that any user could rectify this by making a __block reference to self, like this:
__block id blockSelf = self;
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
[blockSelf startAnimations];
}];
But, honestly, I am sure almost all of the users would not know to do so or forget to do so. An API should be not only easy to use, but also hard to misuse, and this clearly violates that principle. Users of the API would most definitely misuse it.
What bugs me is that I know that 'self' does not have to be retained -- it works in my current implementation without retaining it. So I want to tell the block that he does not need to retain self -- me, the library, should tell the block that, so that the user does not have to think about it.
In my research, I have not found a way to do so. And I can't think of a way to change my architecture to fit that limitation of blocks.
Has anybody got an idea what I could do about it?
Even if you have not, thanks for reading this far -- I know it was a verbose question ;-)
I discussed this topic with Apple support, and they told me what I had expected: currently, there is no way that would let me tell the block that it should not retain self. However, they offered two ways to work around the problem.
The first would be to pass self as an argument to the block rather than a block variable. The API would become:
[self addEventListenerForType:ADDED_TO_STAGE
block:^(id selfReference, SPEvent *event)
The API would thus be responsible for passing in the (non-retained) self. Developers would still have to be told that they have to use this reference instead of self, but at least it would be easy to use.
The other solution that Apple has used in this sort of situation is to provide a separate method from release/dealloc for shutting down the listener. A good example of this is the "invalidate" method of NSTimer. It was created because of a memory cycle between NSRunLoop and NSTimer (by design).
I think the design is fundamentally flawed, blocks or no blocks. You are adding objects as event listeners before they are fully initialised. Similarly, the object could still receive events while it is being dealloced. The adding and removing of event listeners should be decoupled from allocation/deallocation IMO.
As for your immediate issue, you may not need to worry about it. You are having a problem because you are implicitly adding a reference to an object to itself (indirectly) by referencing it in the block. Bear in mind that anybody else supplying a block is unlikely to be referencing the object that generates the event or its instance variables because they won't be in scope where the block is defined. If they are (in a subclass, for instance), there's nothing you can do except document the issue.
One way to reduce the likelihood of a problem is to supply the object generating the event as a property on your SPEvent parameter and use that in the block instead of referencing self. You'll need to do that anyway because a block need not be created in a context where the object generating the event is in scope.
following is .m code:
#import "ThreadLabAppDelegate.h"
#interface ThreadLabAppDelegate()
- (void)processStart;
- (void)processCompleted;
#end
#implementation ThreadLabAppDelegate
#synthesize isProcessStarted;
- (void)awakeFromNib {
//Set levelindicator's maximum value
[levelIndicator setMaxValue:1000];
}
- (void)dealloc {
//Never called while debugging ????
[super dealloc];
}
- (IBAction)startProcess:(id)sender {
//Set process flag to true
self.isProcessStarted=YES;
//Start Animation
[spinIndicator startAnimation:nil];
//perform selector in background thread
[self performSelectorInBackground:#selector(processStart) withObject:nil];
}
- (IBAction)stopProcess:(id)sender {
//Stop Animation
[spinIndicator stopAnimation:nil];
//set process flag to false
self.isProcessStarted=NO;
}
- (void)processStart {
int counter = 0;
while (counter != 1000) {
NSLog(#"Counter : %d",counter);
//Sleep background thread to reduce CPU usage
[NSThread sleepForTimeInterval:0.01];
//set the level indicator value to showing progress
[levelIndicator setIntValue:counter];
//increment counter
counter++;
}
//Notify main thread for process completed
[self performSelectorOnMainThread:#selector(processCompleted) withObject:nil waitUntilDone:NO];
}
- (void)processCompleted {
//Stop Animation
[spinIndicator stopAnimation:nil];
//set process flag to false
self.isProcessStarted=NO;
}
#end
I need to clear following things as per the above code.
How to interrupt/cancel processStart while loop from UI control?
I also need to show the counter value in main UI, which i suppose to do with performSelectorOnMainThread and passing argument. Just want to know, is there anyother way to do that?
When my app started it is showing 1 thread in Activity Monitor, but when i started the processStart() in background thread its creating two new thread,which makes the total 3 thread until or unless loop get finished.After completing the loop i can see 2 threads.
So, my understanding is that, 2 thread created when i called performSelectorInBackground, but what about the thrid thread, from where it got created?
What if thread counts get increases on every call of selector.How to control that or my implementation is bad for such kind of requirements?
Thanks
how to update UI controls in cocoa application from background thread
Simple: Don't.
How to interrupt/cancel processStart while loop from UI control?
Outside of processStart, set a flag variable. Inside of processStart, check that flag and exit the loop if it is set.
Don't try to “kill” a thread from another thread. It's always a bad idea. Tell the thread it's time to stop by setting the flag, and have the thread check that flag and stop at an appropriate time.
I also need to show the counter value in main UI, which i suppose to do with performSelectorOnMainThread and passing argument. Just want to know, is there anyother way to do that?
Yes.
When my app started it is showing 1 thread in Activity Monitor, but when i started the processStart() in background thread its creating two new thread,which makes the total 3 thread until or unless loop get finished.After completing the loop i can see 2 threads. So, my understanding is that, 2 thread created when i called performSelectorInBackground, but what about the thrid thread, from where it got created?
Profile your app using Instruments or Shark and look. It's probably the heartbeat thread for the progress indicator.
What if thread counts get increases on every call of selector.How to control that or my implementation is bad for such kind of requirements?
Every performSelectorInBackground:withObject: message starts a thread. If your thread count isn't going down, it's because your thread method didn't exit. If your thread count is too high, it's (probably) because you started too many threads.
There is a much better way to do this.
First, the general rule in Cocoa is never sleep. Think of this as special ultra-caffeinated Cocoa. For anything you might sleep for in another framework, there is almost always a better, usually easier, way in Cocoa.
With that in mind, look at processStart. All it does is do something every centisecond. How best to do that?
Cocoa has a class for this specific purpose: NSTimer. Create a timer that sends yourself a message at the desired interval, and respond to that message by updating the progress bar—that is, your timer callback method should essentially just be the loop body from processStart, without the loop.
By the way, 100 updates per second is overkill. First off, the user does not care that you have made 1/5th of a pixel's worth of progress since the last time you updated the bar. Second, the screen only updates about 60 times per second anyway, so updating anything visible faster than that is pointless.
- (void)dealloc {
//Never called while debugging ????
[super dealloc];
}
Assuming you put your app delegate in the MainMenu nib, the application object owns it because of that—but it doesn't know that, because it only knows about the app delegate as its delegate, which is a non-owning relationship. (And even if it were an owning relationship, that would just be two ownerships, of which the app would release one, which wouldn't help.)
However, the lifetime of the app delegate doesn't really matter. Its purpose as the delegate of the application means that it needs to last about as long as the application does, but when the application goes away, the process is exiting, which means the delegate will be deallocated as well, as part of the reclamation of the process's memory space. That's why dealloc isn't called—the whole process space goes away at once, instead of objects being deallocated one at a time.
So, in principle, yeah, the app delegate not getting explicitly cleaned up is kind of dodgy. In practice, don't put any temporary-files clean-up in its dealloc (use applicationWillTerminate: instead) and you'll be fine.
I typically work around the problem by putting all my real work in one or more other objects which the app delegate owns. The app delegate creates these other controllers in applicationWillFinishLaunching: and releases them in applicationWillTerminate:, so those objects do get dealloc messages. Problem solved.