Obj-c initWithCoder seems to crash NavigationController - objective-c

does anyone know why when i add...
-(id) initWithCoder: (NSCoder *) decoder {
OutputDataMutableArray = [[decoder decodeObjectForKey:#"RootViewControllerPostArray"] retain];
return self;
}
to my RootViewController it will not push a new view from a table cell using the didSelectRowAtIndexPath method. This is the line it seems to hang on...
[[self navigationController] pushViewController:postController animated:YES];
i get no error messages, it just seems to skip it. Unless i take the initWithCoder out and it works fine.
any insight would be appriciated.
Chris

You’re not calling the superclass’s implementation of -initWithCoder:.
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if(self)
{
OutputDataMutableArray = [[decoder decodeObjectForKey:#"RootViewControllerPostArray"] retain];
}
return self;
}

Check the log and look for exceptions being thrown causing your code to terminate prematurely. Or set a break on NSException raise.
BTW, you need to be calling [super init] in initWithCoder:

Related

Why does initWithCoder not return instancetype?

It seems that most init methods in Objective-C now tend to return instancetype instead of id. See [UIView initWithFrame:], [UIViewController initWithNibName:bundle:], [NSArray init] and siblings, etc. But initWithCoder uses id. Why is this? Has it just not been updated yet? Or is there a reason it has to be id?
It is not updated yet. You can still code it with instance type.
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
//...
}
return self;
}

Using dealloc method to release

I'm trying to understand memory management to do some better apps, but was stopped at one point :
I use some UIButtons. So I alloc them, work with them etc. But i need to release them at one moment.
So I implement deallocmethod for all the object which are usefull all the time the UIViewController is on screen, and which need to be released when it desappeard. So in my UIViewController I implement :
-(void)dealloc
{
NSLog(#"Dealloc call");
[myButton release];
.... //Some objects release
[super dealloc];
}
But i never see the Dealloc call printed, so I think that it doesn't passed by the dealloc method when the UIViewController desappeard.
So, how does it work ? / What is false ?
Thanks !
EDIT : method to change of viewController :
-(void)myMethod
{
if (!nextviewcontroller)
{
nextviewcontroller = [[NextViewController alloc]init];
}
UIView *nextView = nextviewcontroller.view;
UIView *actualView = actualviewcontroller.view;
[actualviewcontroller viewWillAppear:NO];
[nextviewcontroller viewWillDisappear:NO];
[actualView removeFromSuperview];
[self.view addSubview:nextView];
[actualviewcontroller viewDidAppear:NO];
[nextviewcontroller viewDidDisappear:NO];
}

Not calling [super dealloc] inside a static UIViewController subclass

In my app I have a UIViewController subclass (VC for short) that I only use in one place in the entire app. In that place, I have been creating and pushing it like this:
MyViewController* VC = [MyViewController new];
[self.navigationController pushViewController:VC animated:YES];
[VC release];
but I was thinking that since this is the only place I am using a view controller of this type, I could do something like this so the settings used won't be reset each time the view controller is pushed onto the stack:
static MapsToSendPicker* VC = nil;
if(!VC) {
VC = [MapsToSendPicker new];
}
[self.navigationController pushViewController:VC animated:YES];
[VC release];
The problem with that code is that in VC's dealloc method, I release all of my instance variables and set them to nil, and finally I call [super dealloc]. This deallocates the static view controller, but the test if(!VC) isn't evaluated to true after (this would defeat the purpose of the whole idea if it were; then I'd have to recreate the view controller each time anyway).
My solution is overriding the dealloc method in MyViewController and not calling [super dealloc] at the end. This works, but the compiler raises a warning. How can I get rid of that warning while maintaining the functionality I gain with this design? Thanks!
Edit:
After a quick Google search, I have found this solution:
- (void)dealloc {
if(NO) {
[super dealloc];
}
}
but I would like something a little bit... cleaner. Any thoughts?
Remove the [VC release]; line and add [super dealloc] back. Everything will work properly and dealloc will never get called. Generally you should consider using NSUserDefaults in order to restore the VC properties instead of keeping the controller in memory all the time.

How to write a custom initializer that prevents viewDidLoad being called?

I would like to build a custom init method for a UIViewController, but after digging around on the Internet and specifically in SO I am confused about designated initializers.
I have a subclass of an UIViewController with these two initializers:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if ( self ) {
}
return self;
}
- (id) initWithFilename:(NSString *)aFilename {
self = [self initWithNibName:#"WallpaperDetailsViewController" bundle:nil];
if ( self ) {
self.filename = aFilename;
}
return self;
}
Then I have a viewDidLoad method that customizes the view according to the filename property:
- (void)viewDidLoad {
[super viewDidLoad];
// Create a UIImageView to display the wallpaper
self.wallpaper = [[UIImageView alloc] initWithImage:[UIImage imageNamed:self.filename]];
// ...
}
In another UIViewController I make the following call:
WallpaperDetailsViewController *detailsViewController = [[WallpaperDetailsViewController alloc] initWithFilename:#"foobar.png"];
[[self navigationController] pushViewController:detailsViewController animated:YES];
The result is that viewDidLoad is being called as a consequence of [self initWithNibName:], which does not initialize the UIImageView because self.filename is null.
According to other SO questions and answers, that should be the expected behavior. I am not sure about this because of my own experience in other projects prior to iOS 5. My question is:
How can I ensure that viewDidLoad: is call after initWithFilename: and not between initWithFilename: and initWithNibNameOrNil:bundle:?
If that's not possible, how can I implement an initializer method that receives custom data to create and customize the view?
Thanks!
I have found the problem.
WallpaperDetailsViewController does not inherit directly from UIViewController, but from another custom UIViewController I have implemented.
And what was the problem? That I have initialized a subview in the parent's initWithNibName method, instead of following the lazy-load technique and doing it in viewDidLoad. When WallpaperDetailsViewController was calling its parent initializer it got messy and cause viewDidLoad not to behave properly.
The solution? I moved every subview initialization in the parent class to its viewDidLoad method, and keep my original implementation of WallpaperDetailsViewController intact. Now everything is working as expected
Thanks to #Josh Caswell and #logancautrell
You don't need that empty implementation of initWithNibName:bundle:. Furthermore, it looks like your class here is establishing its designated initializer to be initWithFilename: If that's true, initWithFilename: should be calling the superclass's D.I.:
- (id) initWithFilename:(NSString *)aFilename {
// Call super's designated initializer
self = [super initWithNibName:#"WallpaperDetailsViewController"
bundle:nil];
if ( self ) {
self.filename = aFilename;
}
return self;
}
The rule is that all initializers within a class should call the class's D.I., and the D.I. should itself call the superclass's D.I.
It's not completely clear from what you've posted why loadView: is being called before your initializer has completed. Logancautrell's comment suggesting setting breakpoints in the view loading methods is good.
Why don't you just use a custom setter for the filename property that initializes the UIImage every time the filename is set?
Or, alternately, set the UIImage from the filename property in viewWillAppear: instead of viewDidLoad.
First, it is not recommended that you use dot syntax within your initializer. See the following for some good discussion:
Objective-C Dot Syntax and Init
Second, what you could do is assign the image in your initializer as well. So you could do something along the lines of
- (id) initWithFilename:(NSString *)aFilename {
self = [self initWithNibName:#"WallpaperDetailsViewController" bundle:nil];
if ( self ) {
filename = [aFilename retain];
wallpaper = [[UIImageView alloc] initWithImage:[UIImage imageNamed:aFileName]];
}
return self;
}
This will allow you to get everything setup and in good shape before viewDidLoad is called.
Good Luck!

Why release a property that you've already set to nil?

Here are two methods in a view controller from an Apple tutorial:
- (void)viewDidUnload {
self.eventsArray = nil;
self.locationManager = nil;
self.addButton = nil;
}
- (void)dealloc {
[managedObjectContext release];
[eventsArray release];
[locationManager release];
[addButton release];
[super dealloc];
}
Couldn't the dealloc method be shortened to the following? If not, why not?
- (void)dealloc {
[managedObjectContext release];
[super dealloc];
}
- (void)viewDidUnload is not guaranteed to be called, so you should always release things in dealloc too.
See this question to find out when it's called, and what you should do when it is.
No, because you cannot rely on viewDidUnload being called upon deallocation. viewDidUnload is only called when the view controller receives a memory warning while its view is not on screen. If the view controller gets deallocated, viewDidUnload is not called (AFAIK, I'm not entirely sure).
because it's a good practice to always cleanup your ivars in dealloc. something could go wrong, or you may encounter an execution you do not expect.
Setting eventsArray to nil just means it has no content, but still space for content
Calling [eventsArray release] releases the space the array consumed.