• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: totally confused by bindings settings
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: totally confused by bindings settings


  • Subject: Re: totally confused by bindings settings
  • From: Ken Thomases <email@hidden>
  • Date: Thu, 5 Jun 2008 15:12:42 -0500

On Jun 5, 2008, at 10:47 AM, Daniel Child wrote:

First off, thanks very much for the lengthy response.

You're welcome. Let's see if we can resolve some of the remaining confusion. :)



Hmm. An NSArrayController doesn't manage tables, it manages an array (as its name implies). So, do you have an array somewhere with instances of the Word class? Where is it? For example, is it a property of File's Owner, with a key "words"?
The class structures are:
Word has three ivars {characters, reading, english} (all strings)
WordList has (for now) just one ivar {wordList} (a mutable array)
In practice, wordList will be an array of Word objects, but it is simply declared as NSMutableArray.
This is simply a reduction to basics from part of a larger target application where I think bindings will be very useful.

All OK.


Contains how? What is the interface of this WordList class? What properties does it have?
wordList is an array of Word objects but is simply declared as NSMutableArray. It could have other classes (and in the real application would) but they're not needed for trying to get the table to work.

OK. Just to rephrase this to match the terminology in my question, that means that the WordList class has a property named "wordList" which is a to-many relationship to instances of the Word class.



That doesn't seem right. The Class Name should indicate what kind of elements are in the array being managed. That's still Word, unless I'm misunderstanding what you're trying to achieve.
OK, thanks for the clarification. But binding to "Word" is not a option given in Interface Builder.

At this point, we're not discussing establishing a binding. We're configuring the NSArrayController so that it (and IB) knows what type the elements of the array will be.



Similarly, the Content Array binding of the NSArrayController should refer to the array of Words maintained by the WordList instance (perhaps as a key path from the File's Owner, or whatever). Therefore, it doesn't make sense to have ".arrangedObjects.wordList" appear in the key path of the table column bindings. The arrangedObjects are already the words in the WordList instance.

Let's suppose that File's Owner has a property "wordList" that is a to-one relationship to a WordList instance.
I haven't altered File's Owner. It is simply NSApplication. I'm not clear how I would even set a property for NSApplication. (If this test were more complex and I were using a window controller for the owner of a different file, I would totally see how to put in such a property.)

You're right when you say (further down) that it's not common to subclass NSApplication. If you did, though, you could tell IB about the custom class using the Identity tab of the inspector (Command-6, IIRC). In that case, your subclass could have properties of its own, possibly including a WordList.


What about the more common case of not subclassing NSApplication? In that case, your application-wide custom behaviors and data are typically put in a custom class and an instance of that class is set to be the delegate of NSApp. The easiest way to set this up is to write up the class (the .h and .m files), drag an NSObject instance into the nib, and set its class to your custom class. For purposes of this discussion, let's say you name that object "MyAppDelegate". Then, Control-drag from File's Owner to MyAppDelegate to connect the application's delegate outlet to it.

In this case, you can establish the bindings directly to MyAppDelegate. So, when I said "File's Owner" in my previous email, substitute "MyAppDelegate".

You could also establish the bindings through File's Owner.delegate.<whatever key path>. It amounts to the same thing for this nib. In other nibs, the application delegate won't actually be in that same nib, and so the ability to refer to it via a key path from either the File's Owner or the Application stand-in will be useful.


Let's also suppose that the WordList class has a property "words" which is the to-many relationship to some instances of Word. Then, the NSArrayController's Content Array binding should be bound to File's Owner with Model Key Path "wordList.words".
But in my case that means connecting to NSApp, and I don't see how I would add properties to that. In fact, your suggestion of supposing that File's Owner has a property "wordList" confused me further and led me to read the entire thread posted recently on that topic. I sympathize with the original author's confusion, because as one participant pointed out, you don't generally "do" much with File's Owner unless you're building a second nib. (I've used it as a proxy for window controllers not so long ago, but never did much with the FO of the MainMenu.nib.) I see that (like someone else in the thread), I have also been mislead by some beginner code that instantiates part of the model in the nib when, as you point out, it's not generally a good idea. I was trying to do this in my "stupid bindings" app listed above.

Your confusion is understandable. I'm glad you referred back to that other thread, and you seem to have a good understanding of what you've read.


Hopefully, what I said just above clarifies the point. In the main nib it's true that you don't often change the class of File's Owner, and so it doesn't have any custom outlets, although the delegate outlet is useful.


Maybe if I talk my way through a description of my target "simple app with bindings" it will become clear what I don't understand.

MODEL: Word and WordList classes, where WordList instances have an array of words.
VIEW: Table that's going to display the one and only word list. (Not document-based, and I'm only trying to learn binding basics, so there will only be one word list.) Maybe some add and remove buttons. Eventually a detail view, but not yet.
CONTROLLER: This is what I thought would be the canned (Cocoa- provided) NSArrayController object to manage the array of words. *** BUT *** I am also confused, as some tutorials / examples use a separate (home-made) controller (e.g. MailApp demo) and others don't. *** Since I'm not doing anything fancy (just Add and Remove buttons) for now I don't see the need for a separate controller or even a delegate.

In this case, you have both. There's a mediating controller, which is the NSArrayController. There's also a coordinating controller MyAppDelegate, and it will be the delegate. At the moment, MyAppDelegate is little more than a container for your model. This probably looks pretty close to the case where the model is instantiated in the nib, which I warned against in the other thread. Two points about that: instantiating the application delegate in the main nib is a bit of a special case. In other nibs, the controller often lives outside of the nib and is its owner. Second, it is typical that a controller in MVC instantiates and holds the model, but that isn't quite the same as it being the model.


Eventually, as this basic example is fleshed out into a more real- world application, both the model and the controller will gain more complexity. At that point, it will become clearer to you that the controller is in the nib and has responsibilities for coordinating between the model and the view. Similarly, the model will be more complex and will more obviously _not_ be in the nib even if it is instantiated and held by the controller which is.


FILE's OWNER: NSApp (proxy) since I only need MainMenu.nib => in this case, FO is not a good place to put the wordList, since I've read you shouldn't subclass NSApp.

NSApp will run (setting up Event loop and other good magical things), and load the MainMenu.nib. Objects "freeze-dried" in the nib will be unarchived, and any connections (a bunch of outlets I don't usually use) may get handled automatically. This is where things get fuzzy for me. By now I should have an instantiated NSArrayController, an instantiated Window containing a View and Table. And I somehow need to hook this controller to my model. Thanks to the long File's Owner thread, I can visualize NSApp as loading the nib and (sort of) being the nib's owner or nib's loader.

But since I can't place wordList in NSApp, how am I going to connect to the WordList's ivar, wordList? I could create another controller that contains in ivar of type WordList, and then instantiate that in the nib. Are there better options?

Actually, I think you're doing pretty well. There are always other ways to do things, but you've described almost exactly what is normally done.




That is, this array controller is managing access to the words of the wordList of File's Owner.
Not my case.... But I'm trying to understand this scenario as well.

FO: A window controller that has outlets, one of which will be the table. This FO will also have an ivar "wordList". (Somehow) the table columns get bound to values in the word objects kept in the wordList. (The exact setup eludes me so far.)

In the scenario you're considering here, it would be:

*) NSArrayControler with "Class Name" set to Word. It's Content Array binding would be bound to File's Owner with Model Key Path "wordList.wordList". The first "wordList" is the property of the custom window controller as you just described, which I'm assuming is an instance of the WordList class. The second "wordList" references the property of the WordList class which is the to-many relationship to (array of) the Word objects.

*) A table column whose Value binding is bound to the NSArrayController, Controller Key "arrangedObjects", Model Key Path "characters". (Additional columns might use "reading" or "english" for their model key paths.)


In the scenario using MyAppDelegate, the NSArrayController's Content Array binding would be bound to MyAppDelegate but will otherwise be the same. Likewise, the table column bindings will be the same.



[Later addition: I think you were assuming a DocumentApp, which I haven't really messed with yet, but FO discussion helps me see how that too might be a good case for placing wordList in there.]

Nope, I wasn't discussing a document-based application.


Now, the table columns would be bound to the NSArrayController, with the Controller Key "arrangedObjects" and Model Key Path being whichever property of an individual Word instance should be presented in the column.
Given that wordList was simply declared as an NSMutableArray, how will bindings know that wordList contains words?

They won't and they won't care. Key-value coding, observing, and binding use strings to access object properties at run-time. The dynamic nature of Objective-C allows this lookup-by-string and doesn't require that the keys and key paths to be verifiable at build time.


That said, when you told IB that the Class Name for the NSArrayController was Word, you gave it sufficient information so that it could help you out by presenting model key path options to you. Note that the Model Key Path field in the bindings inspector is fully editable. Just because it's prompting you with some likely suggestions doesn't mean you're limited to those. You can type whatever you like there. If at run-time the key path can be resolved, it will be; otherwise, you'll get an error logged to the console.


Note that the table column bindings are pretty much unchanged from the case without the WordList class. This is a reflection of the encapsulation provided by the MVC design pattern and KVC/KVO/ bindings. If the model changes -- in this case, the array of words moving from being a direct property of File's Owner to being a property of a WordList object held by the File's Owner -- the controller which mediates between the view and the model may have to changed, but the view itself need not.
I actually think your example is easier to understand than my set up, since I can visualize a controller having an outlet (table) and an ivar (wordList), with these being bound to each other via the array controller. But I'm still going blank if File's Owner is NSApp.

I guess one thing is starting to be clear. You generally make two sets of bindings.
1. Bind some view object to the controller.
2. Bind the controller to some model object.


For (1), my table columns need to bind to something. My guess? The array controller. What "value" do I want returned by the the controller? The values returned by the "arrangedObjects" key. Which model key path? I'm not too sure. Logically, I'm thinking that column 1 should have a model keypath of wordList.word.<ivar1>. But I don't see how the controller even knows what wordList is, or where? Sure enough, this approach leads to an error.

That leads me to think I need a custom AppController containing a WordList ivar.

2008-06-05 11:17:15.986 Table Practice[265:10b] An uncaught exception was raised
2008-06-05 11:17:15.987 Table Practice[265:10b] [<AppController 0x13f820> addObserver:<NSArrayController 0x137c30> forKeyPath:@"words.<ivar1>" options:0x0 context:0x0] was sent to an object that is not KVC-compliant for the "words" property.
2

I think what you're missing is this: the Model Key Path is relative to the Controller Key which is relative to the bound-to object. (If the bound-to object is not a NSController-derived object, then Controller Key is not used.) So, when you tried to use "wordList.word.<ivar1>", you were being redundant. Binding to the arrangedObjects of the NSArrayController already got you to the words in the wordList. The Model Key Path is then relative to a Word instance, so it should just be the name of a property of a Word.



Oops.... This reminds me of something I read somewhere that certain methods need to be implemented for KVC to work with arrays. Here's their example:

Listing 6 Suppor ting mutableArrayValueForKey: for the to-many transactions proper ty
- (void)insertObject:(Transaction *)transaction
inTransactionsAtIndex:(unsigned int)index {
// implementation specific code
return;
}
- (void)removeObjectFromTransactionsAtIndex:(unsigned int)index {
// implementation specific code
return;
}


But what exactly would the implementation specific code look like? I'm guessing....
// insert
[[self wordList] insertObjectAtIndex: index];


// remove
[[self wordList] removeObjectAtIndex: index];

If so, I'm kind of surprised I need to add code. I thought I could hook it all up in IB.....

That's not strictly necessary. From here <http://developer.apple.com/ documentation/Cocoa/Conceptual/KeyValueCoding/Concepts/ Compliant.html> you can see that if -<key> returns an NSMutableArray, you're good to go. The methods you're thinking of are useful if you want to present a KVC-compliant interface for a to-many relationship, but that property isn't implemented in terms of an NSMutableArray.


By the way, you got the implementation of the insertion a bit wrong. You didn't pass the object to the array! It would be:

	[[self wordList] insertObject:transaction atIndex:index];


More importantly, could someone clarify the rhyme and reason for choosing when to:

- binding the content arrangedObjects versus
- binding the value (of table cells) to arrangedObjects.<ivar> versus
- binding the contentArray to selection.<ivars?>


Apple's Figure 13 of Cocoa Bindings Programming Topics takes a huge leap from the trivial case of two simple controls (slider and textfield) to a maze of connections between and among columns and controllers. I simply don't see when to choose among the various options, or what they stand for.

In many cases, there's only one binding to make. For example, a table column has a Value binding, but not a Content or Content Array binding. For an NSArrayController, there are Content Array, Content Array For Multiple Selection, Content Object, and Content Set bindings, which can be confusing. You'll need to look in the Cocoa Bindings Reference for the page on the NSArrayController bindings. The most common case is to bind Content Array.
OK, maybe this leads to a key concept I'm trying to grasp. When are you binding to values, and when to content?

Frankly, the terminology is fairly arbitrary. Or, at least, if there's a rationale for the distinction, it eludes me.


However, you really never need to figure it out. Of the classes in the frameworks which support bindings, they either use the "value(s)" or "content*" terminology but not both.


You suggested I look at the reference for NSArrayController with respect to contentArray. They say this:

contentArray
an indexed collection that specifies the content of the NSArrayController.
The indexed collection is an NSArray instance or subclass, a property that is accessible using the key-value-coding indexed accessor methods, or is accessible through mutableArrayValueForKey:.


But I'm still a little fuzzy here. In my example, wordList is the indexed collection.

Well, there's some confusion because you have a class named WordList. We have, at times, considered an ivar on something (File's Owner in one scenario, MyAppDelegate in another) called "wordList" which is a reference to an instance of WordList. However, you have also used the name "wordList" for the property of the WordList class which is the actual to-many relationship.

So, MyAppDelegate.wordList is not an indexed collection -- it's a WordList instance. On the other hand, MyAppDelegate.wordList.wordList is an indexed collection.

In my earlier email, I hypothesized (since you hadn't yet said) that the property name for the to-many relationship was "words". It's something of a convention that a to-many relationship is a plural. (See Apple's KVC documentation for examples of this convention.) In that case, the NSArrayController's Content Array would be bound to MyAppDelegate.wordList.words.
So I thought that should be it. But you said the object class should be Word because wordList is an array of words.

The Class Name assigned to an NSArrayController indicates the type of the _elements_ of the array being managed.

I'm obviously missing this key distinction (between value and content), because to me either of the following sentences make sense:

a) I want my array controller to reflect (bind to) the values in the table

b) I want my array controller to reflect (bind to) the contents of the table

(all with the understanding that "reflecting" is a loose term to mean a reciprocal and bidirectional communication so that changes on one side are reflected in the other)

Sure. The terminology seems roughly interchangeable to me, too. Don't worry about it.


Now, as to the question of what to bind to (arrangedObjects, arrangedObjects.key, selection.key, etc.), that depends on what you're trying to accomplish. If you want to access all of the objects in the array being managed by an array controller, you bind to that controller's "arrangedObjects" controller key.

In my case, I think the answer is yes. I want to display everything in the wordList => use arrangedObjects (?).


If you want to access a particular property of the objects in that array, you specify a model key path to that property.
In my case, the answer is again yes. I want to access specific values of those objects => use the model key path. This will be especially true once I'm ready to add the detail view. Again, this is a point where it seems fuzzy. To me, since my model objects are WordList and Word, my path should be for column 1 should be wordList.word.<ivar1>.

Hopefully, my explanation above cleared this up.

But InterfaceBuilder is offering "self" as a starting point. I'm not even sure who "self" is.

IB isn't necessarily all that smart. It has some keys that it knows about, but it doesn't necessarily know which is best for any given use. So, don't put too much stock in what IB "offer[s] as a starting point".


The NSArrayController or my separate controller containing an instance of WordList?

If you were to supply "self" as the Model Key Path, then it would be relative to the bound object and the Controller Key Path. When those two are NSArrayController and arrangedObjects, respectively, then self would be applied to the objects of the array (the arranged objects). So, you'd be binding to Word instances, themselves -- not a property of the Word objects.


Good luck in following all that.  :)

Cheers,
Ken
_______________________________________________

Cocoa-dev mailing list (email@hidden)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


  • Follow-Ups:
    • Re: totally confused by bindings settings
      • From: Daniel Child <email@hidden>
References: 
 >totally confused by bindings settings (From: Daniel Child <email@hidden>)
 >Re: totally confused by bindings settings (From: Ken Thomases <email@hidden>)

  • Prev by Date: Re: totally confused by bindings
  • Next by Date: Re: Cocoa n00b frustrations
  • Previous by thread: Re: totally confused by bindings settings
  • Next by thread: Re: totally confused by bindings settings
  • Index(es):
    • Date
    • Thread