On Jun 10, 2008, at 11:28 AM, Charles Srstka wrote:

I think the problem is that if NSArray has +[NSArray array] returning an NSArray, then NSMutableArray has to return an NSArray also, since it can't have a different method signature for the same method. As a result, if you called +[NSMutableArray array], the compiler would think you were getting a regular, non-mutable NSArray, and you'd get a warning if you tried to do this, although the code would still work:

No, each class is allowed to define different types for the same method:

http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/chapter_9_section_2.html#/ /apple_ref/doc/uid/TP30001163-CH16-TPXREF161

This whole business about '(NSArray *) means NSArray AND any of it's subclasses' is the result of sloppy thinking and confusing 'able to' with 'as per spec'.

If you declare a method prototype as '-(NSArray *)resultsArray', then you have explicitly communicated that a NSArray is going to be returned. Not a NSMutableArray. Not 'Jimmies groovy array with red pin stripes'. A NSArray. Period. A NSMutableArray != a NSArray. If you're going to be returning (or accepting) more than a single class, you use id, which clearly communicates your intentions.

A return type of NSArray * means something very explicit. "But all you can expect is an object that behaves like a NSArray, so returning a subclass of NSArray, like NSMutableArray, is perfectly legal!" You're exactly right that all I can expect is an object that behave like a NSArray. /Exactly/ like a NSArray. Not sorta. Not almost. Exactly. Because that's what you explicitly stated. So when the returned array mutates, either because you were sloppy and returned the pointer to your objects internal state, or because during its travels something, somewhere sends it a mutation message inside a @try / @catch block, I'm going to file a bug because whatever it is you returned did not behave like a NSArray. An /immutable/ array.

Consider the following:

@interface MyObject : NSObject {
  double answer;
  int integerRing;
}
-(MyObject *)copyAndCombineWith:(MyObject *)object;
@end


-(MyObject *)copyAndCombineWith:(MyObject *)object
{
  MyObject *copy = malloc(sizeof(MyObject));
  memcpy(copy, object, sizeof(MyObject));
  copy->answer += answer;
  copy->integerRING += integerRing;
  return(copy);
}

Then you make a subclass:

@interface ASubclass : MyObject {
  NSArray *stuff;
  NSView *view;
}
@end

You inevitably do something like:

@interface ASubclass

-(void)doSomething {
  ASubclass *copy = [otherMyObject copyAndCombineWith:self];
}
@end

And things very obviously do not work. The reason is that doSomething assumes that the declaration of copyAndCombineWith:(MyObject *)object means "and any subclass". It doesn't. It means "MyObject *". I knew that when I wrote it. I was quite explicit with the prototype. The compiler obviously knew /exactly/ what I meant. The final clue is the error returned for the obvious misspelling:

test.m: In function '-[MyObject copyAndCombineWith:]':
test.m:24: error: 'struct MyObject' has no member named 'integerRING'

Statically typing a class in a prototype doesn't mean "and any subclass". It never did. The 'struct MyObject' is the tell tale evidence that it's not even possible for MyObject * to mean "and any subclass". When you statically type the class you mean it literally. The fact that sending a subclass of the declared base class behaves in a nearly indistinguishable manner is the result of careful manipulation and subtle bending of the rules, not because it's literally so. No amount of insisting that the illusion is real will ever turn it in to fact.
_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]

Reply via email to