Make NSDocument "edited" when a binded control changes - objective-c

I have an array of NSDictionaries and a NSDictionary iVar (*selectedDictionary) that points to one of the array's objects. *selectedDictionary points to a different object every time the user selects a different row in a NSTableView. Several GUI controls are binded to the selectedDictionary instance (IB).
I just want to make the NSDocument dirty (edited) every time the user alters the above controls. I think using Key Value Observing for ALL the objects in the array and all their kaypaths, is a bit insufficient. Any suggestions?
Thanks

NSDocument's support for marking a document as dirty comes directly from the NSUndoManager. The easiest way to change the document to dirty is to do an implementation of Undo, and this is basically going to mean doing the undo inside of the model class that the document is using (or the subclass of NSDocument if you choose to handle all storage directly in there).
Apple has documentation on this here:
http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/UndoArchitecture/Articles/AppKitUndo.html
Since you indicate you have an array of dictionaries, that's going to make it a bit more work to implement, but once you have nailed that, you'll be in good shape.
Alternatively, if you don't want to go with the freebie support provided by NSDocument and NSUndoManager, you can manually handle undo and use the updateChangeCount: method to modify the internal understanding of whether changes have occurred. This takes some work, and likely is a lot less useful than just setting up undo correctly.
As for the efficiency of observing all the objects in the array, I wouldn't worry about it unless you have profiled it and found it to be inefficient. KVO is really pretty darned efficient and we regularly observe multiple values in every element of arrays without seeing performance problems. You have to observe the array itself in order to handle adds and removes (assuming your arrays have this).
As far as I can tell, though, you have a selectedDictionary which is used to determine the other controls that are shown. In this case, you can use KVO to observe the value of selectedDictionary and when it changes, you can remove the observers from the previous selectedDictionary and add them to the keys in the current selectedDictionary. This is basically what bindings is doing in order to handle the display and setting, anyway.
One other consideration that I've used in the past is referenced in this StackOverflow post:
NSMutableDictionary KVO. If you look at my answer here, I outline a trick for getting notifications when a new key is added or an existing key is deleted. It also has the benefit of giving you a notification when there's any change. It's not always a great solution, but it does save some effort on coding the list of keys to observe.
Beyond that, you'll have to add every key you're expecting to have an effect on the saved state of the document.

Related

Copy-on-write-if-leaked idiom in Objective-C

I have a situation where I must generate a sequence of objects, and pass them back to an application one at a time (think block-based, or fast enumeration).
However, each object is going to be relatively expensive to generate, so I am looking for ways to avoid this cost.
It happens to be the case, that given one object of the sequence, the next one can be efficiently generated by a simple modification of the former. For this reason, it is tempting to "cheat" by only ever creating one object, and then keep passing that same object back to the application, and only carrying out the "cheap" modification "behind the scene" at each step in the sequence.
The problem is, of course, that the application may choose to (and it should be allowed to) store a reference to some, or all of the objects somewhere else. If it does that, the "illusion" of a true sequence of unique objects breaks down.
If Objective-C allows it, a neat way of solving this problem would be to detect when the application actually does store references elsewhere, and whenever that happens, replace the object by a copy of itself, before applying the modification that produce the next element in the sequence.
I don't know what the official name of this idiom is, but we could call it "copy on write if leaked", "copy on write if shared", or simply "copy on write".
My question is then: Does Objective-C, with ARC enabled, allow for such an idiom to be implemented?
And, is it even the right way to solve this kind of problem in Objective-C?
I did notice that with ARC enabled, there is no way I can extract the reference count from an object, nor override the methods that increment and decrement it.
EDIT: I have noticed that there is a copy attribute that can be applied to properties, but I have a hard time figuring it out. Are any of you guys able to explain how it works?
http://clang.llvm.org/docs/AutomaticReferenceCounting.html#property-declarations

Is there a way to tell when KVO starts/ends for a particular value?

I have some values that are computed over collections, and may or may not be displayed (and thus may or may not have an observer) at any given time. I would rather not have to track all the members of the collection if nobody is observing my computed values.
Can I tell if anyone is currently observing a value, and can I tell when they start observing?
I know for a given object foo I can use [foo observationInfo] to get a list of observers with key paths registered with a root at foo, but that doesn't automatically get all paths TO foo (in fact it only gets ones registered to observe foo's self key).
That’s not a good idea from the design point of view. If you really insist on not updating the contents when nobody needs them (which could be a legitimate case, for example if the updates are expensive), you can introduce methods to start/stop the updates:
- (void) beginUpdatingContents;
- (void) endUpdatingContents;
These should be tied to a counter inside the class and if the counter is > 0, you know somebody wants to keep the contents updated. This solution is explicit and therefore better than silent magic with KVO.
If you want more magic, how about overriding addObserver:forKeyPath:options:context: and removeObserver:forKeyPath: and tracking what is still observing you?
The way I've actually done this in the past is by making wrapper objects (I called them bindings) which set up KVO and also register themselves with the target. So, a user would call MyBinding *binding = [targetObject bindKeyPath:#"foo" ...] and then later [binding detach]. You then have the binding use KVO under the hood and keep a list of themselves so you know when it's empty.

Is it ok to re-sort an array each time you need to access an element at a specific index?

I have a TableViewController that displays a list of elements CoreData relationship. In my cellForRowAtIndexPath:, I am getting a sortedArray from the set and then accessing the element at indexPath.row. Then in didSelectRowAtIndexPath:, I am doing the same thing.
Is this considered a bad practice? I see a theoretical danger in that if my sorting method returns a differently sorted array on a subsequent call, I could end up interacting with object B, even though the user clicked on the cell that showed object A. It shouldn't be possible for my sorting to return a differently sorted array, but I still feel that it is technically risky.
The only other way I know of to do this is to have my tableViewController keep it's own NSArray member, and when it first loads, populate the array by sorting the set. The only problem with this is that I would then have to separately maintain both the set and the array; modifying, inserting, or deleting objects from both anytime the user changes something. Is that considered the "correct" way to display CoreData elements in a table?
I would definitely go with option B. Even though you would be maintaining your model data, and the array in your controller, this should not be too difficult. adding / deleting items from the controller would require an update to the model. Not only do you get around your problem of risking data duplication, but you would see a big gain in performance as you aren't acquiring a newly sorted array each time you call for it.
Define "okay". It's likely not as efficient if you're sorting the same keys he same way every time. On the other hand, cycles are cheap; if you're not worried about performance and this is the clearest way, then go for it.
What you seem to be asking about "sorted the same way every time" is what's called a "stable sort". Ask yourself this: if the order of something other than the key(s) used, then why aren't you sorting by those too?

How do I observe the creation/destruction of an object instance?

I am smitten by KVC/KVO. Super powerful. There is one problem though. I'm trying to be true the the MVC etho but I see no way to use an observation pattern to monitor the allocation or deallocation of an Objective-C class instance.
This is actually important as I have a model with fine-grained internal messaging that I want to observe from a controller (or delegate). The stumbling block for me is I don't see how, external to the model, I can remove an observer for a sub-component that is about to be deallocated without the controller knowing about the internal logic of the model which would compromise encapsulation.
Can someone suggest an approach for this scenario.
Thanks,
Doug
Doug - there really isn't enough information in your description to know what it is you are doing and how to best (or if it is appropriate at all) apply KVO to the problem.
KVO is all about observing properties on objects. You typically shouldn't care when they are created or destroyed except insofar as you must stop observing them before they are destroyed.
You should instead start and stop observing objects when those objects become interesting to you. Consider a graphics drawing package where a document has an ordered array of shapes, and you are interested in observing the backgroundColor property of each shape.
We wouldn't try to observe the instantiation and deallocation of the Shape instance, but instead we observe the "shapes" property on the document. Through that observer, we can determine when a shape is added to, or removed from, the document. When a shape is added to the document, we start observing it. When it is removed from the document, we stop observing it. (Note that it may be removed from the document but not deallocated, if it is on the undo stack, etc.)
In the object graph for your model, to use KVO you'll want to add and remove the objects from your object graph in a KVO compliant way so you can observe the relationship mutations, and in that observer, start and stop property observers on the related objects.
I think you'll have to post the notifications yourself, unless you use something like CoreData. If you're using CoreData, NSManagedObject (the root class of all stored CoreData objects) has an -awakeFromInsert method that gets called after the object has been created and inserted into the ManagedObjectContext.
As for destruction, you could probably just post a notification right as you enter the -dealloc method.
I'm not sure exactly what you're trying to achieve, so a little more explanation would be good.
If you just want to remove an observer before the observed object gets deallocated, then don't worry, because KVO will handle it. Even if you're using notifications it won't cause a problem, you just won't receive any notifications from the object.
If you're trying to observe multiple objects (e.g. an array of Widgets), and would like to know when an object is added or deleted, KVO can handle that too. You just have to make the array a key on your model object, and observe it with KVO. You also have to modify the array in a KVO compliant way (e.g. mutableArrayForKey:, or use your own willChangeValueForKey and didChangeValueForKey).

Adding item to NSPasteboard using "setData"?

Im learning how to do drag and drop to allow ordering of items in an NSOutlineView. (Its my first time using NSPasteboard).
From the slightly related examples I found online, it seems you use:
[pasteboard setData: data forType: #"myapptype"];
What should be put into the NSData when you are doing a move. The samples put an archived version of the object as the data.
The object attributes could possibly change while the item is being dragged, so doest it make sense that instead I stuff a pointer to the object in there?
No, you generally don't want to stuff a pointer to an object onto the pasteboard.
The pasteboard has to have (basically) one of two types of things on it: data, or a promise to provide the data when asked for it. The pasteboard's lifetime is potentially (and likely) far longer than the lifetime of your application. So imagine this scenario:
User copies some data. You put a pointer to your object on the drag.
User quits your program. Your object is deallocated.
User starts up another instance of your program and hits paste.
Likely outcome: you crash when the second instance of your program dereferences an invalid pointer.
What data actually needs to go on the pasteboard can be very specific to your program, but you generally speaking don't want to put 'live' data on there. It's in your best interests to archive. You may find NSCoder useful for doing simple archiving.
I will carve an exception here and say one could conceive of doing pointer stuff if it was only ever done for drag and drop (since I don't think you can do the drag without the program still running) and if you cleared the drag pasteboard when you quit the program. But it's not a best practice, and I would say that if you're not very experienced and very aware of the pitfalls, it's a risky technique.