views:

456

answers:

1

Dear all: In advance, thank you for your time.

Lately, I have decided to learn Objective-C (I am a long time C-hacker) and after reading the beautiful text by Kochan and diving into the Apple documentation I am still confused as to the best way to implement a recursive class (ie. a class in which an ivar has the type of the same class). For concreteness, let's assume we wish to implement a Binary Tree class. First we have a basic node class, which I have simplified to:

@interface MMNode : NSObject {
    NSString *label;
}


Now we can implement our tree in two different ways. The first (and what I consider the more obvious approach) is placing the recursion in the class itself.

@interface MMTree : NSObject {
    MMNode *root;
    MMTree *leftTree;
    MMTree *rightTree;
}
@property (nonatomic, copy) MMNode *root;
@property (nonatomic, retain) MMTree *leftTree;
@property (nonatomic, retain) MMTree *rightTree;


The second method, which is used in the wonderful CHDataStructures.framework, implements this data structure as follows:

typedef struct MMTreeNode {
    MMNode *node;
//  union { 
//      struct { 
            struct MMTreeNode *leftTree;
            struct MMTreeNode *rightTree;
//      };
//  };
} MMTreeNode;


@interface MMTreeStruct : NSObject {
    MMTreeNode *root;
}

Here the solution is more "pointer-riffic", with the recursion pushed into the structure. (As mentioned in the comments, the anonymous structures and unions are not required. However, since many applications will require additional information at each node, I will leave the code as is).


I have implemented both solutions and they work well. The former seems more straightforward, more "OO"; the latter, more "C-centric" with slightly, more complicated source.

Is the latter technique preferred? If so, what is an objective reason? All I can determine is maybe the latter is more memory friendly as the structure has a fixed size.

Again, thank you StackOverflow and thank you CocoaHeads.

UPDATE: I should add, it seems that the CoreFoundation object CFTree uses a similar structure implementation.

+4  A: 

As the author of CHDataStructures.framework, hopefully I can add a little insight. :-)

My rule of thumb is to use objects unless there is a demonstrable reason to use structs.

Since I'm implementing low-level data structures, I opted to use a struct instead of an object, primarily for performance reasons. Not only do objects require a little more memory per instance, but there is also some overhead for calling methods. This is mitigated if the object only has instance variables declared as @public, but you still have to alloc/init, and Objective-C object variables are zero-filled, whereas structs are not unless you use calloc().

One advantage that Objective-C objects have over structs is automatic integration with garbage collection (10.5+), whereas raw C memory has to jump through a few hoops to get the same benefit. I agree with you that memory management with objects is more familiar (and obvious) to Cocoa developers. That's why I use classes as the interface, and structs for the storage.

Note: The anonymous union and struct in your second code example are extraneous for this particular situation. I use them only to make it possible to write more streamlined yet readable binary search tree algorithms. (Details at http://dysart.cs.byu.edu/CHDataStructures/struct_c_h_binary_tree_node.html) I commented them out to hopefully avoid confusing casual readers, but they remain for future reference.

Quinn Taylor
SplittingField
A friend actually pointed out the post to me. :-) I don't have any hard numbers. Plan on an extra 4 bytes storage (on 32-bit) per object for the `isa` pointer. Function invocation requires a handful or two more of instructions than field access, and dynamic method dispatch adds still more, but I haven't run any concrete tests. In any case, either approach should certainly be adequate for use in an iPhone app. However, for truly heavy use cases, using structs starts to make more sense, especially since you don't have to worry about complexities of structs and GC on the iPhone. :-)
Quinn Taylor
You're correct, there are a number of situations in which an object can get released from under you. For properties, "retain" is correct under both GC and retain-release modes. (In many situations, "copy" is preferable, but probably not for a hierarchical data structure.) For allocating raw memory, use NSAllocateCollectable with the size and NSScannedOption so the GC will know to scan that memory for other GC-managed pointers; it's equally important to use the __strong attribute for GC-managed struct pointer variables so they remain rooted and GC doesn't collect them prematurely. :-)
Quinn Taylor
Thank you, Thank you, Thank you. Again, great work with CHDataStructures.
SplittingField