IBOutlet is somehow a NSZombie? - objective-c

This is an OS X application. I am designing a custom sheet. Within my XIB file I have the sheet's window and a subclass of NSViewController which is responsible for controlling the views within the window/sheet. The owner of the XIB is another controller class.
I placed an NSTextField into the window's content view. In my NSViewController I created an IBOutlet declaration for the NSTextField and ensured the outlet was properly connected in the XIB.
I overrode -[NSViewController setRepresentedObject:] and within that method I am looking at the representedObject and depending on it's properties I am either removing the NSTextField from it's superview or I'm adding it back into the superview.
The first time I display my sheet with a representedObject that dictates the textfield should be removed from the superview. This works just fine.
The second time I display my sheet with a representedObject that dictates the textfield should be added back to the superview my application crashes with EXC_BAD_ACCESS when calling -[NSView addSubview:].
Running Instruments shows that the NSTextField was a Zombie at the time I tried to add it back to the superview. Instruments also indicates that every call to retain/release/autorelease was performed by either AppKit or Foundation - so at no point does any of my code increase or decrease the retain count. In fact the only two lines that reference the IBOutlet in my code are a call to -[NSView removeFromSuperview] and -[NSView addSubview:].
Am I doing something wrong or is this a bug in OS X? An IBOutlet should never be deallocated unless the XIB is unloaded.

Well naturally I figured it out like 2 minutes after I posted my question.
There are actually 2 textfields in my XIB both of which have identical behavior as described in the question. Only one of the textfields was turning into a NSZombie which made things more confusing.
Well when I was designing the XIB I laid down one NSTextField, configured it, and then copied it by holding Option while dragging the NSTextField. I did ensure the outlets were setup properly for the copied NSTextField, but for some reason the objects remained the same.
The solution was to delete the offending NSTextField and then drop a fresh one from the palette and configure it that way.

Related

How to properly set up NSPopover with separate viewController

I have created an extremely simple test program. It has one button. Clicking the button brings up an NSPopover with a label. That label is on a separate xib file with its own NSViewController.
The Goal is to, when on the main screen, when I click the button, a popover will show the xib file of the viewcontroller. And the label of the xib file should have it's text set to "It works".
Well.. It works, but only on after the second loading of the popover. On the first click of the button, the label still has its old default value. But from the 2nd click and onwards, "It works". Does any one have an idea what can be causing this issue? Its only about 5 lines of code The code can be seen on this repository --> https://github.com/patchthecode/testtest
Call [mainScreenPopoverViewController view]; in - (void)windowDidLoad method. This will load your view into memory.
Before call [mainScreenPopoverViewController view]; (textfield address is 0x0)
You should not use strong property for all IBOutlet.
#property (nonatomic, strong) IBOutlet NSTextField *textField;
Take a look at Resource Programming Guide
From a practical perspective, in iOS and OS X outlets should be
defined as declared properties. Outlets should generally be weak,
except for those from File’s Owner to top-level objects in a nib file
(or, in iOS, a storyboard scene) which should be strong. Outlets that
you create should therefore typically be weak, because:
Outlets that you create to subviews of a view controller’s view or a
window controller’s window, for example, are arbitrary references
between objects that do not imply ownership.
The strong outlets are frequently specified by framework classes (for example,
UIViewController’s view outlet, or NSWindowController’s window
outlet).

Make label content persistent through the changes of storyboard scenes

I have a storyboard with three scenes, all of which contain the same UILabel. It is connected to an IBOutlet, IBOutlet UILabel *mainLabel; in my ViewController.h and everything works until the scene is changed, when it resets as if the app was killed from the background and launched again.
The UILabel should simply not reset when the scene is changed and keep the value it had prior to the change instead of resetting to the default value.
The changes between scenes are done solely through the Storyboard as modal segues.
I would provide more relevant code but I can't find anything that could affect this.
How would You fix this?
is the viewDidUnload called?
in case of memory needs a view not on screen can be unloaded, in that case it gets loaded again from the nib the next time it is needed, restoring to default values.
the viewController is not, so you can store the label content in an ivar (NSString?) and on viewDidLoad you can restore then the state of your label.

UITextView: Must I always resignFirstResponder?

Must I always resignFirstResponder for a UITextView? Or, will this happen automatically when its view controller disappears?
I'm asking because I'm having an issue similar to iPhone Objective-C: Keyboard won't hide with resignFirstResponder, sometimes, where the keyboard stays up even when the nav controller pushes and pops other view controllers. The keyboard works, and when I hit done, it unfocuses the UITextView (i.e., the cursor disappears), but the keyboard stays up.
I never found out why this is happening, but maybe it's due to not doing resignFirstResponder before pushing another view controller, but I thought it was optional?
At a total guess, the UITextView has a reference to the view controller (as its delegate) but does not retain it. When you go to the next screen, the controller is dealloced and then the UITextView (which has perhaps been retained by something else) tries to call back to the dealloced controller and crashes. When you call resignFirstResponder, you reverse the order this happens, and therefore no crash.
The way round this to add a textView.delegate = nil call in your view controller's dealloc method - obviously put it before you release the text view.
The contract between a UITextView and it's delegate says that the delegate will send -resignFirstResponder when the text view is done editing. This informs the framework that the view is done editing, fires the events relating to that (willEndEditing and didEndEditing), and allows other parts of the responder hierarchy to react accordingly. Failing to do so might work, but it's not following the contract (that's all a protocol is) it agreed to.
I don't think you have to because the Xcode Sample UICatalog UITextField doesn't call resignFirstResponder before the TextViewController is popped.
The reason the keyboard got stuck for me is that I was having the same view controller present two view controllers modally at the same time, one after the other. UIKit didn't like that.
Calling resignFirstResponder makes sure that the text property contains the actual text shown in the control.
Depending on the state this is not always necessary, but if your controls have resigned first responder, you know that you're working with valid data.

Cocoa Application Template - MainMenu.xib - Main Window

When you create a new application in Xcode, it automatically creates a AppDelegate and a MainMenu.xib. The latter also contains the application main window, which is linked to the AppDelegate as an IBOutlet.
What I tried to do is, use a MainWindow from a different xib-file. However, there's absolutely nothing I can do to prevent Cocoa from showing the first window it created in the first place, even if I remove the IBOutlet link and comment it out in the source file and what not...
Hope someone can explain this, as it has been bugging me for a while now...
Whether or not a window in a XIB is shown at launch is not controlled by an explicit code, but instead controlled by the state of the window "freeze-dried" in the XIB.
More concretely, in an inspector of an NSWindow in the Interface Builder, you have the option called Visible at launch under the heading Behavior.
When the Cocoa system loads a nib and encounters a window with this bit on, it just shows it on the screen. It's independent of whether you have IBOutlet or not. You see, it's also the Cocoa system which sets UI objects to IBOutlets when it loads a nib... it can do whatever it wants.

uitextfield delegate must be file's owner?

I've seen a few references (eg here) in response to folks having trouble getting the keyboard to dismiss in iPhone that say "double check that the delegate is attached to file's owner.
Is this necessarily true? Or just standard practice? Can't I have other objects in my nib, such as a subclass of UIViewController, and make connections to those as I like? I'd hate to have to route everything into the object that happens to be file owner.
That said, I'm having a difficult time getting the keyboard to disappear. I know it's connected to the delegate, because I can set break points and step through the code. I can see the [theTextField resignFirstResponder] get called (and return true), but the keyboard still won't go away.
Any other suggestions?
All of the controls in a particular view are intended to talk to the View Controller that owns the nib file. Even if you have, say, a UISlider that changes the value of a UITextField, this will be handled by a method in your UIViewController subclass that gets fired when the slider's value changes and updates the text in the text field. So 9 times out of 10 your UIViewController will be the nib file's owner.
Typically the text field delegate method you want to define is textFieldShouldReturn, calling resignFirstResponder on the text field, which it sounds like you've done.
Make sure that your outlet for theTextField is connected as well. It can be nil and the runtime will treat [nil resignFirstResponder] as a noop, not as an error.