PDA

View Full Version : NSTableViews, NSOutlineViews, id


Feanor
2002.06.04, 02:13 PM
Wassah!!

So for all the searching I'm doing in Apple's docs and Cocoa Programming, I can't figure something. Consider this NSObject method:

-(id)valueForKey:(NSString *)attrName;

This returns an id, although it is meant to work for any instance variable. But what if the variable you're requesting is a primitive type? It works fine, but what is going on? Aaron Hillegasse's RaiseMan example program uses this to return values to a TableView, which sets the value of either a name or an expected raise, using

-(id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex

How is a float acceptable in place of an id? What if it was a byte? Is the type coercion that clever? Apparently so, but I just wondered if someone had some insight. What if you used a double -- isn't that 64 bits long, vs a pointer's 32 bits?

How does the TableView know that it's getting a float? If you send it an object, I have to assume it uses the object's description method to get a string value to put into the target Cell. But you can't send a -description message to a primitive type. That would be BAD.

OK, I have a guess. -valueForKey: is actually wrapping the primitive instance value into a suitable NSNumber, and that's being passed around. So the class object must have methods which enable introspection on the kinds of instances it has to enable wrapping it in the correct object *if required*. If that's true, are they documented somewhere?

Brent

jefftkd
2002.06.04, 02:46 PM
id is only used for objects. It is not an "all inclusive type". As a general rule of thumb, id is the root object for all other objects. Imagine if somewhere you saw:

@interface id {}
@end

@interface NSObject : id < /* protocols */ >
{ /* data */ }
/* methods */
@endNow, does "id" make more sense? Okay, let's continue onto the problem of returning primitive values: use NSValue or NSData based on what you want to do.

For example, if we have an NSDictionary (hash table) filled with numbers, we might do:


- (int)intOfKey: (id)key {
NSValue *value;
int integer;

if ((value = [dict objectForKey:key]) != nil) {
[value getValue:&integer];
return integer;
}

return 0;
}Similar methods can be used with NSData (or NSNumber if you know you want to use floats, ints, etc.).

I prefer NSValue, because it is fast and allows comparison of more detailed information.

HTH,
Jeff :cool:

Feanor
2002.06.04, 03:18 PM
id is only used for objects.
Well, that fact I understand, being that "id" is the same as "NSObject *". I have no problem with the definition of id.

I'm not sure you got the gist of my post. I'm not trying to *do* anything; I'm trying to understand how something works *behind the scenes*.

My object has a float instance variable. With -valueForKey: I am magically returning an object, but I don't know what kind of object it is (I guess it's an NSValue or NSDecimalNumber). That's OK, I'm not using the object, NSTableView is. Once it has that object, it can ask it for its -class or get its -description or whatever.

*BUT*, how does -valueForKey: know what type my instance variable is?

I found the Key Value Coding programming topic on Apple's website so I expect to find the answers there. I didn't find it before because I couldn't see the relevant methods on the NSObject class page -- it only lists the methods implemented as part of the NSObject protocol.

And the answer is:

These methods are not restricted to accessing only properties that represent objects. Scalar values (such as int or float) can be accessed as well. Their values are just passed using NSNumber objects, instead. The key-value coding methods automatically detect the scalar instance variables and convert them to and from NSNumber objects as needed.


Sometimes it's just hard to figure out where in the docs things are, and then I remember to just do a "find" in Project Builder.

The other thing I was having a hard time understanding I now understand, which is that, while an NSOutlineView maintains refs to the objects displayed, it never talks to them, it only sends them back to you with requests for data to be displayed. But I can't find anything in the docs that there is a right or wrong kind of object to send back. If I sent it the object itself, I wonder what it would do with it... I'm sure it's safe to use NSStrings and NSNumbers, though.

Brent

OneSadCookie
2002.06.04, 03:32 PM
Originally posted by Feanor
Well, that fact I understand, being that "id" is the same as "NSObject *". I have no problem with the definition of id.

Actually, the two types are not the same.

If you have an NSObject*, you're saying that all you're willing to guarantee is that it's a subclass of NSObject. If you try to send a message to an NSObject*, the compiler will complain unless NSObject declares that method.

If you have an id, you're not even guaranteeing that it's a subclass of NSObject -- maybe it's a subclass of a different root class. You're just saying that you don't know what the type is. You can send any message to an id without the compiler complaining.

jefftkd
2002.06.04, 04:25 PM
I think I understand your question a little better now, Feanor. Basically, there is a typedef of object somewhere that looks something like this:
typedef struct {
unsigned _isa;
/* more stuff */
} _object; Now, every single object compiled (NSObject, etc.) has that structure.

The _object structure is a header (if you will) for any object. However, the methods for a class are located in memory elsewhere. This memory address is where the _isa value points to.

When you pass "id" back and forth, you are passing a pointer to a location in memory where an _object structure is located. That can then be used to determine what exactly in fact the object is, what methods is responds to, etc.

Many of these things can be tested:

// One of these will be true
sizeof(id) = 4 // or sizeof(void*)

Make a dummy object:

@interface Foo { int x; }
@end

Now, try this:

Foo *foo = [Foo alloc];
NSLog("%d",foo);
NSLog("%d",&foo->x);

&foo->x - (unsigned)foo = sizeof(_object);

To clarify: _object is just the name of a structure that I invented -- the real structure name is something different -- but the concept is the same.

HTH,
Jeff :cool: