-1

I've been trying to implement a protocol and protocol extension that provides a default == method in swift. Something like this:

protocol NameProtocol: Equatable {
    func getName() -> String
}

extension NameProtocol{}

func ==<T: NameProtocol>(lhs: T, rhs: T) -> Bool{
    return lhs.getName() == rhs.getName()
}

Then a class like this:

class NamedObject: NSObject, NameProtocol {

    let name: String

    init(name: String) {
        self.name = name
        super.init()
    }

    func getName() -> String {
        return self.name
    }

}

However, the custom == method is never called:

 let one = NamedObject(name: "Name")
 let two = NamedObject(name: "Name")
 if one == two {
     NSLog("EQUAL")
 }
 else{
     NSLog("NOT EQUAL")
 }

Am I doing something wrong here?

UPDATE:

From the answers I got it looks like what i'm trying to accomplish isn't really possible. The only way to come close is to subclass (which has its obvious drawbacks). I'm going to keep a lookout for a proper solution.

john_ryan
  • 1,557
  • 1
  • 16
  • 34

2 Answers2

2

Because the == operator from the superclass takes precedence over that of the protocol. And for NSObject, == means pointer equal.

If you remove the inheritance from NSObject, it works as expected:

class NamedObject: NameProtocol {

    let name: String

    init(name: String) {
        self.name = name
        super.init()
    }

    func getName() -> String {
        return self.name
    }

}

I can't find any documentation on the order of precedence when there are multiple implementations for ==. This is just my experience.


Edit: instead of defining == for the protocol, define your own base class to override NSObject's default behavior:

class MyBaseClass: NSObject {
    func getName() -> String {
        fatalError("You must override this method")
    }
}

func == (lhs: MyBaseClass, rhs: MyBaseClass) -> Bool {
    return lhs.getName() == rhs.getName()
}

class NamedObject: MyBaseClass {
    let name: String

    init(name: String) {
        self.name = name
    }

    override func getName() -> String {
        return self.name
    }
}
Code Different
  • 90,614
  • 16
  • 144
  • 163
  • Is there any way to change that behavior? I don't really want to lose the NSObject superclass methods. I'd basically like to provide a default implementation of isEqual. – john_ryan Jun 21 '16 at 03:10
  • Create your own base class to override NSObject's behavior. See my edited answer – Code Different Jun 21 '16 at 03:18
  • that works, I was hoping to come up with a generic protocol that could be applied anywhere though, for instance i could apply it to a UIView or a regular NSObject – john_ryan Jun 21 '16 at 03:33
0

Your class derives from NSObject. You override isEqual: (not Swift ==) to change an NSObject-derived class's idea of what instance equality means.

Your strategy to move this off to a protocol extension, however, can never work because Objective-C knows nothing of Swift protocol extensions. Your code sitting in the protocol extension is completely invisible to Objective-C.

So, basically, by making this class derive from NSObject, you've shot your protocol extension strategy in the foot.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • For an example of how to do it, see my answer here: http://stackoverflow.com/a/37900408/341994 – matt Jun 21 '16 at 03:22
  • adding func isEqual(object: AnyObject?) -> Bool to the protocol extension doesn't get called either – john_ryan Jun 21 '16 at 03:27
  • yea overriding the method works, but i was trying to make a protocol so that any object that conformed to the protocol could use the same isEqual without having to subclass – john_ryan Jun 21 '16 at 03:32
  • "adding func isEqual(object: AnyObject?) -> Bool to the protocol extension doesn't get called either" Of course not, because _nothing_ you do in a Swift protocol extension is going to get called by Objective-C. Objective-C cannot see Swift protocol extensions or anything in them. — That point has been made on Stack Overflow many, many times (many of them by me). See for example my answer here: http://stackoverflow.com/questions/35809253/swift-using-protocol-extension-results-in-unrecognized-selector-sent-to-instan Added that point to my answer. – matt Jun 21 '16 at 04:23