I've just ported my app from Garbage Collection to ARC using the Xcode tool. When my app creates a new window, the window immediately vanishes. Under GC, the window remained visible.
I understand that in ARC, any object that doesn't have a strong pointer to it disappears. I have a strong pointer from my NSDocument-subclass object to the window that belongs to it, but the NSWindow disappears immediately after being created anyway.
Do I need to have a strong pointer to the new NSDocument-subclass object? And if so, what does that pointer belong to?
- (IBAction)importLegacyDocument:(id)sender{
myDocument* theDocument = [[myDocument alloc]init];
NSWindowController* theWindowController;
theWindowController =[[NSWindowController alloc]
initWithWindowNibName:#"myDocument" owner: theDocument];
[theDocument makeWindowControllers];
[theDocument showWindows];
//WINDOW VANISHES IMMEDIATELY AFTER IT HAS BEEN CREATED
}
Thanks very much in advance to all for any info!
Yes you should have a reference to the document. Any objects you create inside a method will be destroyed if there are not being retained. The same goes for your NSWindowController instance in that code as well.
#property (strong, nonatomic) myDocument *theDocument;
#property (strong, nonatomic) NSWindowController *theWindowController
(Your AppDelegate would be a good place to declare those properties)
Then assign the created instances to your properties:
self.theDocument = [[myDocument alloc] init];
self.theWindowController = [[NSWindowController alloc] initWithWindowNibName:#"myDocument" owner:self.theDocument];
And as a side note - The Objective-C convention is to name classes with uppercase letters so myDocument should be MyDocument
Hope this helps!
Yes, and the correct place for that strong reference is the nsdocumentcontroller singleton.
You should create new documents using nsdocumentcontroller's methods rather than alloc/init. This will automatically add new documents to the document controller and remove them when the document is closed.
Related
Apple's resource programming guide (RPG) states "it is better to distribute components across multiple nib files."...
therefore,
i have an associate window nib (Nib 2) that has an nsobjectcontroller that needs to be linked (selection self) to a nsarraycontroller in the main document window nib (Nib 1).
i need to share a common instance (either the nsarraycontroller in nib 1 or nsobjectcontroller in nib2). I can add a custom object in Nib 1. and set the File's Owner to that type of custom object. however, each nib instantiates their own instance.
is there a method of setting which nib an object was instantiated, or declaring an external reference.
i also "Make the File’s Owner the single point-of-contact for anything outside of the nib file" (RPG). Which is a NSWindowController.
Thanks in advance.
You probably want to make the owner of NIB1 responsible for instantiating NIB2. This will allow it to be the owner of both NIBs. In the common case, it might look something like this:
// In the interface...
#property (nonatomic, readwrite, retain) NSArray* nib2TopLevelObjects;
// In the implementation...
- (void)awakeFromNib
{
NSNib* nib2 = [[[NSNib alloc] initWithNibNamed: #"NIB2" bundle: [NSBundle mainBundle]] autorelease];
NSArray* tlo = nil;
[nib2 instantiateWithOwner: self topLevelObjects: &tlo];
self.nib2TopLevelObjects = [tlo retain];
// Do other stuff...
}
- (void)dealloc
{
[_nib2TopLevelObjects release];
[super dealloc];
}
At the end of this, NIB2 will have been instantiated with NIB1's owner as it's owner as well, and NIB2 will have plugged its objects into the shared owner (be sure not to plug things into the same outlet from both NIBs.)
All that said, I'm not sure this is necessarily the right pattern to use here. If these windows are both views upon the same document, you should probably make an NSWindowController subclass for each window and override -[NSDocument makeWindowControllers] to instantiate them. (The NSWindowController will be the "File's Owner" for each NIB.) Having the document NIB's owner be the NSDocument subclass is a "short cut" for simple situations. Once you need multiple windows, NSWindowControllers are the way to go.
Each NSWindowController can get back to the document via -document and the NSDocument subclass can coordinate state between the different NSWindowControllers. This is a cleaner approach, and avoids all the shenanigans with clobbered IBOutlets, etc.
For your specific case, I could see having a property like sharedArrayController on the NSDocument subclass that gets the NSArrayController from NIB1 during -makeWindowControllers, and re-vends it. Then you can then access it from NIB2 by binding to File's Owner > document.sharedArrayController.selection.
I'm trying to get speech recognition working on a MacBook (OS 10.8.2) but I never get any callbacks on the delegate method. Using XCode 4.6 with ARC, here is my simple test code. I do get the "listening" output in the console. The "microphone" appears on screen and if I press the ESC key, then I can see my speech pattern in the display of the microphone, but still no delegate callbacks ever. There must be something basic missing but I haven't found it.
I've looked at numerous SO questions but none solve this. Some talk about calibration in the control panel, but I find nothing there for calibration (maybe that was a previous OS?).
Full project source available in github.
#import "RBListener.h"
#interface RBListener() <NSSpeechRecognizerDelegate>
#property (nonatomic, strong, readonly) NSSpeechRecognizer* recognizer;
#property (nonatomic, strong) NSArray* commands;
#end
#implementation RBListener
#synthesize recognizer = _recognizer;
- (id)init
{
self = [super init];
if (self) {
// initialize
_commands = #[#"hi", #"yes", #"no", #"hello", #"good", #"time"];
_recognizer = [[NSSpeechRecognizer alloc] init];
_recognizer.delegate = self;
_recognizer.commands = _commands;
_recognizer.listensInForegroundOnly = NO;
_recognizer.blocksOtherRecognizers = YES;
[_recognizer startListening];
DLog(#"listening");
}
return self;
}
#pragma mark -
#pragma mark NSSpeechRecognizerDelegate methods
- (void)speechRecognizer:(NSSpeechRecognizer*)sender didRecognizeCommand:(id)command
{
DLog(#"command: %#", command);
}
#end
It's likely your RBListener instance isn't sticking around (or is never even created), so right after everything's set up, there's no RBListener instance to receive the delegate messages.
The easiest thing to do is create an outlet in your XIB and connect it to an RBListener instance. That is, drag a basic "NSObject" (plain cube) into your xib from the library and change its class to "RBListener". This instance can then be referenced via your outlet (once you connect it in IB) AND should stick around in memory.
If you're alloc/initing your RBListener instance programmatically, make sure you're storing it somewhere (like as an instance variable on some other object that sticks around - your app delegate or your NSDocument subclass - whichever is appropriate to your design). If you don't stash it into a property or make it a singleton (another possible approach), ARC will kill it before you get a chance to use it since you did nothing to hold onto it.
I hope this helps.
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.
In the Implementation Overview section of the NSPersistentDocument Core Data Tutorial it says:
…
One issue with creating the new top-level object in the nib file is that when you use bindings an object retains other objects to which it is bound. This means that bindings must be broken to ensure there are no retain cycles when a document is closed. Moreover, since the nib file the new controller owns contains top level objects and the controller’s class does not inherit from NSWindowController, you need to release the top level objects when the window is closed.
Why not just have the controller inherit from NSWindowController? Is there a reason this would not work? Or was this just a matter of style?
As commented below, I did get this to work with an NSWindowController subclass, and it does seem to save quite a bit of code.
Here is my subclass header:
#import <Cocoa/Cocoa.h>
#interface NewAccountSheetController : NSWindowController {
#private
BOOL isValidForInsert;
NSManagedObjectContext * managedObjectContext;
NSObjectController * objectController;
NSObjectController * targetController;
}
#property (setter=setValidForInsert:) BOOL isValidForInsert;
#property (nonatomic, retain) IBOutlet NSManagedObjectContext * managedObjectContext;
#property (nonatomic, retain) IBOutlet NSObjectController * objectController;
#property (nonatomic, retain) IBOutlet NSObjectController * targetController;
- (void)beginSheetForWindow:(NSWindow *)window;
- (IBAction)endSheet:(id)sender;
#end
And here is the implementation in a Pastebin.
I have no good idea how to describe the required bindings, etc. but if you're familiar with the above tutorial they should be straightforward to extrapolate… I think. :-)
In the example, its talking about controlling a sheet instead of a window. A sheet is technically a window component and not a window itself so it can't use a NSWindowController subclass as a controller. A window controller does not know how to handle a window owned by another window.
The text above is just reminding you that although the sheet controller looks very much like a window controller it is not one and that you have to manually handle releasing that is handled automatically by the window controller.
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.