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...
Related
Say I have a Core Data class called RecipeBook which has a property (relationship) called recipes, a NSSet of Recipe Objects.
I display the RecipeBooks on a UITableView and when the user taps on a cell, it should display the Recipes on another UITableViewController.
What should I pass as the model to this last UITableViewController:
a context and a fetchRequest
or the NSSet of Recipe objects?
If there's a change to the db will the NSSet "automagically" update?
From your description I'd probably pass the selected RecipeBook instance. From that I can (presumably) get all the recipes contained in the book and display them in the table. That assumes that a relationship exists from RecipeBook to Recipe which-- based on your description-- should be true. If I need to do any other work with the data store, I could ask the RecipeBook for its managed object context and work with that.
No NSSet of fetched objects is going to update automatically. But the relationship from a RecipeBook to its Recipes will update any time a recipe is added or removed from the recipe book.
And finally-- passing any of this directly to a UITableView doesn't make a lot of sense. Apple's iOS frameworks are designed with MVC in mind, and going against that will make things a lot harder than they need to be. If you have a UITableViewController, you could pass your model objects to that.
Generally, you shouldn't pass "model" objects to view objects.
My favorite way to think about it is that views are actually another form of a model (think of them both as simply representations of data). The controller's job is to ensure that neither representation needs to know anything about how the other stores it's representation.
So basically, your controller will be your data delegate, and is responsible for properly populating table cells with it's own references to your core data models.
The automagically question depends on your core data setup, but usually the answer is yes.
By default, CD uses key-value coding, which simply lets you access properties with valueForKey:. More advanced setups involve having Xcode generate classes for you, in which case a few mouse clicks get you "dot notation" accessor methods regenerated from an updated model.
Let's say I have an class to model a city. Its characteristics are the following:
It has only two properties "name" and "population", both private, that are set in the constructor.
It has getters for these properties, but not setters.
I don't want any user of this class to set the properties, I want them to use a public .edit() method.
This method needs opens up a form to input the new name of the city and population, i.e.: a view. Then, if I have a view, I would like to implement the MVC pattern, so the idea would be that the controller receives the .edit() call, renders the view, retrieves the data back, and sends it to the view so that it changes its state.
But, if I do so, I have to change the properties of the city model from private to public. So, if any user instantiates my class, she/he can directly change the properties.
So, the philosophical question: Isn't that breaking the encapsulation?
EDIT Just to make it more explicit:
This city_instance.edit() method should be the only way to mutate the object.
Besides, I see that part of my problems comes from the misunderstanding that a model is an object (you can read that on php mvc frameworks), when it is actually a different abstraction, it's a layer that groups the business logic (domain objects + I guess more things)
Disclaimer: I don't really understand where are you proposing the .edit() method to be implemented, so it would help if you could clarify that a little bit there.
The first thing to consider here is that in the bulleted list of your question you seem to imply that a City instance acts like an immutable object: it takes its instance variables in the constructor and doesn't allow anybody in the outside to change them. However, you later state that you actually want to create a way to visually edit a City instance. This two requirements are clearly going to create some tension, since they are kind of opposites.
If you go the MVC approach, by separating the view from the model you have two main choices:
Treat your City objects as immutable and, instead of editing an instance when the values are changed in the form, throw away the original object and create a new one.
Provide a way to mutate an existing City instance.
The first approach keeps your model intact if you actually consider a City as an immutable object. For the second one there are many different ways to go:
The most standard way is to provide, in the City class, a mutator. This can have the shape of independent setters for each property or a common message (I think this is the .edit() method you mentioned) to alter many properties at once by taking an array. Note that here you don't take a form object as a parameter, since models should not be aware of the views. If you want your view to take note of internal changes in the model, you use the Observer pattern.
Use "friend" classes for controllers. Some languages allow for friend classes to access an object's internals. In this case you could create a controller that is a friend class of your model that can make the connection between the model and the view without having to add mutators to your model.
Use reflection to accomplish something similar to the friend classes.
The first of this three approaches is the only language agnostic choice. Whether that breaks encapsulation or not is kind of difficult to say, since the requirements themselves would be conflicting (It would basically mean wanting to have a model separated from the view that can be altered by the user but that doesn't allow the model itself to be changed for the outside). I would however agree that separating the model from the view promotes having an explicit mutation mechanism if you want mutable instances.
HTH
NOTE: I'm referring to MVC as it applies to Web applications. MVC can apply to many kinds of apps, and it's implemented in many kinds of ways, so it's really hard to say MVC does or does not do any specific thing unless you are talking strictly about something defined by the pattern, and not a particular implementation.
I think you have a very specific view of what "encapsulation" is, and that view does not agree with the textbook definition of encapsulation, nor does it agree with the common usage of it. There is no definition of "Encapsulation" I can find that requires that there be no setters. In fact, since Setters are in and of themselves methods that be used to "edit" the object, it's kind of a silly argument.
From the Wikipedia entry (note where it says "like getter and setter"):
In general, encapsulation is one of the four fundamentals of OOP (object-oriented programming). Encapsulation is to hide the variables or something inside a class, preventing unauthorized parties to use. So the public methods like getter and setter access it and the other classes call these methods for accessing.
http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)
Now, that's not to say that MVC doesn't break encapsulation, I'm just saying that your idea of what Encapsulation is is very specific and not particularly canonical.
Certainly, there are a number of problems that using Getters and Setters can cause, such as returning lists that you can then change directly outside of the object itself. You have to be careful (if you care) to keep your data hidden. You can also replace a collection with another collection, which is probably not what you intend.
The Law of Demeter is more relevant here than anything else.
But all of this is really just a red herring anyways. MVC is only about the GUI, and the GUI should be as simple as possible. It should have almost no logic in either the view or the controller. You should be using simple view models to deserialize your form data into a simple structure, which can the be used to apply to any business architecture you like (if you don't want setters, then create your business layer with objects that don't use setters and use mutattors.).
There is little need for complex architecture in the UI layer. The UI layer is more of a boundary and gateway that translates the flat form and command nature of HTTP to whatever business object model you choose. As such, it's not going to be purely OO at the UI level, because HTTP isn't.
This is called an Impedance Mismatch, which is often associated with ORM's, because Object models do not map easily to relational models. The same is true of HTTP to Business objects. You can think of MVC as a corollary to an ORM in that respect.
In my application, I have to display image files as a list in tableview, present them in full size and as multiple thumbnails. Hence basically I developed three seperate classes to handle these three views. Now to perform any file operations, I can think of two approaches:
Create appdelegate objects for all these classes, handle them accordingly. When one operation on a photo file is performed in one class, all other classes are notified using NSNotification, keeping the obeserver as Appdelegate object.
Create locally objects for these classes as and when required and assign delegates for performing file operations from one class to other by calling relevant methods.
However, I was not able to judge Which approach would be better in terms of memory usage and performance? Thanks in advance.
Using a one-to-one relationship with direct messaging is the simpler relationship and means of communication/messaging. Favor the delegate callback -- Number 2.
It is also easy to make this design bidirectional -- if the view goes offscreen, you could perform a cancellation. If the load fails, it is easier to inform the controller.
NSNotifications are comparably heavyweight. Not necessary.
Storing a bunch of stuff in a singleton (app delegate) can result in several unnecessarily retained objects. If your program is concurrent, then that can add even more complexity. There's no need for any of this complexity or introduction of mutable global state, and there is no reason presented whereby the objects should have a much larger scope of access and lifetime.
You can optimize for specific needs beyond that, but I don't see any at this time.
It depends a lot on the code and how you are structuring your app. I general use delegates in the following situation:
Where the delegate object exists before and after the main object that needs it. In other words the main object does not need to worry about the lifecycle of it's delegate.
Where the relationship between an object and it's delegate object is a strict one to one. In other words only one delegate object needs to interact with the main object. I have seen situations where delegates are swapped in and out and I would not recommend such code.
Where the main object needs information from the delegate.
I would use notifications where:
Multiple objects need to know of about things happening in another class.
Where the main class does not need to interact with the other classes or even know they exist.
Which ever you choose I would not have more than one file management object for each image. The simple reason being that having multiple means you need to ensure that they all have the same state and therefore are communicating with each other. Otherwise bugs will creep in.
I have 2 basic questions regarding Core Data;
What exactly is a managed object -- Is it equivalent to 1 instance of a class. For example, if there is an entity called Shape which has attributes like no of sides & color and if there are 5 entries displayed in a table, does it mean there are 5 managed objects each with attributes no. of sides & color. I am a bit confused about this basic concept.
What exactly is the relationship between Fetched Results Controller (FRC) and a table view? I know the delegate methods, but how exactly is the table view impacted/related to FRC ?
Any basic examples will be really useful. Thank you.
Q1. A managed object is a representation of some entity that has been persisted by an application. It is simply a generic abstraction away from the actual persisted type. So you are right in saying that the managed object will have the same keys/properties as the concrete type. As the NSManagedbject class implements the key-value coding pattern you can query its key/values at runtime...
- (id)valueForKey:(NSString *)key
For full documentation on NSManagedObject see here
Q2. The fetched results controller is what your UIViewController is to your UIView. It contains the logic that controls persistence for your table view. Its sole purpose in life is to keep database handling logic out of your UITableView. It does so by allowing you to define the behaviour you want to execute in the context of your UITableView. What I mean by this is that its delegate provides methods with signatures that explicitly imply an effect on a UITableView.
Q1. Yes, there are 5 managed objects out there and available to you. You could put all five in an array if you wanted to. Managed Object is simply a term that means you have code to manage the Insert, Change, and Delete actions into the database - in otherwords, some code manages it through its life cycle. You get at it through an FRC, the FRC ^fetches^ instances of the object from the database, and allows your code to ^control^ what happens to the ^result^. Hence the name.
For example, I have window (non-document model) - it has a controller associated with it. Within this window, I have a list and an add button. Clicking the add button brings up another "detail" window / dialog (with an associated controller) that allows the user to enter the detail information, click ok, and then have the item propagated back to the original window's list. Obviously, I would have an underlying model object that holds a collection of these entities (let's call the singular entity an Entity for reference).
Conceivably, I have just one main window, so I would likely have only one collection of entities. I could stash it in the main window's controller – but then how do I pass it to the detail window? I mean, I probably don't want to be passing this collection around - difficult to read / maintain / multithread. I could pass a reference to the parent controller and use it to access the collection, but that seems to smell as well. I could stash it in the appDelegate and then access it as a "global" variable via [[NSApplication sharedApplication] delegate] - that seems a little excessive, considering an app delegate doesn't really have anything to do with the model. Another global variable style could be an option - I could make the Entity class have a singleton factory for the collection and class methods to access the collection. This seems like a bigger abuse than the appDelegate - especially considering the Entity object and the collection of said entities are two separate concerns. I could create an EntityCollection class that has a singleton factory method and then object methods for interaction with the collection (or split into a true factory class and collection class for a little bit more OO goodness and easy replacement for test objects). If I was using the NSDocument model, I guess I could stash it there, but that's not much different than stashing it in the application delegate (although the NSDocument itself does seemingly represent the model in some fashion).
I've spent quite a bit of time lately on the server side, so I haven't had to deal with the client-side much, and when I have, I just brute forced a solution. In the end, there are a billion ways to skin this cat, and it just seems like none of them are terribly clean or pretty. What is the generally accepted Cocoa programmer's way of doing this? Or, better yet, what is the optimum way to do this?
I think your conceptual problem is that you're thinking of the interface as the core of the application and the data model as something you have to find a place to cram somewhere.
This is backwards. The data model is the core of the program and everything else is grafted onto the data model. The model should encapsulate all the logical operations that can be performed on the data. An interface, GUI or otherwise, merely sends messages to the data model requesting certain actions.
Starting with this concept, it's easy to see that having the data model universally accessible is not sloppy design. Since the model contains all the logic for altering the data, you can have an arbitrarily large number of interfaces accessing it without the data becoming muddled or code complicated because the model changes the data only according to its own internal rules.
The best way to accomplish universal access is to create a singleton producing class and then put the header for the class in the application prefix headers. That way, any object in the app can access the data model.
Edit01:
Let me clarify the important difference between a naked global variable and a globally accessible class encapsulated data model.
Historically, we viewed global variables as bad design because they were just raw variables. Any part of the code could alter them at will. This nakedness led to obvious problems has you had to continuously guard against some stray fragment of code altering the global and then bringing the app down.
However, in a class based global, the global variable is encapsulated and protected by the logic implemented by the encapsulating class. This encapsulation means that while any stray fragment of code may attempt to alter the global variable inside the class, it can only do so if the encapsulating class permits the alteration. The automatic validation reduces the complexity of the code because all the validation logic resides in one single class instead of being spread out all over the app in any random place that data might be manipulated.
Instead of creating a weak point as in the case of a naked global variable, you create strong and universal validation and management of the data. If you find a problem with the data management, you only have to fix it in one place. Once you have a properly configured data model, the rest of the app becomes ridiculously easy to write.
My initial reaction would be to use a "modal delegate," a lot like NSAlerts do. You'd create your detail window by passing a reference to a delegate, which the detail window would message when it is done creating the object. The delegate—which would probably be the controller for the main window—could then handle the "done editing" message and add the object to the collection. I'd tend to not want to pass the collection around directly.
I support the EntityCollection class. If you have a list of objects, that list should be managed outside a specific controller, in my opinion.
I use the singleton method where the class itself manages it's own collections, setup and teardown. I find this separates the database/storage functionality from the controllers and keeps things clean. It's nice and easy to just call [Object objects] and have it return a reference to my list of objects.