How to bind NSTableView to a custom collection class? - objective-c

I know how to bind a NSTableView to an NSArray via NSArrayController.
But what about binding NSTableView to MyCustomCollection? What do the bindings look like? Can I still use NSArrayController and just conform to a protocol in MyCustomCollection? Or should I use a different controller object? What does MyCustomCollection need to conform to?

This is hard to answer completely or specifically without knowing a lot more about your collection class. Almost certainly creating a custom collection class is the wrong thing to do. You should definitely elaborate on the reasons you feel this is necessary and your implementation. Be prepared to consider your reasons aren't good enough to justify jamming a custom collection into an NSArrayController or that it's not even possible to do exactly what you want (at least the way you're trying to do it).
Also note Cocoa collection classes are (I think all) members of a class cluster. This makes "doing it right" a significantly more complex challenge and makes subclassing an existing Cocoa collection class a Bad Idea™ if you have to ask how ("if you have to ask, you're not ready to try it" conceit, albeit justified).
I believe as long as your class responds to the same selectors as does (at least) NSArray (if not NSMutableArray if your class is mutable), including the Key Value Coding collection accessor methods, it should probably work alright. Since your table (or, more likely, its columns) is bound to the array controller, that part should work the same. Since NSArrayController inherits -content / -setContent: from NSObjectController and these methods take an id argument, I believe it likely uses the KVC accessor methods I referenced to get at the collection's members.
But I could be wrong... :-)

Related

Objective-C - Effective Subclassing of Cocoa Class Clusters

I have an object that used to be an NSMutableSet but needed some more stuff attached to it. The obvious (and obviously not supported) thing to do is to subclass NSMutableSet and tack on the two additional properties. Since NSMutableSet, like basically all Cocoa data structures, is a class cluster I cannot subclass it in the usual way, since the super class just throws exceptions. This led me down several paths.
The first path was to create sort of a composite object that declared itself as a subclass of NSMutableSet but really just forwarded the invocations to an internal NSMutableSet. I didn't want to have to implement every method on NSMutableSet, so I thought forwardInvocation: would be a good way to accomplish my mission. Unfortunately, the abstract class of NSMutableSet implements all of the methods on the interface and their implementations throw exceptions, so I was never getting to the point where I could forward an invocation.
The second path was to subclass NSProxy and forward the invocation from there. This solution falls short in that I need to copy the interface of NSMutableSet over unless there's a way to declare "this class implements this interface" that I don't know about (this could very well be the solution).
The third path was to create a category on NSMutableSet and import it just for the class that needs to use it but that falls short since you cannot add non-dynamic properties via a category. That led me to using associated objects in a category. I'm willing to admit that that is the correct solution for this use case, but I wish it weren't since it's kind of clunky. It's doubly clunky since the properties I'm adding are primitive so I'll have to wrap and unwrap them when setting and getting the association (unless there's a way to associate primitives which I'm unfamiliar with).
Essentially, what I would like is something that behaves functionally as a subclass of NSMutableSet (and all class clusters) but cannot figure out the best approach. Thanks!
Trying to subclass Cocoa class clusters will just create an awful lot of hurt. It may seem a good idea, but you will forever run into problems.
Just create an NSObject with an NSMutableSet as the first member object.
Subclassing Cocoa class cluster is kind of discouraged. Not without reasons. Please do not enter this crashy world.
Either of your solutions will work. I've successfully used the first path with NSArray and NSDictionary, so I believe it should work fine for NSMutableSet as well. Just remember that you need to override not only forwardInvocation:, but a few of other methods as well. Please consult Surrogate Objects sections of Apple docs:
Although forwarding mimics inheritance, the NSObject class never confuses the two. Methods like respondsToSelector: and isKindOfClass: look only at the inheritance hierarchy, never at the forwarding chain.
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html
In my case, I've overridden:
conformsToProtocol:
isKindOfClass:
isMemberOfClass:
respondsToSelector:
instancesRespondToSelector:
forwardInvocation:
methodSignatureForSelector:
instanceMethodSignatureForSelector:
from which isKindOfClass:, conformsToProtocol: and respondsToSelector: are definitely crucial.
I've also used the third path with good results, but I admit the associated objects API is clunky.
First, gnasher729 is correct. Don't subclass class clusters. Just don't do it. Can you do it? If I tell you that you can't, will it help you convince yourself that you shouldn't? I can lie if it helps you make good choices.
But in all seriousness, it is almost always meaningless as well. Is your subclass really a specific kind of set? Or is it really kind of like a set. Consider NSAttributedString. It isn't a kind of string, it has-a string. This is almost always better.
And also, class clusters happen to be a royal pain to subclass.
That said, adding associated values onto a data structure, as you've already discovered, is generally just fine, because what you really want is "hey, I have some data that needs to go along with this other data." Wrapping has gotten so easy that it shouldn't really slow you down. See https://stackoverflow.com/a/14918158/97337:
objc_setAssociatedObject(self, animatingKey, #(value), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
And with "one weird trick", you can make this really easy:
#interface NSObject (BoolVal)
#property (nonatomic, readwrite, assign) BOOL boolVal;
#end
#implementation NSObject (BoolVal)
- (BOOL)boolVal {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (void)setBoolVal:(BOOL)value {
objc_setAssociatedObject(self, #selector(boolVal), #(value), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
But I'd still come back to the question of whether this is really a kind of set (rather than just like a set), and whether it really needs to respond to every message that can be sent to a set. As with NSAttributedString, your real needs are often much smaller than that in practice, and wrapping the handful of methods you need is often worth the simplicity and control.
For completeness, let's look at your first path:
create sort of a composite object that declared itself as a subclass of NSMutableSet but really just forwarded the invocations to an internal NSMutableSet
Can you subclass an NSMutableSet? Yes, but should you? The documentation for NSMutableSet says:
Subclassing Notes
There should be little need of subclassing. If you need to customize behavior, it is often better to consider composition instead of subclassing.
So weigh that up and if you want to subclass refer again to the documentation:
Methods to Override
In a subclass, you must override both of its primitive methods:
addObject:
removeObject:
You must also override the primitive methods of the NSSet class.
And looking at the NSSet class documentation we find its primitive methods are:
Methods to Override
In a subclass, you must override all of its primitive methods:
count
member:
objectEnumerator
That's it, 5 methods.
You can define your own class as a subclass of NSMutableSet, add an instance variable which is an instance of NSMutableSet, implement 5 methods and redirect them to the set instance, add whatever init methods you wish, and then add your additional properties.
If performance is of concern then the tradeoff is between redirecting those five methods and accessing associated objects for your additional properties. You'll need to profile to work that out, but if and only if performance becomes an issue.

Why would you use categories over subclasses?

I just try to figure out the upside of categories compared to subclassing... I do understand how they are implemented, but the only upside I see right at the moment is, that it saves you from refactoring your whole code, if you wanna extend a used class in a later stage, which normaly shouldn't happen with a good planning. Otherwise it takes about the same time to implement as a subclass and it doesn't really bring different functionality. So for my knowledge about subclasses vs. categories I don't see a reason why to use categories. Can someone please wash my head and explain the reason for the existence of categories? I'd be very thankful :)
You're focusing on objects that you create, in which case, subclassing is fine. But what if you're calling some Cocoa method that returns some standard object. Do you want to have to create a new instance of your subclass everytime just so you can use your new method? No, it's much more convenient to be able to create methods that you add to existing class via category.
Also, you might want your new methods to be available to not only the base class, but all of its subclasses, too (e.g. if you add extension to NSString, it's available to NSMutableString instances, too).
For more information, see the discussion in Customizing Existing Classes in the Programming with Objective-C guide.
A major difference is that categories can not add instance variables, subclasses can.
Additionally there are classes that are very difficult to subclass such as NSString, see the subclassing notes. Here is an excerpt: "It is possible to subclass NSString (and NSMutableString), but doing so requires providing storage facilities for the string (which is not inherited by subclasses) and implementing two primitive methods." As soon as you see but you know it will not be easy.
Try adding a new method to the NSString class. Try doing it by subclassing NSString and by adding a category. One of these takes two minutes, the other you are never going to get working properly. That will then answer your question.

Subclass NSArray in Objective-C

I need to have a class, which has all methods of NSArray, which behave the same way, but 2 methods are modified.
I want to override these 2 methods in my custom class:
1) countByEnumeratingWithState:objects:count:
2) objectAtIndex:
After hours of research I don't see any reasonable way to do that, because:
I don't want to use category, because not all NSArray instances should have the modified behaviour. (Plus that throws warnings)
I don't want to re-write all initializers plus all arrayWith... methods + the primitive methods + implemented my own storage (because this functionality is already implemented in Cocoa, right? Why would I re-implement all the functionality of a class that is already there?)
If I have my custom class inherit NSObject and use NSArray as storage in an ivar, then all NSArray's methods are not available when programming in Xcode (even if I can forward them to the NSArray ivar)
I had some success overwriting the method implementations on demand by using method_setImplementation(...), but still can't figure out a way to have dynamically a class created at runtime, which then will have custom implementation of the 2 methods I mentioned.
Looking forward to your ideas! Thanks
Mantra: If something is hard (or seems like it requires more code than is necessary), it is likely that your design is counter to the design principals of the iOS / OS X frameworks. It may yield a better solution to revisit your design.
To answer the original question, if you want to subclass NSArray (or NSMutableArray), you need to implement the primitive methods, no more, no less.
The primitive methods are the methods declared in the #interface of the class itself. I.e.:
#interface NSArray : NSObject
- (NSUInteger)count;
- (id)objectAtIndex:(NSUInteger)index;
#end
And for NSMutableArray:
#interface NSMutableArray : NSArray
- (void)addObject:(id)anObject;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)removeLastObject;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
#end
If you subclass NSMutableArray and implement the above 7 methods (the two from NSArray, too), you will have an NSMutableArray subclass that is compatible -- assuming your methods are correctly implemented -- with all APIs that consume mutable arrays.
This is because of the way class clusters are designed. The public classes are abstract; are never directly instantiated. They provide a primitive interface that contains the class's core functionality and then concrete implementations of all the other non-primtive API (save for the initializers, see below) that are implemented in terms of the primitives. Concrete, private, subclasses then override all the primitives and some of the non-primitives to provide optimal behaviors for specific configurations.
I want to have an NSArray instance for a library I'm working on and I
want to have it working transparently for the users of my library. Ie.
for them should be no difference between using a normal NSArray and
the modified class I'll be providing. Ie. it's a storage concern,
which the end users should not be concerned with and the interface
should remain the same as NSArray - therefore loosing all init methods
is not really an option at that point.
The initialization methods are not a part of the primitive interface to NSArray. You are adding a requirement above and beyond "make a class compatible with NSArray / NSMutableArray" as defined by the documentation. Nothing wrong with that, just pointing it out.
The reason why this is the case is because it is exceptionally rare to subclass the collection classes to provide the kind of business logic you describe. Collections are very generic in their behavior whereas such business logic that conditionalizes collection behavior would be done in a class that manages the overall model layer object graph.
If you really want to do this, provide an implementation of whatever init* methods you want, calling through to your wrapped generic instance as needed. There isn't anything so special about the implementations of the initializers that you are going to lose much in doing so.
No need to implement all of them, either. Implement one or two and #throw a descriptive exception on the rest.
If you do decide to forward the ones that accept var-args, you can't directly because there are no va_list accepting methods. Instead, you'll want to convert the va_list of arguments into a language array (i.e. id[] foo = malloc(... * sizeof(id));) and pass it to initWithObjects:count:.
Some other comments:
What you are doing [provide full NS*Array interface in a subclass] seems hard because it is not a common pattern and the framework designers saw no need to create a design to support it. Custom behaviors at the primitive collection levels are almost always better implemented at a higher level within the object graph. Almost always.
method_setImplementation() and dynamic class creation is academically interesting, but pretty much never a solution. Obviously, mucking with the NSArray or NSMutableArray classes (or the concrete implementation classes) is going to blow up the rest of the frameworks that rely upon standard behavior. Beyond that it, it is a pattern of dynamic OO composition that is not really intended to be used in Objective-C; it'll be a pain in the ass to maintain.
Instead of subclassing NSArray why not create a new class based on NSObject that contains an NSArray?
Then you can use all the functions of the NSArray and add your own methods that will do custom actions with it?
Or do you NEED an NSArray?

Avoiding coupling in a document-based Cocoa app?

I'm new to Mac programming and I'm working on a document-based application.
My NSDocument subclass creates a NSWindowController subclass. This window controller creates two NSViewController subclasses as well.
Sometimes, a change in one of the views of a NSViewController needs to notify the NSDocument and/or the main model class. Also, a change in the model needs to be notified to every/some view(s).
My question is: what is the best approach so that there is no (or minimum) coupling? I know there are several choices, but I'm not sure which one suits best for my application as I'm newbie not to programming but to Cocoa and especially NSDocument:
KVO. Looks nice and easy to implement, but I don't like the idea of not explicitly notifying the observer(s) about a change (AFAIK, self.someProperty = newValue does automagically notify observers), and don't like the fact that you have to register to property names which could change in time.
Notifications. I know what they are and I've used them for iOS. But I've read somewhere that they are not guaranteed to be sent immediately to observers. Is it true? If not, do you see them as a good approach for a document-based app?
Delegates. Yes, under normal conditions (or what I've usually seen), a class has one delegate. But creating an array of delegates works as well (just tested it). The problem I see here is that every time I need to notify the delegates I have to loop through them, make sure they respond to a method, and call that method.
Are there any other alternatives I'm missing?
KVO by a controller class is the most common way to do coupling between a model and its view(s). In fact, Cocoa Bindings, which are intended to mostly eliminate code in the controller layer, are based on KVO. It is true that KVO/KVC relies on property names, and that if those change, you'll have to change the bindings or KVO setup connecting your view. However, it's not usually feasible to make your views completely unaware of the underlying model specifics, so I don't see this as a problem.
My recommendation would be to use Cocoa Binding where you can, as they eliminate a lot of glue code. In places where they can't be used, your controllers (the middle layer in MVC) should use KVO to observe model changes and update the appropriate views. Changes in the views can be passed back to the model via property accessors and/or KVC by the controllers.
Yes, under normal conditions (or what I've usually seen), a class has
one delegate. But creating an array of delegates works as well (just
tested it).
Delegates are often used to modify the behavior of the delegating object. An application delegate is a good example: NSApplication by itself isn't very interesting; it relies on its delegate to define the interesting behavior of the application. Having multiple delegates all trying to modify the behavior of a single object could be a problem if the various delegates conflict with each other. What do you do if the delegates disagree?
There are some cases in Cocoa where a class uses more than one delegate, but each one has a separate role. For example, NSTableView has both a delegate and a data source, but both are really delegates of a sort.
The problem I see here is that every time I need to notify the
delegates I have to loop through them, make sure they respond to a
method, and call that method.
That's not hard to solve. For example, you could create an NSInvocation to encapsulate the call, and then send the invocation to each "delegate." However, if you do that you'll have nearly reinvented the notification system. If you need the one-to-many communication that you'd get with your multiple delegates proposal, you'll probably be better off using notifications or KVO.

What describes objective-C and Cocoa Bindings best?

I have trouble understanding Cocoa Bindings. Can someone explain me what this is all about, in an way that is humanly perceivable?
Bindings is a system for connecting your views to your controllers without writing a lot of glue code to make them explicitly talk to each other. All you have to do is set up properties in both classes* and hook up the binding in IB.
The traditional approach is that the view has one or more outlets to talk to the controller (the most generic examples being delegate and target) and the controller has outlets to talk to the views. When the controller updates the model, it sends (for example) [view modelChange:newModelObject]. When the view wants to update the model, it sends some delegate message to its delegate (the controller), such as NSText's textDidChange:.
With Bindings, all you have to do in code is implement properties on the view and properties on the controller, then expose one or more properties of the view as bindings*. Then you only need to hook up the binding. If it's a Cocoa class, this is cake: just set it up in IB. If it's a custom class of your own, you'll probably write the bind:toObject:withKeyPath:options: message yourself (not much harder).
Let me restate that: With Bindings, your entire glue code (most of the time) is [view bind:#"viewProperty" toObject:self withKeyPath:#"controllerProperty.modelProperty" options:options]; in the controller. Everything else is handled by the Bindings and KVO systems behind the scenes, and by your properties' accessors.
The disadvantage is that you must strictly conform to Cocoa Bindings' requirements. These are simple, but a lot of older applications are designed in a way that doesn't fit Cocoa Bindings.
You must create real model objects, not just pass primitive objects (e.g., arrays of dictionaries) around. If you're using Core Data, this is easy: your managed objects are model objects.
You must either write your accessors correctly or synthesize the correct accessors. For example, an NSString property should always be #property(copy), never #property(retain) (because otherwise, you will find yourself retaining someone else's mutable string, which they will then mutate while you're holding it).
You must only change properties of your model objects by their properties (model.foo = bar) or by accessor messages ([model setFoo:bar]), never by direct instance variable access. (Obvious exception for accessor methods themselves, if you've written your own, because they must access the instance variable directly.)
There are two advantages:
You can write a brand new view class without having to rip out a lot of glue code. The most you'll have to delete is some bind:::: messages for the old view's properties. If, a couple of years down the road, you decide that your current view just can't scale to your application's forthcoming capabilities, this gives you the flexibility to rip it out and start afresh with the minimum of pain.
More importantly, the less code you have to read, the easier it is to read it.
*And, according to the documentation, implement a KVO observation method in the view class, but I've never actually had to do this. I filed a documentation bug.
Added 2009-03-07: Ah, found a citation. “NSView subclasses can expose additional key-value-coding/key-value-observing compliant properties as bindings by calling the class method exposeBinding: for each of the properties.” —NSKeyValueBindingCreation So you shouldn't need to implement a KVO observation method.
Previous answer is very comperhensive and good, I'd just thought I'd add an answer explains what it is at its core without involving Cocoa or Objective-C specifically. That is because the concept itself is language agnostic although dynamic languages like Objective-C makes it a lot easier than more static language like C++.
Example
Say you have two objects M and V. M has methods:
setX(int x);
setY(int y);
int getX();
int getY();
While V has methods:
setA(int x);
setB(int y);
int getA();
int getB();
One way of looking at this is that M has properties x and y and V has properties a and b. You want a change of property x to cause a change in property b and a change in y to cause a change in a.
By change in property x we mean e.g.:
M.setX(10)
where previously
M.getX() != 10
So we want a call of setX on M to cause a call to setA on V.
What bindings allow you to say is that property b on object V is bound to property x on object M. And then this updating is handled automatically. You as a coder don't have to write code that checks if x is changed and then call setB on V. Bindings takes care of this automatically.
Summary
Bindings allows you to bind two properties together that exist on two different objects, so that changing the value of one of the properties causes the dependant property in the other object to change to the same value.