Re: Making an array property without a backing store. How?
Re: Making an array property without a backing store. How?
- Subject: Re: Making an array property without a backing store. How?
- From: Quincey Morris <email@hidden>
- Date: Tue, 07 Apr 2015 18:55:15 +0000
On Apr 7, 2015, at 10:58 , Daryle Walker <email@hidden> wrote:
> @interface MyClass : NSObject
> @property (readonly) NSArray * myDatumList;
> @property NSArray * myDataList;
> @end
>
> @implementation MyClass
>
> - (NSUInteger)countOfMyDatumList {
> return self.myDataList.count;
> }
>
> - (id)objectInMyDatumListAtIndex:(NSUInteger)index {
> NSDictionary * const dict = self.myDataList[index];
> return dict[@“datum”];
> }
> @end
The problem is that this approach doesn’t actually work, not in this form. There’s a little bit of Doing It Wrong™, but mostly this is pretty badly broken in Cocoa.
Your custom accessors are *never* used, except in two circumstances:
1. If you expose them publicly, and a client of MyClass invokes them directly. This is fine, except that clients don’t get access via all the other NSArray methods for free.
2. Clients access the “myDatumList” property via a *proxy* object which is obtained as follows:
[someMyClassInstance valueForKey: @“myDatumList”]
Using the returned proxy object as a NSArray, they can then use any of the standard NSArray methods (including ordinary ‘count’, ‘objectAtIndex:’ and all the others) on this proxy, and the calls will be translated into some combination of your custom accessors.
This is fine, too, except that you’ve lost the ability for clients of the class to write:
myClassInstance.myDatumList.count
etc. It would *seem* you could solve this by giving MyClass a simple getter:
- (NSArray*) myDatumList {
return [self valueForKey: @“myDatumList”];
}
but if you try that, you’ll find it causes an infinite loop. *By API contract* (look in NSKeyValueCoding.h for the gory details), under these circumstances ‘valueForKey’ will call the ‘myDatumList’ method — hence the loop.
(In your case, without an implementation of the “myDatumList” getter, because you’ve declared a “myDatumList” property and declared it @dynamic, you’d just end up crashing, because ‘valueForKey’ would try to call a method that doesn’t exist. Or, if you let it be @synthesized, ‘valueForKey’ would call a method that always returns nil.)
There’s no direct solution to this. You either can’t use the custom NSArray accessors, or clients of the class have to remember to us the crummy ‘valueForKey’ approach and you have to get rid of the “myDatumList” property. The indirect solution is to implement your own proxy object. It’s doable, and I’ve done it in the past, but it makes your head hurt.
My suggestion, therefore, is to take one of the following approaches:
— If clients of the class access “myDatumList” in limited ways, just expose the custom accessors directly, and don’t have the @property at all.
— Have the “myDataList” property be cleverer, and let it maintain a parallel array (_myDatumList). This is just an array, therefore, without custom accessors, so the “myDatumList” property will work fine.
Finally, if on top of all of this you want the “myDatumList” property to be KVO compliant — if you want to KVO observe it — there’s yet another level of complication because there’s a different proxy involved:
[<self-or-someMyClassInstance > mutableArrayValueForKey: @“myDatumList”]
and/or you must provide KVO compliance manually when updating the backing store NSArrays. (And if you think this proxy avoids the looping problem of the non-mutable proxy — it doesn’t.)
Sorry to ruin your day. :)
_______________________________________________
Cocoa-dev mailing list (email@hidden)
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:
This email sent to email@hidden