Re: When would I use this (im)mutable property pair setup? (Misunderstanding advanced KVO)
Re: When would I use this (im)mutable property pair setup? (Misunderstanding advanced KVO)
- Subject: Re: When would I use this (im)mutable property pair setup? (Misunderstanding advanced KVO)
- From: Quincey Morris <email@hidden>
- Date: Fri, 15 Aug 2014 17:50:11 -0700
(reposted for formatting)
On Aug 15, 2014, at 16:24 , Daryle Walker <email@hidden> wrote:
> As I’m stripping out the NSDocument stuff, I realize that I need some sort of global object to hold each window(-controller) as I create them, otherwise ARC will purge them soon after creation. The application delegate seems like a good option, as it’s my only other custom class and Apple uses it to store the window in sample projects with a single window. I read a neat technique at <http://eschatologist.net/blog/?p=189>, and I’m wondering if I would ever need it and should switch to something easier.
It’s the right idea, but the wrong implementation.
> @property (nonatomic, copy) NSSet * windowControllers;
> @property (nonatomic, readonly) NSMutableSet * mutableWindowControllers;
Make the “windowControllers” property ‘readonly’ too. Although it’s feasible to allow the entire set to be changed via the setter, there are other ways to do this (e.g. removeAll + add), and you’re unlikely to need to do that anyway. The ‘copy’ property “pretends” that the implementation is something other than it is.
> - (NSSet *)windowControllers
> {
> return [NSSet setWithSet:_windowControllers];
> }
It’s simpler to ‘return _windowControllers;’. Or, better, use ‘@synthesize windowControllers = _mutableWindowControllers;'
Since the return value is type ‘NSSet*’, the client can’t use the pointer to mutate the collection. (You can typically assume the client won’t do something hacky, like cast the pointer back to a NSMutableSet*. If you can’t assume that, you’ve got bigger problems.)
> - (void)setWindowControllers:(NSSet *)windowControllers
> {
> [_windowControllers setSet:windowControllers];
> }
>
> You don’t need this, if you make the “windowControllers” property ‘readonly’, as I recommended above.
>
> - (void)addWindowControllersObject:(PrBrowserController *)controller
> {
> [_windowControllers addObject:controller];
> }
>
> - (void)addWindowControllers:(NSSet *)controllers
> {
> [_windowControllers unionSet:controllers];
> }
>
> - (void)removeWindowControllersObject:(PrBrowserController *)controller
> {
> [_windowControllers removeObject:controller];
> }
>
> - (void)removeWindowControllers:(NSSet *)controllers
> {
> [_windowControllers minusSet:controllers];
> }
These are correct, though you only need one of the ‘add…’ and one of the ‘remove…’ methods. Although it’s not wrong to implement both of each, it’s overkill for a set that’s not going to be very large. It’s typical to implement only the 1st and 3rd of these. (Keep in mind, you’re unlikely to want to add a *set* of window controllers at once.)
Note that these are the KVC “mutable unordered accessors” described here:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/AccessorConventions.html
This means it’s safe to expose these methods publicly. Or you can keep them private, and let clients use “mutableWindowControllers” for all their mutation needs. This is a matter of taste only.
> - (void)intersectWindowControllers:(NSSet *)controllers
> {
> [_windowControllers intersectSet:controllers];
> }
No, you don’t need this, or any other accessors. If you do everything else right, any client invocation of (say) ‘[myAppDelegate.mutableWindowControllers intersectSet: controllers];’ will get automatically translated into a combination of ‘add…’ and ‘remove…’ methods *and will be KVO-compliant*. Your code isn’t KVO compliant.
> @synthesize mutableWindowControllers = _windowControllers;
No, this is wrong and dangerous. You’re handing out a mutable reference to your private ivar, thus allowing clients to run rampant on your property’s integrity.
Instead, you should implement it like this:
- (NSMutableSet*) mutableWindowControllers {
return [self mutableSetValueForKey: “windowControllers”];
}
This returns a proxy object, so your underlying set stays private, and any mutation that a client applies to the proxy is actually applied to your underlying data via your accessors (the ‘add…’ and ‘remove…’ methods) *and* is KVO compliant.
Finally, my preference is to use a NSArray metaphor for this sort of thing, rather than a NSSet metaphor. Yes, the collection of window controllers is unordered, but NSSet uses ‘isEqual:’ equality rather than pointer equality to distinguish between members. By default, it’s the same thing, but the reliance on *not* having a customized ‘isEqual:’ for your window controller classes is a hidden fragility in your implementation.
Again, because you’re not going to have a very big collection of window controllers, performance and efficiency of the underlying (set vs array) representation is not a significant issue.
_______________________________________________
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