The issue:
I have a UINavigationController as as subview of UIWindow, a rootViewController class and a custom MyViewController class. The following steps will get a Exc_Bad_Access, 100% reproducible.:
[myNaviationController pushViewController:myViewController_1stInstance animated:YES];
[myNaviationController pushViewController:myViewController_2ndInstance animated:YES];
Hit the left back tapBarItem twice (pop out two of the myViewController instances) to show the rootViewController.
After a painful 1/2 day of try and error, I finally figure out the answer but also raise a question.
The Solution: I declared many objects in the .m file as a lazy way of declaring private variables to avoid cluttering the .h file. For instance,
#impoart "MyViewController.h"
NSMutableString*variable1;
#implement ...
-(id)init
{
...
varialbe1=[[NSMutableString alloc] init];
...
}
-(void)dealloc
{
[variable1 release];
}
For some reasons, the iphone OS may loose track of these "lazy private" variables memory allocation when myViewController_1stInstance's view is unloaded (but still in the navigation controller's stacks) after loading the view of myViewController_2ndInstance. The first time to tap the back tapBarItem is ok since myViewController_2ndInstance'view is still loaded. But the 2nd tap on the back tapBarItem gave me hell because it tried to dealloc the 1st instance. It called [variable release] resulted in Exc_Bad_Access because it pointed randomly (loose pointer).
To fix this problem is simple, declare variable1 as a #private in the .h file.
Here is my Question:
I have been using the "lazy private" variables for quite some time without any issues until they are involved in UINavigationController. Is this a bug in iPhone OS? Or there is a fundamental misunderstanding on my part about Objective C?
It might be related to both instances of your view controller using the same statically-allocated variable.
In other words, both myViewController_1stInstance and myViewController_2ndInstance are using the same variable1 location in memory and overwriting one another.
Variables declared inside of the curly braces after your #interface definition have a memory location allocated by the runtime for each instance of the class (every time you call [<ClassName> alloc]. Variables declared in the global scope (that is, outside of any functions or class declarations) are just that: global. That means that the variable can only hold one value per running copy of your application.
There are no truly private variables in Objective-C, but you can hide them from other instances at compile time as described here.
A bit of a late reaction, but I've seen this problem before. Don't push two viewControllers animated at the same time. Push the first one without animation and push the second one with animation. UINavigationController can't handle two animations at the same time.
Related
I am using Xcode 4.3 and need to know the parent view controller of the current view.
I am also using storyboard.
self.parentViewController always returns nil.
I saw different answers to save the parent view controller in AppDelegate class as a property. E.g., To create a variable: UIViewController parentViewController in AppDelegate and in the code write:
appDelegate.parentViewController = self;
I am afraid that it will consume memory - as this is a heavy object.
What is the best approach to know aretnViewController when using story board?
Whether or not an object is "heavy" does not matter as long as you store only a reference to it (in your case in the application delegate). Creating a second object would make a difference, but the line
appDelegate.parentViewController = self;
does not do that, it merely stores a reference to the object.
I know that this does not answer your direct question, but I think you should go ahead with the "store a reference in the app delegate" approach.
Let's say I have a storyboard scene that has a UIViewController in it with its custom class set to ParentViewController. I'd like to create a ChildViewController that does almost everything the same as ParentViewController but customizes just a few behaviors. ParentViewController's init loads the scene from the storyboard.
What I have now is ChildViewController subclassing ParentViewController. When ChildViewController's init calls ParentViewController's init through [super init], the ParentViewController loads the scene which in turn makes my class a ParentViewController even though it was a ChildViewController. After that the ParentViewController's methods get called.
I can get around that by using a strategy pattern, giving the view controller objects that customize the behavior as needed. But the code I'm working in also keys off of the Class types, so I really need a ChildViewController to stay a ChildViewController, or logic down the line does the wrong thing.
Question 1: In this case is it safe to use object_setClass to set the class back to what it was?
Question 2: If I do that do I not need the strategy any more? I assume once the class is set back the proper subclass methods will be called.
I'm just concerned that changing the class when the framework changed it for me in the first place might break something else.
There is a gap in my understanding of blocks and ARC that I would like some help with. I have received a crash report from a tester which originates from an animation completion block. The crash is as follows:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0xf0000010
This is happening inside the following animation completion block (using animateWithDuration:animations:completion:). I am swapping two view controllers using a temporary variable:
{
[current wasMovedOffScreen];
PlayerViewController *temp = current;
current = next;
next = temp;
}
next = temp; is the line on which the crash occurs. The duration of the animation is 0.3 seconds.
This code is executed inside a view controller which acts as a container for two other view controllers, used to simulate the user passing through a list of objects, like a navigation controller, but with a number of customisations that meant a navigation controller was not appropriate for me.
The two child view controllers are called "current" and "next" - as you can guess the current controller displays the current content, and the next one is used to animate in the new item when the user moves through the list.
If I change the block so that temp is declared in the implementation (along with current and next) the crash stops. However it seems unnecessary to me to have to hold a class ivar for something that is by definition local and temporary.
So,
Why is temp apparently released by ARC during this process?
What would be the correct way to implement this? Is there some lifetime qualifier I should add to the block implementation?
Why could I not reproduce the crash on my own device or in the simulator? Both devices were iPhone 4 running the same version of iOS (5.0.1).
I don't think the problem is actually ARC in this instance. Variables (including local variables) are __strong by default, so current should be retained in the first assignment.
http://developer.apple.com/library/mac/#releasenotes/ObjectiveC/RN-TransitioningToARC/_index.html - see section 'Variable Qualifiers'
If you've got a crash you can't reproduce yourself, it's always going to be difficult to fix. Have you tried running the app outside of the debugger? Having the debugger loaded can sometimes change behaviour. Also, try debugging with NSZombieEnabled, or Instruments.app's zombie tool.
And if it really keeps you up, since you're only alternating between two views you don't actually need all this current / next business. Well, you can keep track of current if you want... but here's the idea.
If you have properties:
#property (nonatomic, strong) UIViewController *controller1;
#property (nonatomic, strong) UIViewController *controller2;
#property (nonatomic, weak) UIViewController *currentController;
Then you can have a method:
- (id)swapControllers {
// use the current controller to figure out what the next controller is, so
// you don't have to do the two way swap. do in the completion, if you like.
UIViewController *nextController = ([self.currentController isEqual:self.controller1]) ? self.controller1 : self.controller2;
[UIView animateWithDuration:.25
animations:^{
// TODO: some animations on currentController and nextController
} completion:^(BOOL finished) {
[self.currentController wasMovedOffScreen];
self.currentController = nextController;
}];
}
As for your original issue, ARC knows how to handle blocks pretty well - if you weren't using it, I might ask if you'd forgotten to copy a block declared elsewhere, then stored, then passed in as the completion handler. Since that's not the case, and since you haven't been able to get a repro... I'm guessing that you might be running a beta sdk - there have been some ARC bugs in beta releases of late. If that's so, you might try building with the last stable release, and see if you get better results.
I persist to be a little confused about when to put something in a viewController and when to put it inside an AppDelegate, and if it is ok to reference viewController methods from the AppDelegate (i know you can but that doesn't mean it is encouraged).
Really, I wouldn't be confused if it weren't for all this multi-tasking stuff that seems to complicate the concepts for me. First, if anyone knows of a good, thorough and easy to read overview of how to deal with multitasking, I'd love to know.
Here's the deal: there are things I need my app to do when it loads, whether loading fresh or loading from the background. Stuff like perform a network reachability test, setup the interface based on data received from the internet, and this or that.
One of my main questions relates to how the viewcontroller's view interacts with background states. If the app resumes from the background and the view is immediately present without loading, then I assume it is still in memory and I have verified that viewDidLoad was not called with a basic NSLog. So, is it safe to say that any and all objects retained by my viewcontroller (like the data models and all subviews) are thus still in memory? If not, what's the best practice discovering which objects need to be re-loaded, what are still there, etc?
I think it's safe to assume that the standard memory management rules apply, even in a multi-tasking environment. That means that your controller, and anything you've got a reference to in your controller should still be valid until either:
You explicitly deallocate your controller/objects
Your app terminates
It seems like your assumption is that the system is going to mess with your objects behind your back, which (I hope) can't happen. Those methods are there in the app delegate in case you want to explicitly do anything when those particular events occur.
Regarding the viewDidLoad question, you could implement viewDidUnload or check the isViewLoaded method to make sure your view wasn't unloaded due to a low memory condition or otherwise. More on this in the UIViewController documentation.
One way to approach this problem is with lazily-loaded properties. In your .h file:
#interface YourViewController : NSObject
#property (nonatomic, retain) NSArray *exampleObject;
#end
And in your .m file:
#implementation YourViewController
#synthesize exampleObject = _exampleObject;
- (NSArray *)exampleObject {
// reload only if necessary
if (!_exampleObject) {
_exampleObject = [[NSArray alloc] init];
// do whatever other setup you need to
}
return _exampleObject;
}
#end
I don't know if it's possible for me to include code here that's relevant as my project is so large but are there any typical reasons why NSLog would repeat some warnings and calls to it at occasions where only one call/error is occuring?
As an example, I have a subclass of NSBox that inits an instance of another class on awakeFromNib:
- (void) awakeFromNib {
burbControllerInstance = [[BurbController alloc] init];
if (burbControllerInstance) {
NSLog(#"init ok");
}
}
I get NSLog printing "init ok" twice. I don't see why this subclass would be 'awoken' twice anywhere in my project. This is part of a larger problem where I can't get variables to return anything but nil from the class I'm creating an instance of. I'm wondering if perhaps the double values are something to do with it.
This post could be helpful, i. e. one comment:
Also important: awakeFromNib can be
called multiple times on the
controller if you use the same
controller for several nibs – say,
you’re using the app delegate as the
owner of both the app’s About Box and
preferences dialog. So you’ll need an
extra guard test if you use
awakeFromNib for anything but
initializing the nib objects
Update: Much more interesting could also be this, where the author mentions that awakeFromNib gets called twice. Unfortunately there is no real answer for this particular problem but maybe some basic ideas.
Update #2: Another potential solution from stackoverflow.com: View Controller calls awakeFromNib twice.