开发者

UIViewController prevent view from unloading

When my iPhone app receives a memory warning the views of UIViewControllers that are not currently visible get unloaded. In one particular controller unloading the view and the outlets is rather fatal.

I'm looking for a way to prevent this view from being unloaded. I find this behavior rather stupid - I have a cache mechanism, so when a memory warning comes - I unload myself tons of data and I free enough memory, but I definitely need this view untouched.

I see UIViewController has a method unloadViewIfReloadable, which gets called when the memory warning comes. Does anybody know how to tell Cocoa Touch that my view is not reloadable?

Any other suggestions how to prevent my view from being unloaded on m开发者_运维技巧emory warning?

Thanks in advance


Apple docs about the view life cycle of a view controller says:

didReceiveMemoryWarning - The default implementation releases the view only if it determines that it is safe to do so

Now ... I override the didReceiveMemoryWarning with an empty function which just calls NSLog to let me know a warning was received. However - the view gets unloaded anyway. Plus, on what criteria exactly is decided whether a view is safe to unload ... oh ! so many questions!


According to the docs, the default implementation of didReceiveMemoryWarning: releases the view if it is safe to do (ie: superview==nil).

To prevent the view from being released you could override didReceiveMemoryWarning: but in your implementation do not call [super didReceiveMemoryWarning]. That's where the view is released by default (if not visible).

The default didReceiveMemoryWarning releases the view by calling [viewcontroller setView:nil], so you could override that instead.


What appears to be working for me was to override setView: to ignore setting to nil. It's kludgy, but then, this is a kludgy issue, and this did the trick:

-(void)setView:(UIView*)view {
    if(view != nil || self.okayToUnloadView) {
        [super setView:view];
    }
}


Could it be so simple?

Even though nowhere in the documentation this is mentioned, it seems that if I exclusively retain my view in viewDidLoad, then it does not get released on Memory Warning. I tried with several consecutive warnings in the simulator and all still seem good.

So ... the trick for the moment is "retain" in viewDidLoad, and a release in dealloc - this way the viewcontroller is "stuck" with the view until the time it needs to be released.

I'll test some more, and write about the results


I don't think any of these ideas work. I tried overriding [didReceiveMemoryWarning], and that worked for some phones, but found one phone unloaded the view BEFORE that method was even called (must have been in extremely low memory or something). Overriding [setView] produces loads of log warnings so I wouldn't risk that by Apple. Retaining the view will just leak that view - it'll prevent crashes but not really work - the view will replaced next time the controllers UI is loaded.

So really you've just got to plan on your views being unloaded any time they're off-screen, which is not ideal but there you go. The best patterns I've found to work with this are immediate commit so your UI is always up-to-date, or copy-edit-copy, where you copy your model to a temporary instance, populate your views and use immediate commit with that instance, then copy the changes back to your original model when the user hits 'save' or whatever.


Because the accepted solution has problems with viewDidUnload still getting called even though the view was blocked from being cleared, I'm using a different though still fragile approach. The system unloads the view using an unloadViewForced: message to the controller so I'm intercepting that to block the message. This prevents the confused call to viewDidUnload. Here's the code:

@interface UIViewController (Private)
- (void)unloadViewForced:(BOOL)forced;
@end

- (void)unloadViewForced:(BOOL)forced {
    if (!_safeToUnloadView) {
        return;
    }
    [super unloadViewForced:forced];
}

This has obvious problems since it's intercepting an undocumented message in UIViewController.

progrmr posted an answer above which recommends intercepting didReceiveMemoryWarning instead. Based on the stack traces I've seen, intercepting that should also work. I haven't tried that route though because I'm concerned there may be other memory cleanup which would also be blocked (such as causing it to not call child view controllers with the memory warning message).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜