i've been reading for a while a little bit of Objective C and Cocoa, and i have to say that although i am thrilled with the overall simplicity of Objective-C, I feel totaly lost regarding Cocoa and especially the MVC design pattern. The Cocoa books I've read, ask the reader to just complete a set of steps for a program to work, without doing much explanation about the mechanisms of the API. I am trying to understand the overall meaning of NSTableView, NSOutlineView, their datasources, their delegates, and what all these have to do with the various controller classes like NSArrayController and NSTreeController. So i have a couple of questions to ask:
How do these classes really interoperate ?
Is the datasource of an NSTableView an instance of an NSArrayController ?
Should a delegate and a datasource be the same class in a NSTableView or NSOutlineView ?
Leaving binding aside, do NSArrayController and NSTreeController have any other important advantage ?
Let's assume that there is a container (in C) with some sort of data (a C struct). We want to be able to show these data in an NSTableView (or NSOutlineView) and to exchange views at runtime, if the data structure is hierarchical. Should i create a class that has an NSArrayController (or NSTreeController) as instance, and wrap the functionality of the C container ?
Thank you and forgive me for my silly questions.
These are not silly questions. You're asking precisely the right questions, and have gone to the heart of how to learn Cocoa. Learn MVC first, and how Cocoa implements it, and the rest will be clear.
While slowly becoming dated, there is still no better book for learning Cocoa than Cocoa Programming for Mac OS X by Aaron Hillegass. I wrote up some more comments about this book last year if you're interested. And some more guides (mostly targeted at new iPhone developers) here and here.
To your specific question, rather than store the data in a C struct, store it in an Objective-C object (a model object, the "M" in MVC).
Then build a Controller class to be the datasource for the NSOutlineView (which is designed for hierarchical data). For the moment, avoid Cocoa classes that end in "Controller." These are related to bindings (and mostly used for Core Data). While very powerful, they're more than you want to dive into at the beginning (and even after years of Cocoa programming, I only use bindings for certain targeted problems; they can make your programs very hard to maintain if they're complicated). Your controller for now should be a subclass of NSObject. It will implement the NSOutlineViewDataSource methods. If you need a delegate, it is common and natural to make the same object be the datasource and delegate.
How do these classes really interoperate ?
Consider the traditional way using a TableView or OutlineView (in any language). You typically implement a data source interface that will provide data for the view. This data source implementation is essentially a "controller" that translates your data model to your table view; and it can also handle row selection, inserting, deleting, sorting, filtering, etc.
Now say your app has a number of different TableViews, each with its own data source implementation. You'll find that each data source implementation starts to look the same; you'd be able to reuse a lot of the code between your data sources, because assuming they're working on the same or similar data models, the code for sorting etc. is also the same.
That's what NSArrayController is: a generic TableView controller/data source. It handles row selection, inserting, deleting, sorting, filtering, etc. thereby saving you from having to write a bunch of boilerplate code.
Is the datasource of an NSTableView an instance of an NSArrayController ?
No, data bindings replace the traditional data source interface; therefore, the NSTableView is connected to the array controller using data bindings.
For example, you would bind a table view column to a property of your model:
myArrayController.arrangedObjects.firstName
where arrangedObjects is a property of NSArrayController and firstName is a property of your model.
Should a delegate and a datasource be the same class in a NSTableView or NSOutlineView ?
When using data bindings you do not set the NSTableView's data source. You may still provide an NSTableView delegate to customize the look and behavior of the view. NSArrayController is not an NSTableView delegate.
When not using data bindings then you must implement a data source. Often the data source and the delegate are the same object.
Delegate is object that receives callbacks when something interesting happens with the view.
Datasource is very similar to delegate - it also receives callbacks, but only those related to getting data for the table.
They don't have to be the same (you might have controller/delegate that can operate on any data source), but in practice they're usually the same object, because it's convenient to provide data and operate on it in the same class.
You don't need to use NSArrayController at all, if that doesn't fit your program. Any class can be a delegate (it's informal protocol, meaning you don't need to declare any interface nor inherit any base class), you can simply write your own delegate methods.
Related
The UITableView can be used to create a list view if the at least the UITableViewDataSource is adopted by the relevant class. I have the below questions:
Why is it designed in such a way that based on the section and row , the controls are created through data source methods and given back to the UITableView instance. Why not provide all these information in UITableView instance with out using the UITableViewDataSource. What difference is it going to make?
EDIT1:
#hermann and #JOhn: You have mentioned that it breaks the MVC pattern. Let us assume I am creating a custom UITableView like control myself. I design it in such a way that I do not pass the data directly to the UITableView but instead I pass the relevant subviews that needs to be added in the rows and section and their relavant headers alone. I think this will not break the MVC..Am I correct? But still it has the problem that the current UITableView implementation style solves..the ability to reuse controls and images instead of bloating the memory usage.
First, doing so allows to stick on the MVC pattern. A UI object, meaning a view, should never directly communitcate with the model, wich is the business data. Plus it should not perform any business logic, which belongs to the controller.
Second, it is more flexible without the need of subclassing the UITableView object.
Third, the full concept is quite efficient from a performance and memory management point of view. Instead of loading all data upfront, that may be displied within a table, it can be fetched or calculated or whatever on a just in time "need to know now" basis.
Plus data containers, especially memory consuming once like images, can be released as soon as the data has been provided using the delegate/data source methods.
Of course, a subclass of UITableView could do the same in principle. I just doubt this would result in more maintainable code or even save any time or work resprectively.
If you don't want to stick with MVC, then feel free to subclassing the table view and hand all its data over in its init method or enable the table view subclass to load all that data from webservices or data bases or wherever it comes from.
And when you run into problems and get back to us searching for guidance, then be prepared for some nasty replies like "Why don't you stick to the established best practices or the MVC pattern?" ect.
In the event that your very table just displays some rather static values, such as if it just acts as menu for further navigation drill down etc. then this concept may look a bit rediculous. But it does not hurt either. And when it comes to more complex tasks of data providing then it certainly has its advantages.
Give it a chance. Give it a try!
For MVC you want to try to clearly separate what the model, controller, and views are responsible for doing. By allowing the view (tableview) to ask for the data from a delegate, the view can be create extremely generalized.
Using the delegate pattern, the controller can "stage" the data in anyway that is wants and then give it to tableView as the tableView needs it. The tableView doesn't care where the data comes, how it was transformed, what ADT is used, nothing. It is complete ignorant to where or what the data is. All it knows it should show this string at this location for this section and row.
I've been struggling to understand the best way to insert controller logic when using IB, NSObjectController sub-classes and bindings.
I need to insert controller logic between the model and the view, and I'm struggling to find an elegant way to do so. Yes, you can send actions to the file owner and handle controller logic in there, but when some core data models can extend across fifty entities or more with deep relationship structures, this starts to mount up to an incredible amount of boiler-plate code.
A very simplified example would be this; imagine you have an entity with four string attributes myTextWinter, myTextSpring, myTextSummer, myTextAutumn. You have a view which connects to this in IB via an NSObjectController. Now, say the user can select which 'Season' they wish to view by choosing Spring, Summer, Autumn, Winter from a Menu somewhere - when that season is selected, I would like to display the appropriate season's text.
In this simplified example I could probably fetch the object in the NSDocument sub-class, create a property called mySeasonText which I bind to in my view, and then check my NSUserDefaults for the appropriate season and route the requests to the appropriate attribute in the model.
The problem comes when I have fifty entities, some with relationships some two, three or more deep, each with their own set of season specific text attributes that I wish to switch between when selecting from the Season menu. Or if I have a bunch of nsarraycontrollers chained together to access objects at a deeper, and deeper level.
To date, I've been doing the following; adding a property in each of my model objects called 'mySeasonText', then grabbing the setting from my controller setting, and routing to the appropriate season. I refresh these objects whenever a new item in the menu is selected.
Whilst this works and eliminates an absolute ton of boiler-plate code, my controller logic is now in my model.
There must be a better way! Could someone please point me in the correct direction?
This is a tricky topic. Apple even mentions these challenges in its own documentation:
By using the bindings technology, you can easily create a Cocoa MVC application whose views directly observe model objects to receive notifications of state changes. However, there is a theoretical problem with this design. View objects and model objects should be the most reusable objects in an application. […] Design-wise, it's best to keep model and view objects separate from each other, because that enhances their reusability.
The design pattern you are looking for is a Mediating Controller - a way to use the cocoa-bindings classes to insert controller logic:
Mediating controllers are typically ready-made objects that you drag from the Interface Builder library. You can configure [Mediating controllers] to establish the bindings between properties of view objects and properties of the controller object, and then between those controller properties and specific properties of a model object. As a result, when users change a value displayed in a view object, the new value is automatically communicated to a model object for storage—via the mediating controller; and when a property of a model changes its value, that change is communicated to a view for display.
Here is how I like to think of them: Have you ever seen a movie or TV show where two characters need to talk, but they don't speak any of the same languages? They find someone else (or in a comedy 5 other people) who each have one language in common, and they communicate by playing a giant game of translation telephone.
Mediating controllers are kind of like that.
As your application grows they learn all the super specific rules about where to look for this one thing on this one view. This is the kind of code that an app needs to run, but you rightly feel that it is nasty when put in your model.
For several specific and detailed examples, Apple provides this amazingly detailed document: Bindings Message Flow.
For some really good discussions about this and related MVC + Bindings please see:
MVC and cocoa bindings best practices question
Why use NSObjectController?
Replacing model objects using an NSArrayController
I'm reading a book on Objective-c and learning about the undo manager. The concept seems very simple but the provided example seems overly complex. Basically, I have a table view connected to an NSArrayController and I add or remove people to an array and I can edit their names and stuff. Because the example uses NSArrayController and bindings, add and remove are automatic and all of the editing is automatic.
To use the undo manager, from what I understand, I need to implement my own methods to add/remove/edit.
These methods I've implemented to do the adding and removing and get called automatically due to key value coding:
- (void)removeObjectFromEmployeesAtIndex:(int)index;
- (void)insertObject:(Person *)p inEmployeesAtIndex:(int)index;
Then for editing, I had to register the class as an observer and observe changes to edit:
- (void)changeKeyPath:(NSString *)keyPath
ofObject:(id)obj
toValue:(id)newValue
Here are my questions:
Why do I have to do so much? My understanding was that using the NSArrayController and bindings was supposed to make things like adding/removing/editing items easier and more automatic. But if I have to implement all of these methods manually anyway just to add undo support, why use NSArrayController or bindings at all?
What's going on behind the scenes? In Interface Builder, the add button is connected to the add method on the NSArrayController. How then does my insertObject method get called? I know it's through key value coding but what makes the NSArrayController's add method get overridden just b/c my document implements this method?
The solution is asymmetric. I use one concept to handle undoing add/remove and another concept to handle undoing edits. Couldn't I also just observe changes to the array? I suppose it would complicate the observeValueForKeyPath method, but would that make more sense?
1) Nearly, but not quite. If you think of your application code being divided into three overall areas: Model, View and Controller (as documented here) then the Cocoa/XCode environment provides you with a 'code-free' way of handling the basics of each: IB for the view, Core Data for the model, and Bindings / Object Controllers for the controller.
Undo management is primarily a concern of the model, not the view or controller. So it's not really Bindings or the Object controller's job to manage this stuff. It looks like your problem is that you're using arrays as your data objects, which are too lightweight to handle this stuff. If you want undo support, you'll want to use core data to handle the model and give you this stuff for free, or hand-roll your own model objects, (which will probably contain arrays) which handle this logic.
FWIW, once you've done this, bindings will indirectly make your life much easier, as when an undo command reverts your data to its previous state, the view will automatically reflect the changes.
Also, NSArrayController's name is slightly misleading -- it isn't there to 'control arrays'. It's really for controlling data objects which have to-many relationships to other data objects. Which brings me on to...
2) KVC allows you to treat a to-many relationship between an object and other objects as an array or set, regardless of how the relationship is actually implemented. It does so by requiring you to implement methods fitting a naming convention, which very closely match the primitive methods of arrays and sets. KVC-compliant objects will return a proxy array or set when you call mutableArrayValueForKey: or mutableSetValueForKey:, which exposes those methods as an array. Roughly, that's how NSArrayController knows what to call --- KVC maps between the primitive objects of an array and some methods whose manes it generates from the key. Since you don't want to use arrays as your data objects, it's generally very useful to be able to treat any to-many relationship as if it were just an ordinary collection.
3) I think this is related to you handling undo in the wrong place. Implement KVC-compliant methods to get/set properties in your data objects, have them update the undoManger at the same time as setting the data. You'll need a special method for the undomanager to revert changes, as you don't want undos to be recorded as undoable. Or you could just use Core Data and get all this stuff for free...
This is a question about best-practices.
I have an application that uses a standard iOS tab controller.
One of the things that I'd like to do is split the XIB up into separate files. I can achieve this by specifying the 'child' XIB in the 'NIB Name' section for each tab controller. So far, so good.
In this application, I have an object that is used by virtually all of the UIViewControllers (e.g: provides web service calls). Let's call it MyServices.
In the single XIB solution, I can drag an object onto the Objects list, set the type to be 'MyServices'. I can declare in each ViewController an IBOutlet of type MyServices*, and wire the two together. This works nicely.
However, if I move my view out to a separate XIB, any controllers further down the stack that need access to the MyServices object are out of luck, because the object no longer exists within that XIB to perform wiring with.
What I'd expected to be able to do is to declare an 'external object', and wire to that instead. But I can't see how I 'pass' the MyServices Object in the 'parent' XIB as the 'external' object in the child XIB.
Is this just not supported in IB? What is the best alternative?
I could not specify the XIB name in the controller, and perhaps programatically create it at runtime (presumably with some kind of loadFromNib code declaring a dictionary to provide the external object). This does mean that the controller that does this has to be aware of MyServices, even if it doesn't use it directly.
Alternatively, I could have a 'dataProvider' in each UIViewController, so instead of setting the MyServices directly as an IBOutlet, it could do do [dataProvider getServices]. Again, will have to be wired to something that can do that - which limits where XIBs can be broken up. And it feels a bit needlessly verbose..
What's the best practice here?
It looks like with using External Object, you take the object instantiation back into your own hands and you also have to instantiate the NIBs manually. At least that's what I gathered from the answer to How to use a common target object to handle actions/outlets of multiple views?
Can I use Interface Builder to inject dependencies across multiple nibs? asks very similar question to yours, also without a real solution.
In How do I set up a proxy object in the main application NIB? the author also gives up on the idea of using Interface Builder as a dependency injection tool.
So I would guess that we, the Java immigrants, are banging our heads against invisible walls here. The metaphors we use to shape the code in our heads (and the code qualities we've come to value and associate with quality), do not apply to Objective-C, as is. That might be because we are not familiar with Obj-C idioms. Or it might be, that we are dealing with different evolutionary stages of language and community development (for example see the staggering immaturity of TDD practice in Obj-C). I, personally, have not seen much best practices described in Obj-C world in the 9 months that I am seriously dealing with it.
for example the UIPickerView, in the tutorial that i am learning i had to include the datasource and delegate protocols in my project for the pickerview to work. how would i know on other objects?
In general that is explained in the documentation of the individual object. For example http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIPickerView_Class/Reference/UIPickerView.html
In the Overview section it explains that, "the delegate must adopt the UIPickerViewDelegate protocol" and that, "the data source must adopt the UIPickerViewDataSource protocol"
From http://developer.apple.com/iphone/index.action just type the name of the object you are interested in into the search box and the documentation should explain everything needed to make it go.
To note, the UIPickerViewDelegate/Datasource are representative of the Delegate design pattern (see Cocoa Design Patterns) and are repeated throughout the Cocoa UI hierarchy as a method of modifying behavior of an object without having to subclass. It's quite graceful, less entropic, fosters the single responsibility principle, and reduces coupling. The delegation pattern is seen throughout all of Cocoa, not just the UI classes, so you can expect to see it often.
To know about other objects, you pretty much have to visit the Framework Library Reference for the specific class at the Apple Developer Center or from within the help system of Xcode. You can almost presume that all data backed UI objects will have datasource (delegate) methods, and most UI objects will have delegate methods.