Objective-C - Effectively subclassing clusters of Cocoa classes

I have an object that was once an NSMutableSet, but it needed a few more files. The obvious (and obviously not supported) thing is to subclass NSMutableSet and glue to two additional properties. Since NSMutableSet, like all Cocoa data structures, is a cluster of classes, I cannot subclass it in the usual way, since the superclass is just throwing exceptions. This led me to several paths.

The first way was to create a kind of composite object that declared itself to be a subclass of NSMutableSet, but actually just forwarded calls to the internal NSMutableSet. I didn't want to implement every method in NSMutableSet, so I thought it forwardInvocation:

would be a good way to accomplish my mission. Unfortunately, the abstract NSMutableSet class implements all the interface methods, and their implementations throw exceptions, so I never got to the point where I could redirect the call.

The second way was to subclass NSProxy and call forwarding. This solution does not justify my needing to copy the NSMutableSet interface unless there is a way to declare "this class implements this interface", which I am not aware of (it may well be a solution).

The third way was to create a category in the NSMutableSet and import it only for the class that needs to use it, but this is not a good idea since you cannot add non-dynamic properties via a category. This led me to use related objects in a category. I'm willing to admit that this is the right solution for this use case, but I wish it weren't as awkward as it was. This is doubly awkward, since the properties I add are primitive, so I'll have to wrap and unwrap them when setting up and getting the association (unless there is a way to associate primitives I'm not familiar with).

Basically, I would like this to be functional as a subclass of NSMutableSet (and all class clusters), but cannot find an optimal approach. Thank!

+3


source to share


4 answers


For completeness, take a look at your first path:

create a class of a composite object declaring itself a subclass NSMutableSet

, but really just redirecting calls to internalNSMutableSet

Can you subclass NSMutableSet

? Yes, but isn't it? The documentation for NSMutableSet

states:

Subclass notes

There should be no need for a subclass. If you need to customize behavior, it is often better to consider composition instead of subclassing.

So, weigh that up, and if you want to subclass it again, refer to the documentation:

Overriding methods

In a subclass, you must override both of its primitive methods:

addObject:

removeObject:

You must also override the primitive methods of the class NSSet

.



And looking at the class's documentation NSSet

, we find its primitive methods:

Overriding methods

In a subclass, you must override all of its primitive methods:

count

member:

objectEnumerator

What is it, 5 methods.

You can define your own class as a subclass NSMutableSet

, add an instance variable that is an instance NSMutableSet

, implement 5 methods and redirect them to a collection instance, add any init methods you want, and then add additional properties.

If performance is a concern, then the tradeoff is to redirect these five methods and access related objects for your additional properties. You will need to profile in order to work, but then and only if performance becomes an issue.

+1


source


Trying to subclass the Clusters of Cocoa's classes will just create an awful lot of pain. This may sound like a good idea, but you will run into problems forever.



Just create NSObject with NSMutableSet as the first member object.

+2


source


The subclassification of the Cocoa class cluster is somewhat discouraged. Not without reason. Please do not enter this cool world.

All your solutions will work. I have successfully used the first path with NSArray

and NSDictionary

, so I believe it should work fine for NSMutableSet

. Just remember that you need to override not only forwardInvocation:

but several other methods as well. Please refer to the Surrogate Objects sections of the Apple documentation:

Although redirection inherits inheritance, the NSObject class never mixes the two. Methods such as responsesToSelector: and isKindOfClass: look only at the inheritance hierarchy, never in the redirect chain.

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html

In my case, I have overridden:

  • conformsToProtocol:

  • isKindOfClass:

  • isMemberOfClass:

  • respondsToSelector:

  • instancesRespondToSelector:

  • forwardInvocation:

  • methodSignatureForSelector:

  • instanceMethodSignatureForSelector:

of which isKindOfClass:

, conformsToProtocol:

and respondsToSelector:

are critical.

I've also used the third route with good results, but I admit that the related object APIs are clunky.

+2


source


First, gnasher729 is correct. Don't put clusters of subclass classes. Just don't do it. You can do that? If I tell you that you can't, would it help you convince yourself that you shouldn't? I can lie if it helps you make the right choice.

But in all seriousness, it almost always makes no sense. Is your subclass really a specific set? Or does it really look like a set. Let's consider NSAttributedString

. It's not some kind of string, it has a string. This is almost always better.

And also clusters of clusters are a royal pain for subclassing.

However, adding bound values ​​to the data structure, as you already discovered, is overall just fine, because what you really want is "hey, I have some data that needs to be combined with this other data." The wrapping has become so easy that it shouldn't really get in your way. See fooobar.com/questions/1464052 / ... :

objc_setAssociatedObject(self, animatingKey, @(value), OBJC_ASSOCIATION_RETAIN_NONATOMIC);

      

And with "one weird trick" , you can do it very simply:

@interface NSObject (BoolVal)
@property (nonatomic, readwrite, assign) BOOL boolVal;
@end

@implementation NSObject (BoolVal)

- (BOOL)boolVal {
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}

- (void)setBoolVal:(BOOL)value {
    objc_setAssociatedObject(self, @selector(boolVal), @(value), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

      

But I'll still come back to the question of whether it really is a set (and not as a set), and whether it needs to respond to every message that can be sent to the set. As is the case NSAttributedString

, your actual needs are often much less than in practice, and packaging the multiple methods you need is often worth the simplicity and control.

+2


source







All Articles