(reposted for formatting) On Aug 15, 2014, at 16:24 , Daryle Walker <dary...@mac.com> 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 (Cocoa-dev@lists.apple.com) 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: https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com