NSLevelIndicator Not Updating - cocoa

I've got an application with an NSLevelIndicator Object on it that's refusing to update.
I have a timer that's shoved off during init and updates the value of the NSLevelIndicator using its setIntValue method. Whilst the code executes without any exceptions, the NSLevelIndicator never visually updates. I have some other labels on the window that are updating through this timer, so I know that it is executing.
I've tried using all of the setTypeValue methods (String, straight value and double with appropriate variables being assigned in each). I even tried linking the "setStringValue" action through interface builder from the NSLevelIndicator to a label representation on the window to no avail. It still sits at its initial value (0).
I noticed that setIntValue (and all the other setTypeValue methods) are undocumented in Apple's documentation for NSLevelIndicator - so I'm wondering if I'm approaching this wrong.
Does anyone have any clue what the proper way to set an NSLevelIndicator's value from code is?

setIntValue should work, so it sounds like your IBOutlet for the NSLevelIndicator isn't set properly - most likely its value is nil.
This is probably due to your outlet not being connected in IB, as Johan Kool suggested.
One thing worth mentioning, however, is that IBOutlets don't yet have a valid value at the time your initializer is called - they're hooked up after the initializer returns and shouldn't be referenced until your instance receives the awakeFromNib message.
You mentioned your timer is set up from your initializer - if you happen to be passing your instance's NSLevelIndicator pointer to the timer as its userInfo parameter, the userInfo will have the wrong value (nil) since it isn't yet initialized when the timer is created.
Regardless of whether you use userInfo this way, anything that depends on IBOutlet values should be set up from within awakeFromNib rather than init.

Related

Cocoa bindings - getting "deallocated while key value observers were still registered" warnings

I have been using Cocoa bindings in my NSTableCellView inside my NSOutlineView, but supplying my own data into the outlineView objectValueForTableColumn:byItem: method (i.e. not using an NSArrayController for the content). It works fine, till I delete a row from the table, where I get the managed object and delete it from the context, and in the NSManagedObjectContextWillSaveNotification observer, I update my data model and remove the row from the outlineView. But when I do this, I get this warning:
An instance 0x10d922890 of class Log_Log_ was deallocated while key
value observers were still registered with it. Observation info was
leaked, and may even become mistakenly attached to some other object.
Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger.
Here's the current observation info ....
Setting the breakpoint doesn't really help identify the problem. I know something is observing the properties on Log (the tableCellView that is doing the binding) but how do I clean it up when the row is removed from the outline view? I override Log's didTurnIntoFault and I do get a non-nil value in self.observationInfo, but I don't think that can help identify what is the observing object that needs to be removed as an observer at this point.
I'm not sure why this is happening, and what is the best way to debug this to find the offending object that is the 'bindings' observer.
NSTableCellViews are saved for reuse, so your zombie-esque cellView is holding onto the objectValue. When you delete the row, you might want to nil out the objectValue in the cellviews.
Might make sense to use the NSTableViewDelegate didRemoveRowView:forRow for this.

NSArrayController returns null

I have an NSArrayController bound to CoreData in my application. It is also bound to a TableView that displays the data. Two buttons are bound to the ArrayController that add and remove lines. All of this is working as expected. I can add, edit, save, and remove CoreData Entries.
There is a section of my app that is to accept drag and drop operations from files (working). It takes the data from the files, looks for various information, and is to insert this information into the Core Data database via the NSArray Controller.
I have added the class handling the parsing/adding of the file to the database as an object in IB. I created an IBOutlet for the array controller in the class, and bound the controller to the class' referencing outlet.
If I add a button to the interface to directly call the method that adds a custom record to the database, everything works. If the method is called via the drag and drop operation, nothing works, even logging a simple [arrayController className] returns null (though returns NSArrayController as expected when the method is called from the button click).
The only difference I can see is that when accessed through the button click, the method is called directly, while the other way passes through my drag and drop class before loading the parsing class, but I'm completely stuck on how to remedy this situation. I'll be happy to provide code, just not sure which code you'll need.
Any help is appreciated. Thanks!
==================
UPDATE
turns out I was connecting the IBOutlet to a class (a subclass of a view) object in IB instead of to the view itself handling the drops. Connecting these up made things work. Well, not work, I have other issues to iron out now, but the Array controller is now instantiated.
Moved from comment to answer: The array controller you are trying to add stuff is not instantiated. I assume you are not referring to your original NSArrayControllerinstance but maybe a new created one? Probably a problem of communication between your class instances.
Debugging this should be straightforward ... using the debugger. Set a few breakpoints (one at each action the button(s) call, and one at each point where your class instances are meant to talk to each other (your importer and your main controller)). Run, test, step through the code when the debugger breaks at each breakpoint.
My guess: An outlet is not hooked up (is nil) in IB or is not yet reconnected at runtime (see -awakeFromNib and make sure you're not trying to touch an outlet or action that hasn't been fully reconnected from the nib at runtime by the time you're trying to use it).
Something’s not hooked up right, BUT you don’t want to do it this way anyways. There’s no advantage to inserting via an NSArrayController. Just create new objects with NSEntityDescriptions:
+ (id)insertNewObjectForEntityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context;
And you’re done. If your NSArrayController is hooked up correctly it’ll auto-fetch the new objects at the end of the event so the user will see them “immediately.”

why delegates should be unsafe_unretained and not weak?

I added ARC to an app I'm working on. Unfortunately, it crashes. I found that the automatic script which updates all apps to ARC gave __unsafe_unretained qualifier to all id< protocolName> type.
Why isn't it a weak type? I have deployed the app and all its sub-projects to iOS 5, and therefore I do have weak qualifiers.
My main problem is if I declare those delegates as strong, I'll have a retain-cycle. If I do not, the next time I call them they will be zombies. I checked and before my app crash, the delegate is NSZombie.
What is the cause of this crash and how can it be prevented?
The qualifiers __unsafe_unretained and week have quite a few things in common. They both won't increase the retain count for example. If for example a view controller holds an __unsafe_unretained IBOutlet to a UIView and you remove that very UIView from the view hierarchy, then you (given that you don't retain the view anywhere else) will decrease the retain count and most likely dealloc the UIView. The pointer however will still point to that location and is left dangling. Not nice but also not problematic if you know what happened. Weak properties help you avoiding dangling pointers by nullifying the property when the object gets to a retain count of 0.
Now, if your app crashes or the properties show up as zombies, then they are being released - by whichever class though.
One statement that is not entirely correct is that if you retain the property instead, you'll create a retain cycle. There is the possibility of creating retain cycles though but it really depends on your implementation, not just the property declaration. When you retain an object, you take ownership and until you're done with that object, prevent it from being deallocated by increasing its retain count. If your delegate gets released already while you hold a weak pointer, you won't prevent it from being released. I am assuming you deal with modal view controllers here - UIPopoverController to be precise (just a guess).
You should use instruments and look at the lifecycle of your object and see who retains/releases it. It could be helpful to know. Otherwise, you could paste some code and maybe there will be a nice person here to help you find the issue.
cheers
Ronny
Took some time but i solved it:
I deployed the .xcodeproj projects to iOS 5, but the targets were left in iOS 4.3 deployment. When i fixed it (it's in the 'build settings' for each target) - i could change all '__unsafe_unretained' to '__weak', and all 'unsafe_unretained' to 'weak'.
To avoid retain cycle those delegates should be weak, and they won't be zombies anymore (because they are weak and not unsafe_unretained), and the app won't crash anymore.
If i was still using iOS4.3-, and there isn't unsafe_unretained qualifer, i should only assign nil to those delegates after i don't need them anymore.

Unpredictable EXC_BAD_ACCESS and objc_msgSend_vtable5

I was testing my application and suddenly it had EXC_BAD_ACCESS. Now this has become a semi-regular thing, happening on some builds and not others. It also spits out lots of errors in the debugger such as objc_msgSend_vtable5 What could cause such a weird issue like this?
Looks like it was my fault. To fix it all I needed to do was call retain.
I've recently had just this same error. The short version: it occurred because I had an object that had a delegate that had been released. Make sure you have control over the life cycle of any delegates you assign, be it via Interface Builder or otherwise.
The long version: the error occurs when the Objective-C runtime tries to send a message to an object that's been ultimately released and deallocated. This is different to sending a message to a nil object (a perfectly valid Objective-C operation). In these cases, the runtime knows that the object is nil and responds accordingly. Rather, in this case, Objective-C thinks it has a real, valid object to communicate with and sends a message to an address that contains no object.
In my case, I had an object of type NSTextView which was connected up to a delegate by Interface Builder. When a view or view controller is constructed with a NIB/XIB file, Cocoa hooks up all the child views as instructed, including hooking up any delegates that you might have.
My error was that I had created a subclass of NSView, made it an NSTextViewDelegate and then told Interface Builder to hook up a child NSTextView to that parent view. However, during the various operations I was conducting on the owning NSViewController, I had told Cocoa to remove the NSView from the view hierarchy and reinsert it later.
AppKit specifically tells you not to retain any IBOutlets you might have in your classes, but rather to keep track of these by simple assignment. This is because AppKit handles the memory management of these for you. Little did I know that by asking the view to leave the view hierarchy for a while, I had trigged AppKit to clean up that NSView. Since only AppKit had any retains on the view object itself, [pfft], off it went into the ether. Deallocated.
Since I had no direct control over the NSTextView's delegate, the NSTextView now pointed to a delegate that had been released, even though it had a valid parent view. The next time it tried to send a message to its delegate, the Objective-C runtime fell over.
So, make sure that the delegate is one that you own, or at least know will never get released at an inopportune moment.

How to get notifications of NSView isHidden changes?

I am building a Cocoa desktop application. I want to know when a NSView's isHidden status has changed. So far using target/action doesn't help, and I can't find anything in NSNotification for this task. I would like to avoid overriding the setHidden method, because then I'll have to override all the NSView derived class that I am using.
UPDATE: I ended up using KVO. The path for "isHidden" is "hidden", probably because the setter is "setHidden".
You could use Key-Value Observing to observe the isHidden property of the NSView(s). When you receive a change notification from one of these views, you can check if it or one of its superviews is hidden with -isHiddenOrHasHiddenAncestor.
A word of warning: getting Key-Value Observing right is slightly tricky. I would highly recommend reading this post by Michael Ash, or using the -[NSObject gtm_addObserver:forKeyPath:selector:userInfo:options] method from the NSObject+KeyValueObserving category from the Google Toolbox for Mac.
More generally, one can override viewWillMoveToWindow: or the other related methods in NSView to tell when a view will actually be showing (i.e. it's window is in the window display list AND the view is not hidden). Thus the dependency on KVO for the 'hidden' key used above is removed, which only works if setIsHidden has been called on that view. In the override, 'window' (or [self window]) will indicate whether the view is being put into a visible view hierarchy (window is non-nil) or being taken out of it (window is nil).
I use it for example to start/stop a timer to update a control from online data periodically - when I only want to update while the control is visible.
Could you override the setter method for the hidden property so that it will trigger some custom notification within your application?

Resources