bug-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: alloc/init - copy/mutableCopy


From: James Knight
Subject: Re: alloc/init - copy/mutableCopy
Date: Tue, 18 Feb 2003 05:19:53 -0500

David Ayers wrote on Thu, 06 Feb 2003 22:57:40 +0100:
Richard Frith-Macdonald wrote:

Perhaps we should abandon apple compatibility and make the abstract methods implement shallow copies too?

I'm starting to think we should... Anyone writing custom collection classes that don't implement shallow copies, will be subject to breakage of code using copy/mutableCopy as soon as objects that don't implement NSCopying are contained in the array or the code otherwise relies on standard shallow copying behavior. It's not just an inconvenience for class cluster developers but for the "users" of class cluster objects when the semantics are undefined. It's just not that simple to say "the exact nature of the copy is determined by the class" when the API suggests copy/mutableCopy are to be used to transform between mutable an immutable versions. To not rely in shallow copies for copy/mutableCopy constrains the usefulness of these methods needlessly. I think GNUstep can take the lead here, and just maybe Apple will follow suit. But most likely not because we did it, but because hopefully Apple developers will kindly inform them of the > implecations.

Anyway, when we use our own classes explicitly (e.g. GC(Mutable)Array) I think it's perfectly fine to assume copy/mutableCopy to be shallow. We just need to be sure that any arrays passed to us are converted to our classes before we send copy/mutableCopy, which in GDL2 and GC(Mutable)Array should generally already be the case.

I concur with the above. Doing a deep copy in copy/mutableCopy seems very unlikely to ever be helpful. I suggest always doing a shallow copy, and putting prominantly in the API docs for NSArray/NSSet/NSDictionary that if you make your own subclasses, you must override copyWithZone to be portable to MacOSX, until such a point in time as Apple fixes their implementation as well.

I do consider this to be a bug on Apple's part. Accordingly, I reported it - it was assigned Bug #3175688. If anyone else wants to send them additional reasoning, sending an email to devbugs@apple.com referencing that bug number is probably the easiest way.

Apple bug report follows:

* SUMMARY
The abstract collection classes implement copyWithZone: to do a deep copy of the collection. This behavior is fairly non-sensical, as all the Cocoa built-in collections classes implement copyWithZone: to do a shallow copy. This has the effect of biting unsuspecting programmers in the ass when implementing their own subclass of NSArray.

* STEPS TO REPRODUCE
1. Run included test program.

* RESULTS
copyWithZone: and mutableCopyWithZone currently (breaking with past OpenStep behavior which was widely held as nonsensical) always do a shallow copy on all concrete classes provided with MacOSX. However, the abstract base classes have an implementation of copyWithZone which does a *deep* copy. It should do a shallow copy as all concrete subclasses do, so as to not provide unpleasent surprises to subclass implementors. Doing a deep copy is almost certainly not something that anyone expects to happen, as it is not mentioned in the documentation that this may occur.

This bug most likely appears in the abstract base class for NSArray, NSDictionary, and NSSet, although I only tested NSArray.

* REGRESSION
Doubtful, most likely simply inherited behavior from OpenStep.

* NOTES
Test program follows. This program demonstrates that a deep copy occurs in the abstract class of NSArray but not in the concrete subclasses. The output on my machine currently is:

=========== bugtest.m.output ============
Making new array
Cloning array

Making new mutable array
Cloning array

Making new custom singular array
Cloning array
BAD NEWS! Deep copy occured - Foo copyWithZone called!

=============== bugtest.m ================
#import <Foundation/Foundation.h>
@interface Foo : NSObject
@end

@implementation Foo
- copyWithZone: (NSZone *)zone
{
    puts("BAD NEWS! Deep copy occured - Foo copyWithZone called!");
    return self;
}
@end

@interface SingularArray: NSArray
{
    id obj;
}
- initWithObj: newobj;
@end
@implementation SingularArray
- initWithObj: newobj
{
    obj = newobj;
}
- (unsigned)count
{
    return 1;
}

- objectAtIndex: (unsigned)index
{
    if(index == 0)
        return obj;
    return nil;
}
@end
int main()
{
    NSArray *a;
    Foo *f = [[Foo alloc] init];
    puts("Making new array");
    a = [[NSArray alloc] initWithObjects: f, NULL];
    puts("Cloning array");
    [a copy];
    puts("");

    puts("Making new mutable array");
    a = [[NSMutableArray alloc] initWithObjects: f, NULL];
    puts("Cloning array");
    [a copy];
    puts("");

    puts("Making new custom singular array");
    a = [[SingularArray alloc] initWithObj: f];
    puts("Cloning array");
    [a copy];
    puts("");
}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]