0

Clarification: the question is not about mutable vs immutable but about a call to super creating an instance of the correct class. I hadn't thought of using [[self class] alloc] in the base class, and this seems to solve my issues. I'll accept that anser in the next few hours if nothing better comes up :)


The Apple spec says:

If a subclass inherits NSMutableCopying from its superclass and declares
additional instance variables, the subclass has to override mutableCopyWithZone:
to properly handle its own instance variables, invoking the superclass’s
implementation first.

That's very confusing. Consider

@interface Base : NSObject<NSMutableCopying>
@property (nonatomic, assign) NSInteger value ;
@end
@implementation Base
...
@end


@interface Derived : Base<NSMutableCopying>
@property (nonatomic, assign) NSInteger value2 ;
@end

@implementation Derived

- (id) mutableCopyWithZone: (NSZone *) zone {
    // Huh ???
    Derived * derived = [super mutableCopyWithZone: zone] ;
    ...
    // Huh ??????
    derived.value2 = self.value2 ;
    return derived ;
}
...
@end

I just do not understand how this code could possibly be correct if I follow the spec.

When the call to [super mutableCopyWithZone: zone] returns, I am expecting that the base class has only allocated enough room for its own ivars. There's no way it can tell that Derived instances need more room for its own ivars.

What is it that the documentation means, really? How should I implement this?

verec
  • 5,224
  • 5
  • 33
  • 40
  • According to the documentation, only classes that define an “immutable vs. mutable” distinction should adopt the NSMutableCopying protocol. Do you have both a `Derived` and a `MutableDerived` class? – Martin R May 29 '13 at 07:41
  • I think, that Derived is the mutable class and base is the immutable class. – Amin Negm-Awad May 29 '13 at 07:57
  • 1
    If Base were the immutable class then it would not adopt the NSMutableCopying protocol. – Martin R May 29 '13 at 08:10
  • @Martin: Well, then why are NSString, NSArray and NSDictionary, which are all immutable, all adopting the NSMutableCopying protocol? – Kaiserludi Nov 01 '13 at 15:36
  • @Kaiserludi: That's true. Much of what I said in this discussion might be wrong. - I also saw your question http://stackoverflow.com/q/19714985/1187415 and wanted to point you to this one, but could not find it! - This question seems to be a bit different from yours because `Derived` is not the "mutable variant" of `Base`. At least that is what I assume, the OP has not contributed anything to the discussion. – Martin R Nov 01 '13 at 15:50

1 Answers1

4

super only changes the way of dispatching (static instead of dynamic). But it does not change the receiver, esp. it does no upcast. In -mutableCopyWithZone: (super) self still points to an instance object of the derived class.

Trouble is possible, if the super method (or super super method and so on) does not implement the object creation in a proper way:

copy = [[BaseClass alloc] init]; // Wrong, an instance of the base class is created

copy = [[[self class] alloc] init]; // Correct, an instance of [self class] == DerivedClass is created

Using the second approach you get an instance of the derived class with full memory for its ivars.

Conclusion: Implement it in this way, if BaseClass implements its -mutableCopyWithZone: properly. Otherwise you have no other change then creating your own copy and init it.

In my books I always write, that there are little reason to use the class name inside a class. Using [self class] (in an instance method) and self (in a class method) are in 99 % the better approaches.

Amin Negm-Awad
  • 16,582
  • 3
  • 35
  • 50
  • 1
    But what if calling `mutableCopy` on a `Derived` object should return a `MutableDerived` object (compare my comment to the question), like NSString and NSMutableString? - I am not sure how that would work with subclassing. – Martin R May 29 '13 at 07:43
  • 1
    First: I do not think, that his question points to a special problem with mutableCopy, but to copy, too. (And any other situation, in which a instance has to be created by a method of base class (for example new, which is imlemented in NSObject, but creates instances of the receiver cass). Second: In your case an instance of Derived is created, because the message is sent to Derived and [[self class] alloc] (ImmutableBase) will create an instance of Derived. Everything is fine. Third: If -mutableCopy (without WithZone:) is sent to ImmutableBase, ImmutableBase "knows" about its subclass. – Amin Negm-Awad May 29 '13 at 07:53
  • 1
    Don't get me wrong: I think that your answer is correct for the NSCopying protocol. - What I meant is: sending `mutableCopy` to a `Derived` instance should return a `MutableDerived` object. How could the base class implementation handle that? - I wonder how NSMutableCopying and subclassing works together at all. (A "MutableDerived" class cannot be subclass of both "Derived" and "MutableBase" ... - but that is probably a different question :-) – Martin R May 29 '13 at 08:08
  • I do not understand your question, probably because I do not speak english very well. First of all we should clarify: There is a (immutable) class Derived. There is a class MutableDerived. (In my example derived was the mutable subclass.) There is an instance of Derived (immutable) and this gets the message mutableCopy. Is this correct? Implementing -mutableCopy on Derived this class has to know its mutable subclass, of course. So it can create an instance of it. I do not see the problem!? – Amin Negm-Awad May 29 '13 at 09:07
  • Ah, maybe you point to a situation as with NSContedSet: Base (NSSet) is immutable and implements -mutableCopy. Derived (NSMutableSet) is a mutable subclass, implementing -mutableCopy, too. DerivedDerived (NSCountedSet) is a mutable subclass again, implementing -mutableCopy. Is this the situation, you mentioned? – Amin Negm-Awad May 29 '13 at 09:12
  • 1
    The problem I have is: If the implementation of -mutableCopyWithZone on the Derived class calls the superclass method first, then -mutableCopyWithZone on the Base class has to allocate a *MutableDerived* object. But `[[self class] alloc]` would allocate a *Derived* object. - But perhaps this is all theoretical nonsense :-) – Martin R May 29 '13 at 09:12
  • If Derived is immutable (correct?), Base should not implement -mutableCopy, -mutableCopyWithZone:. This is nonsense for Base, because it has no mutable subclass. Therefore there is no -mutableCopyWithZone: in Base. – Amin Negm-Awad May 29 '13 at 09:14
  • In the question, both Base and Derived implement -mutableCopy, that is why I asked the OP if that is really what he needs. - NSCountedSet might be good example, I will think about that! – Martin R May 29 '13 at 09:18
  • Yes, but in his example, the "middle" class (Derived) is mutable. In your example, if I understand that correctly, Derived is immutable. – Amin Negm-Awad May 29 '13 at 09:20
  • Well, it is only my interpretation of the example in the question, and that might be wrong :-) - So I think we can leave it like that. But thanks for the discussion. – Martin R May 29 '13 at 09:31