51

Surely an ordered set is a more-specific case of a set, so why does NSOrderedSet inherit from NSObject rather than NSSet?

jhabbott
  • 18,461
  • 9
  • 58
  • 95
  • If developer.apple.com weren't down right now, I'd go take a look at the docs because now you've got me curious! Excellent question. And hopefully an Apple site won't stay down for too long, right? – WendiKidd Jul 01 '12 at 01:53
  • 2
    (Disclaimer: This is pure speculation, and too unconvincing for me to want to actually provide it as an answer, but...) An `NSOrderedSet` is a more-specific case of both an `NSSet` and an `NSArray`. Those are both classes not protocols, and there's no multiple inheritance in Objective-C. So, which do you pick? There's an argument for `NSSet` because of the name, but it could totally have been `NSUniqueArray`, right? Play it safe, ditch them both... – Kristian Glass Jul 01 '12 at 01:56
  • 1
    I see exactly what you're saying, but you're right that it's not all that convincing. I'd intuitively expect `[aSet intersectsSet:anOrderedSet]` to be possible (but it's not since the `NSOrderedSet` is not a subclass of `NSSet`), so `NSSet` makes a far more logical choice for a superclass than `NSArray` - and even if they made equally logical choices, losing the polymorphism makes choosing neither an inferior option anyway. – jhabbott Jul 01 '12 at 02:33
  • Good question. I had to think hard about this one for a while. – Tom Dalling Jul 01 '12 at 02:56

1 Answers1

77

I went through the interface of NSSet and you're right, ordered sets appear to satisfy the Liskov substitution principle and could therefor inherit from NSSet.

There is one little method that breaks this: mutableCopy. The return value of mutableCopy must be an NSMutableSet, but NSMutableOrderedSet should inherit from NSOrderedSet. You can't have both.

Let me explain with some code. First, let's look at the correct behaviour of NSSet and NSMutableSet:

NSSet* immutable = [NSSet set];
NSMutableSet* mutable = [immutable mutableCopy];

[mutable isKindOfClass:[NSSet class]]; // YES
[mutable isKindOfClass:[NSMutableSet class]]; // YES

Now, let's pretend NSOrderedSet inherits from NSSet, and NSMutableOrderedSet inherits from NSOrderedSet:

//Example 1
NSOrderedSet* immutable = [NSOrderedSet orderedSet];
NSMutableOrderedSet* mutable = [immutable mutableCopy];

[mutable isKindOfClass:[NSSet class]]; // YES
[mutable isKindOfClass:[NSMutableSet class]]; // NO (this is the problem)

What if NSMutableOrderedSet inherited from NSMutableSet instead? Then we get a different problem:

//Example 2
NSOrderedSet* immutable = [NSOrderedSet orderedSet];
NSMutableOrderedSet* mutable = [immutable mutableCopy];

[mutable isKindOfClass:[NSSet class]]; // YES
[mutable isKindOfClass:[NSMutableSet class]]; // YES
[mutable isKindOfClass:[NSOrderedSet class]]; // NO (this is a problem)

In Example 1, you wouldn't be able to pass an NSOrderedSet into a function expecting an NSSet because the behaviour is different. Basically, it's a backwards compatibility problem.

In Example 2, you can't pass an NSMutableOrderedSet into a function expecting an NSOrderedSet because the former doesn't inherit from the latter.

All of this is because NSMutableOrderedSet can't inherit from both NSMutableSet and NSOrderedSet because Objective-C doesn't have multiple inheritance. The way to get around this is to make protocols for NSMutableSet and NSOrderedSet, because then NSMutableOrderedSet can implement both protocols. I guess the Apple developers just though it was simpler without the extra protocols.

Tom Dalling
  • 23,305
  • 6
  • 62
  • 80
  • I think the protocols would make more sense - it could be more to do with the fact that the `NSMutableSet` was already defined and didn't already use a protocol for the mutability. Couldn't the protocol still be added despite the fact that `NSMutableSet` already conforms to it? – jhabbott Jul 01 '12 at 03:00
  • If they wanted to, they could add a new protocol without breaking anything. We might see it added in the future. – Tom Dalling Jul 01 '12 at 03:04
  • 3
    Great answer, this was used in NSHipster book, which is how I found this question... do you know why the NSHipster book links this issue directly to NSSet being a class cluster?, I simply don't see how if NSSet was a concrete class that this would be any different with the mutable copy issue, do you know? http://nshipster.com/nsorderedset/ – Oscar Gomez Mar 03 '14 at 17:30
  • 1
    @OscarGomez Oh cool, I didn't notice this in NSHipster. When they mention class clusters, I think they are specifically talking about how clusters (such as NSSet) often contain a mutable and immutable public class, which is the core of the problem. I don't think they're implying that private cluster subclasses affect the issue. – Tom Dalling Mar 04 '14 at 01:37
  • @TomDalling Thanks, I guess that is what he is referring to. – Oscar Gomez Mar 04 '14 at 02:57