(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