On Mar 28, 2011, at 11:28 PM, Quincey Morris wrote:

> On Mar 28, 2011, at 20:05, Adam Ernst wrote:
> 
>> I have a class with an array property:
>> 
>> @interface Widget : NSObject {}
>> @property (retain, readonly) NSArray *gizmos;
>> @end
>> 
>> I make a mutable subclass. In addition to making the array property 
>> readwrite, I also make it an NSMutableArray for convenience:
>> 
>> @interface MutableWidget : Widget {}
>> @property (retain, readwrite) NSMutableArray *gizmos;
>> @end
>> 
>> This triggers a warning:
>> 
>> Property 'gizmos' type does not match super class 'Widget' property type
>> 
>> I can understand why this is being triggered. How can I work around it? I 
>> could keep it a straight NSArray, but this is cumbersome to edit. 
>> Suggestions on how to properly model this in my code?
> 
> The pattern I use is:
> 
>> @interface MutableWidget : Widget {}
>> @property (retain, readwrite) NSMutableArray *mutableGizmos;
>> @end

Do either of you really mean for that property to be read-write?

I can see making a property read-write with an immutable type, and I can see 
(although I don't particularly like) making a read-only property return a 
mutable type.  (Having properties of mutable type breaks encapsulation in a big 
way.)  But it seems strange to me to make a read-write property of mutable type.

Especially you, Quincey, when you've made a "sibling", mutable variant of the 
property.  Is there really a setMutableGizmos: method?  What does it mean?

Also, if you're using -mutableArrayValueForKey: without providing the mutation 
accessors, then that's probably asking for grief.  Actually, you said something 
I didn't follow about a setter for the gizmos property, but that's read-only.  
So, it's not clear to me how the proxy is supposed to mutate it (other than 
KVC's direct instance variable access, which I always disable).  And without 
either a proper setter or the mutation accessors, you've violated 
encapsulation.  Clients of your class can mutate your property without you 
being informed (unless you KVObserve your own property).

Here's what makes sense to me:

@interface Widget : NSObject {}
@property (retain, readonly) NSArray *gizmos;

// optionally, any of:
- (NSUInteger) countOfGizmos;
- (id) objectInGizmosAtIndex:(NSUInteger)index;
- (NSArray*) gizmosAtIndexes:(NSIndexSet*)indexes;
- (void) getGizmos:(Gizmo**)buffer range:(NSRange)inRange;

@end


@interface MutableWidget : Widget {}

// At least one of:
- (void) insertObject:(Gizmo*)gizmo inGizmosAtIndex:(NSUInteger)index;
- (void) insertGizmos:(NSArray *)gizmoArray atIndexes:(NSIndexSet *)indexes;

// At least one of:
- (void) removeObjectFromGizmosAtIndex:(NSUInteger)index;
- (void) removeGizmosAtIndexes:(NSIndexSet *)indexes;

// optionally, either of:
- (void) replaceObjectInGizmosAtIndex:(NSUInteger)index withObject:(id)anObject;
- (void) replaceGizmosAtIndexes:(NSIndexSet *)indexes withGizmos:(NSArray 
*)gizmoArray;

@end


Then, clients of MutableWidget can either directly use those mutation accessors 
or they can invoke [aMutableWidget mutableArrayValueForKey:@"gizmos"] and 
mutate that.  If you really want to wrap the latter in a convenience method, 
you can, but I don't think that constitutes a read-write property.

And I reiterate that one should almost always implement 
+(BOOL)accessInstanceVariablesDirectly { return NO; } on all one's classes.


If you're looking for guidance, I suggest contemplating what the framework 
classes do.  Can you think of any mutable class which changes the type of one 
of the base class's properties to be mutable?  Can you think of any which added 
a mutable variant of one of the base class's properties (as mutableGizmos is to 
gizmos)?  Well, frankly, can you think of any framework class that has a 
property of mutable type?!?

No.  Mutable subclasses should provide setters or mutation accessors.  That's 
the core of what they should add to the interface of the immutable super class.

Regards,
Ken

_______________________________________________

Cocoa-dev mailing list ([email protected])

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