I notice the Mac App template has create the following:
#interface AppDelegate : NSObject
#property (assign) IBOutlet NSWindow *window;
According to the ARC guidelines all top level object should use a strong property but instead this is using an assign property. Would someone explain why?
A window will generally be "owned" by File's Owner, which will usually be your NSApplication instance (for the main nib) or an instance of NSWindowController, not necessarily the app delegate or the window delegate. This is why it wouldn't be appropriate for the reference to be strong inside the delegate class.
In Mac OS/X 10.7, NSWindow (along with several other Cocoa classes) didn't support management via ARC, so declared properties to NSWindow had to be assign rather than strong or weak. See the Transitioning to ARC and Nib Object Life Cycle documents for a more detailed discussion. The basic answer is that you can't use ARC-managed references for objects that override the release and retain methods.
In 10.8, it looks like NSWindow isn't on that list, but Xcode is still generating the assign attribute rather than weak.
Related
I would like to track each time a certain window appears (becomes visible to the user) in a OS X app. Where would be the most adequate place to call the tracker?
windowWillLoad, maybe?
I expected to find something like windowWillAppear but it seems I'm thinking too much iOS.
How about getting notification such as NSWindowDidBecomeMainNotification, By main I guess the one which is top most on screen directly visible by user.
see : Apple Documentation
Yes, one would expect that a window would notify its delegate or its controller with a windowWillAppear or windowDidAppear message, or post a documented notification like NSWindowDidAppearNotification. But alas, none of those exist. I filed a bug report with Apple and was given the advice to use a storyboard and a view controller instead. This is unhelpful in legacy apps that already use a bunch of window controllers and xibs.
You could subclass NSWindow and override orderWindow:relativeTo: to send a notification. Most, but not quite all, of the messages that make a window show itself ultimately go through this method, including orderBack:, orderFront:, makeKeyAndOrderFront:, and -[NSWindowController showWindow:]. But orderFrontRegardless does not go through orderWindow:relativeTo:, so you would also want to override that for completeness.
Another way to be notified is to make a subclass of NSViewController that controls some view that's always visible in the window. The view controller will receive viewWillAppear and viewDidAppear.
If you're subclassing NSWindow or NSViewController already for some other reason, either of these is a reasonable solution.
If you're not subclassing NSWindow already, and don't have an NSViewController subclass for a view that's always visible in the window, then another way is to use Cocoa bindings to connect the window's visible binding to a property one of your objects. For example, I have a custom NSWindowController subclass. I gave it a windowIsVisible property:
#interface MyWindowController ()
#property (nonatomic) BOOL windowIsVisible;
#end
and I implemented the accessors like this:
- (BOOL)windowIsVisible { return self.window.visible; }
- (void)setWindowIsVisible:(BOOL)windowIsVisible {
NSLog(#"window %# became %s", self.window, windowIsVisible ? "visible" : "hidden");
}
and in awakeFromNib, I bind the window's visible binding to the property like this:
- (void)awakeFromNib {
[super awakeFromNib];
[self.window bind:NSVisibleBinding toObject:self withKeyPath:NSStringFromSelector(#selector(windowIsVisible)) options:nil];
}
When the window becomes visible, the setWindowIsVisible: setter is called with an argument of YES. Note that if the whole app is hidden and reappears, the setter is called again, even though it wasn't called with argument NO when the app was hidden. So be careful not to assume the window was previously hidden.
Also, the binding might create a retain cycle, so you should probably unbind it when the window is closed, unless you want to keep the window and controller around. Note that the window does post NSWindowWillCloseNotification when it's closing, so you don't need any special magic to detect that.
From my understanding the delegating class is the one which always inherits from NSResponder class and delegate is only available to classes that inherits from NSResponder. NSMenu inherits directly from NSObject, so then why do i see deletgate in the outlets?
Or is it that i misunderstood the options that you see after ctrl-click on an object in Interface Builder? I see Outlets, Referencing outlets, Received Actions , what does these mean then?
I am using Xcode 4 (10.7.3), if that matters.
I have no idea where you saw this but it's completely and totally incorrect. Delegation is just a pattern, there are no restrictions on the type of objects that can implement a delegate.
In fact, you are encouraged to use the delegation pattern in your own classes.
NSMenu declares its delegate property as an outlet in the header, which is why you see it in Interface Builder. You can do the same in your own code like so:
#interface YourObject : NSObject {}
#property (weak) IBOutlet id delegate;
#end
In Interface Builder, Outlets refers to the outlets that are defined in your class's header and that you can connect to other objects. Referencing Outlets and Referencing Actions connections from other objects to your object.
I switched to Xcode 4 recently and I don't really understand this new way to write accessors.
For example, in the application delegate class that is auto-generated when creating a new project, the window object is not declared in the #interface but just this way:
#property (nonatomic, retain) IBOutlet UIWindow *window;
Then, in the implementation file, we have the #synthesize window=_window;.
And in the functions, we have either self.window OR _window.
For example:
[self.window makeKeyAndVisible]; // in didFinishLaunchingWithOptions function
[_window release]; // in dealloc function
Can you explain me the difference, why there is nothing in the #interface, why we do #synthesize window=_window; instead of #synthesize window; and what is the difference between self.window and _window, I mean when do I have to call one more than the other?
I'm a bit lost, and feel like the new code I doing trying to do the same in not working properly...
Thanks!
"Why is there nothing in the #interface"
The runtime is synthesizing the ivar for you.
"Why do we do #synthesize window=_window;
This means that the window property will use an ivar named _window (by default the ivar name is the name of the property)
"What is the difference between self.window and _window?"
The former is using the window "getter" method (ie, foo = [self window]), and the latter is accessing the ivar directly.
"Why do I have to call one more than the other?"
It is generally considered unsafe to use accessor methods in your dealloc method, which means using the ivar is preferred.
This has nothing to do with Xcode 4. This is Objective-C 2.0 (which Xcode 4 uses by default when creating project templates).
I recommend reading the chapter on properties in The Objective-C Programming Language, that should make things much clearer
And doing things "the old way" will still work. You don't have to change everything overnight, simply remove the auto-created code if you don't like it, until you feel comfortable with the new syntax.
I am new to document-based applications and hence I may have missed something fundamental. I have written a document based application which uses a subclassed NSWindowController for the interface and a subclassed NSDocument for the model. Per the documentation I initialise the windowController in makeWindowControllers and load its xib. In interface builder, the xib has my windowController subclass set as File's Owner. Among the views in the window, I have a subclass of NSOutlineView and the NSOutlineView datasource and delegate are also refenced in the nib and connected to the windowController via IBOutlets.
According to the documentation, I should be able to access the document from the OutlineView datasource via [windowController document]. However, referencing the windowController (via IBOutlet) from the OutlineView datasource gives me the document instead!
This has lead to some rather ugly code in the OutlineView datasoure (which is a subclass of NSObject in the windowController's xib) to get hold of the document, eg:
-(MyDocument *)myDocument {
MyDocument *theDocument = (MyDocument *)myWindowController;
return theDocument;
}
Where the IBOutlet in the header file references myWindowController as:
IBOutlet MyWindowController *myWindowController
In brief - why does an IBOutlet connected to the windowController get me the document directly instead in this situation? The above code works but seems as if it shouldn't.
Edit: clarification
Okay, I worked out the answer to this one - don't accidentally set the File's Owner of the xib to the NSDocument instead of the windowController in another part of your code and forget that you did it! This overrides the File's Owner that you previously set in the xib.
I know some mechanism of outlet connection when loading NIB, but I am not sure. So I'm asking some questions to ensure my knowledge. I assumed these things all true, but It's hard to find mention about these on reference documentation. Please point wrong and right things.
I have an IBOutlet defined like this: (Of course it's not recommended way)
#implementation
{
IBOutlet id var1;
}
#end
NIB loader (alloc | retain) & autorelease all top-level objects. So it will be dealloc on runloop turn ends without additional retain.
Connecting IBOutlets are done with KVC.
KVC uses accessor method primarily.
KVC uses setValue:forKey secondarily. And the IBOutlet will be handled by this method because there's no declared property or access method.
setValue:forKey retains the new value object.
setValue:forKey releases the old value object.
So top-level object connected to the IBOutlet will be retained once. So I have to release it to dealloc. This is why I must release objects connected to IBOutlet on dealloc method.
If the object connected another IBOutlet like the IBOutlet, it should be released once more to be dealloc.