initWithNibName method in storyboard - xcode

I am following Facebook's tutorial to post to a user's wall: http://developers.facebook.com/docs/howtos/publish-to-feed-ios-sdk/
and although it is made with a .xib project in mind, I have been able to do everything just fine with my storyboard project so far. However, I have come to the point where I need to put some code into the initWithNibName:bundle: method, but I cannot do this because I am using storyboard. And, since it's hard to tell when exactly the method would be called, I can't just write a method named initWithNibName:bundle: and call it from some other method. If anyone has any idea of how to solve my issue, please say so. Thanks.

I just ran into this problem myself. Storyboards appear to not use initWithNibName:bundle:, but either initWithCoder:(NSCoder *)aDecoder or initWithStyle:(UITableViewStyle)style.
The default implementations of the two methods look like:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
// Custom initialization
}
return self;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
I've yet to use the initWithStyle version myself, but I'd imagine it's called for a UITableViewController. If in doubt, you could simply add both methods to the file, along with an NSLog() call that prints the method's name (or any other unique string). You can then run it in the simulator to see which is called, and delete the other.
I'd strongly recommend against calling initWithNibName:bundle: yourself from any of the other init methods. Better to just move the code to the correct method.

Related

CGLayerRelease crashes

I have a Cocoa app with ARC enabled. I am using OS 10.8 and Xcode 4.4. The app sporadically crashes upon calls to CGLayerRelease, dumping messages like the following to console:
error for object 0x10a2d2000: entry for pointer being freed from death-row vanished
If I comment out the calls to CGLayerRelease the crashing stops. Do I need to call CGLayerRelease in ARC enabled projects? The documentation isn't very clear about this.
It is hard to post code for this because it is spread throughout a large file for a class that inherits from NSView. The class has a data member CGLayerRef layer;. Then inside the drawRect function:
if (layer == nil) {
layer = CGLayerCreateWithContext(context, self.frame.size, NULL);
// draw some stuff into CGLayerGetContext(layer)
}
CGContextDrawLayerInRect(context, self.bounds, layer);
The class is also a delegate of its window and has the following function
- (void)windowDidEndLiveResize:(NSNotification *)notification {
if (layer) {
//CGLayerRelease(layer);
layer = nil;
}
[self setNeedsDisplay:YES];
}
Also inside a property setter
- (void)setPitch:(NSArray *)aPitch {
pitch = aPitch;
if (layer) {
//CGLayerRelease(layer);
layer = nil;
}
}
Now if I uncomment the calls to CGLayerRelease then I sporadically get the crash mentioned above. I stress "sporadically" because it does not always happen.
Yes, you need to call CGLayerRelease() in ARC. The above assignments look correct, but I suspect you may have others. You're directly accessing your ivars, which is the #1 cause of these kinds of crashes. Do not access your own ivars except in init and dealloc. Create an accessor for setLayer: that correctly releases the old one and creates a new one and always use that.
It's very possible that your CGLayer code is fine, and that it's something else like a CGPath that you're adding to the CGLayer. Move all your ivars to accessors. Otherwise you will be chasing these kinds of bugs forever.

initWithNibName either called twice or wrong xib loaded

I'm programming a Cocoa application and want the application to work as a kind of Wizard. So in the main window I have a Custom View that interacts with the user and changes from a sign in to a device activation screen as they step through the stages of the wizard. I have currently overridden the WizardViewController's awakeFromNib method:
- (void)awakeFromNib{
//If no redirect request save, add first view: ID Login
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *tokenRequest = [defaults objectForKey:#"redirectRequestToken"];
if (!tokenRequest){
SignInWithIDViewController *signInViewController = [[SignInWithIDViewController alloc] initWithNibName:#"SignInWithIDViewController" bundle:nil];
[wizardView addSubview:[signInViewController view]];
} else {
NSLog(#"Have already logged in.");
}
}
As is, initWithNibName in SignInIDViewController gets called twice, once explicitly by me, and again when the view is loaded (presumably through loadView). However, if I simply call init then initWithNib name is only called once, but the wrong xib file is loaded (of the DeviceActivationViewController class). I can't seem to figure out what I'm doing wrong, because the signInViewController should not be init twice, but I need the proper xib file in IB to display.
The only other method I have in this class currently that is not a user interface IBAction is the generated initWithNibName method plus an added NSLog statement.
I think that creating the objects in IB (the blue cubes), and instantiating them in code is the problem. If you've created objects for them in IB, then they will be instantiated in awakeFromNib, you shouldn't also call alloc init on them in code -- that will create a new instance.
I don't have a lot of experience with using view controllers in OSX, but it seems that you can't connect IBActions to the view controller (as file's owner). The way I made it work, was to subclass the custom view (that's created for you when you add a view controller), change the class of that view to your new subclass, and put the action methods in that class. It seems like this should be something that would be handled by the view controller, but I think it not working has something to do with the view controller not being in the responder chain in OSX (whereas I think it is in iOS).
After Edit: After a detour into memory management problems, I think I found the best way to do this. You can, and probably should (to comply with Apple's MVC paradigm) put the button methods in the view controller class rather than in the view as I said above. You actually can connect the IBActions to the view controller (as File's Owner), you just need to make sure that the view controller is retained when you instantiate it in code. To do this, you need to make signInViewController a property in whatever class you're instantiating the SignInViewController class in, and use "retain" in the property declaration. Then you don't need to (and shouldn't) create any of the blue cubes in IB.

Converting self-releasing objects to ARC

OK, so Apple brought ARC to us, which is great. After refactoring my Application to ARC almost everything works fine and it is a lot easier now to develop and maintain.
There is just one problem I still can't figure out.
My job management program shows different detail information of proposals, orders and so on in their own windows. So I have a special class where WindowControllers gets allocated and initiated with initWithWindowNibName and then the window is shown with showWindow:
DetailWindowController *proposalWindowController = [[DetailWindowController alloc] initWithWindowNibName:#"ThePorposalWindow"];
[proposalWindowController showWindow:nil];
Before ARC the Instance of the WindowController did the release like shown in the documentation:
- (void)windowWillClose:(NSNotification *)notification
{
[self autorelease];
}
But now with ARC this is not possible anymore and what makes it even worse, in my special class where the WindowController is allocated and initiated, the same windowController is released by ARC because there is no pointer to the windowController.
My idea was to copy the windowController into an mutuable array:
[proposalWindowArray addObject:proposalWindowController];
[[proposalWindowArray lastObject] showWindow:nil];
And in the windowControllers delegate method windowWillClose I post a notification to my special class:
- (void)windowWillClose:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"ProposalWindowWillClose" object:[[self window] windowController] userInfo:nil];
}
In my special class I listen to the notification and remove the object from the array:
- (void) proposalWindowWasClosed: (NSNotification *) notification
{
[proposalWindowArray removeObjectIdenticalTo:[notification object]];
}
It works, but I still do not believe that this is the correct way.
Does anybody has the same problem or a tip to make it better?
I'd probably use a delegate approach rather than notifications. Generally it is better to have an external object that keeps track of the open windows. Self-retaining objects, like your old system, break the basic points of object ownership and make it hard to find things (such as "give me a list of open windows"). Non-Singletons that are just "floating" out there often come back to bite you in your architecture (I've had to fix this often enough).
That said, sometimes self-ownership is at least convenient, and at worst not-the-end-of-the-world. So self-own. The only difference is that you need to do it explicitly rather than matching a leak and an over-release (which is what your old code was doing).
Create a private strong property. Assign self to it. That will create a retain loop that will keep you around until you set the property to nil.
I think your alternative approach should be correct, but I don't think you need the second notification. You should be able to do:
- (void)windowWillClose:(NSNotification *)notification
{
[proposalWindowArray removeObjectIdenticalTo:self];
}
Assuming the "proposalWindowArray" is a static NSMutableArray.
Without hacks, there is no elegant way to keep an object retained other than having a strong reference to it in some other object. For example, you could keep a static NSMutableArray/NSMutableSet, add your controller there, and remove it in windowsWillClose:. This will be shorter than posting a notification. To make this reusable, create a WindowControllerRegistry singleton with an array, where you add controllers like this one, and which will automatically listen to NSWindowWillCloseNotification and remove them from its array thus releasing ownership.
As a quick workaround, you could perform retain/autorelease calls from non-ARC file:
my_retain(self);
my_autorelease(self);
// ArcDisabled.mm
void my_retain(id obj) { [obj retain]; }
void my_autorelease(id obj) { [obj autorelease]; }
I had this same issue when I switched to ARC. Your solution works, but you're making it too complicated. You can essentially do what you were doing before by having the window release itself when it closes, but in an ARC compatible manner.
The solution is to simply create a property of your class within the class itself. For your example, in DetailWindowController, you would add the following property:
#property (strong) DetailWindowController *theWindowController;
Then when you create the window with your code above, add one line like so:
DetailWindowController *proposalWindowController = [[DetailWindowController alloc] initWithWindowNibName:#"ThePorposalWindow"];
[preferenceController setTheWindowController:proposalWindowController];
[proposalWindowController showWindow:nil];
Then finally, to have ARC release the window when it is closed like you did with the autorelease pre-ARC, in the DetailWindowController class, simply do:
- (void)windowWillClose:(NSNotification *)notification
{
// Let ARC tear this down and clean it up
[self setTheWindowController:nil];
}

Is -makeWindowControllers the best place to initialize an NSPersistentDocument?

When loading an existing document using NSPersistentDocument, as part of initialization I'd like to prepare some content:
NSFetchRequest *req = [NSFetchRequest fetchRequestWithEntityName:#"DocumentRoot"];
NSArray *results = [self.managedObjectContext executeFetchRequest:req error:NULL];
if (results.count) self._docRoot = [results objectAtIndex:0];
When I put this code in -init, the fetch request doesn't return any results.
I encountered this problem while refactoring the view-controller components from my NSPersistentDocument subclass to a new NSWindowController subclass. I used to handle this initialization in -windowControllerDidLoadNib:, but that isn't called anymore.
If I move the code from -init to -makeWindowControllers I get the results I expect. Is -makeWindowControllers really the right place to prepare content like this?
Based on the responses I've gotten I think I'm doing the right thing, so here's my answer to my own question.
If you're using the Core Data stack provided by NSPersistentDocument, you can not use Core Data in -init.
Instead, you should:
Put the document-initialization code directly in -windowControllerDidLoadNib: – or if you use a custom NSWindowController subclass, in -makeWindowControllers.
You may also abstract the document-initialization code into a helper method with some unique name like -setUpDocument, and call that method from -makeWindowControllers/-windowControllerDidLoadNib: instead.
If you're using a plain NSDocument, or you're setting up the Core Data stack on your own, you can set up the document model in -init.
From this question and your related question about NSArrayControllers, I'm gathering that you're doing something like this:
- (void)makeWindowControllers
{
MyWindowController* wc = [[[MyWindowController alloc] initWithWindowNibName: [self windowNibName]] autorelease];
[self addWindowController: wc];
}
When you do this, -windowControllerDidLoadNib: won't be called, because the NSDocument object isn't the Nib's owner if you init that way. If you look at NSDocument.h you'll see the following comment (see added emphasis):
/* Create the user interface for this document, but don't show it yet. The
default implementation of this method invokes [self windowNibName],
creates a new window controller using the resulting nib name (if it is
not nil), **specifying this document as the nib file's owner**, and then
invokes [self addWindowController:theNewWindowController] to attach it.
You can override this method to use a custom subclass of
NSWindowController or to create more than one window controller right
away. NSDocumentController invokes this method when creating or opening
new documents.
*/
- (void)makeWindowControllers;
If you, instead, do this:
- (void)makeWindowControllers
{
MyWindowController* wc = [[[MyWindowController alloc] initWithWindowNibName: [self windowNibName] owner: self] autorelease];
[self addWindowController: wc];
}
I believe you'll find that -windowControllerDidLoadNib: is called again. That may not help you, if you have a good reason for that Nib's owner to not be the NSDocument, but that's why -windowControllerDidLoadNib: isn't being called, and what you can do to get that behavior back. That's almost certainly a better place to be doing fetches than in init, which likely happens before all the necessary CoreData support stuff is in place. So that's one option.
If the code is not called from init that is because your document is being initialized elsewhere such as initWithContentsOfURL:ofType:error:, initForURL:withContentsOfURL:ofType:error:, initWithType:error: or initWithCoder: makeWindowControllers is not for setting up your data. Try implementing all of the above initializers and log to see which is getting called.

Placement of [super methodName];

I've seen this done both ways, with the [super methodName]; as the first line in a method and as the last line. Is there a best way to do this?
For example:
- (void)viewDidLoad {
[super viewDidLoad];
self.intX = 1;
}
- (void)viewDidLoad {
self.intX = 1;
[super viewDidLoad];
}
- (void)dealloc {
[myControl release];
[super dealloc];
}
In general, when dealing with views you want to take into account the standard behaviour in relation to the changes that your subclass will take.
This is best illustrated with
- (void)drawRect:(NSRect)dirtyRect
Effectively you are drawing your view into the rect but the view itself is doing all of the highlighting and sub view management in there as well.
So, do you need to do something that will occur before or after the super class does it's normal behaviour?
In these cases it is best to test by moving your code before and after the super call. It may also yield some behaviour understanding by not even making the super call to see how it affects what is done.
viewDidLoad is more of an initialiser and typically is more relevant to your subclassed ivars than the actual class itself. If however your subclassed viewDidLoad is affecting the ivars or state of the subclassed view; you likely want to wait until after super so that your changes are't blown away.
In dealloc we call [super methodName]; at last, because before releasing super class you need to clean up child class objects, otherwise they will become orphans.
For example:
Just think about this example. will it make sense to you ?
You will use your dad's money for to get a good education then you will start earning (In your question, get superclass's benifits before you create your own subclass). At your bad time, first you will loose your money and slowly you may drain your dad's money too (In dealloc method, release subclass's object then super class).

Resources