Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Extension fails adding overloaded methods to Objective-C class

If a Swift extension is used to add overloaded methods to an Objective-C class, it appears to only call the first method, producing unexpected behaviour or a crash at runtime. An Extension on a Swift class (as opposed to Objective-C) works correctly. I would like to establish if there's any workarounds to this and confirm that this is a bug that I should report to Apple. Given that Objective-C does not support overloaded methods while Swift does, I can imagine that mixing the two is a recipe for problems.

The classes getting the extension are both empty except for the Objective-C class having an init function. ObjClass is declared within .h and .m files and imported using the Swift bridging header. The original SwiftClass is defined within the code sample along with the Swift extensions:

extension ObjClass {
    func log(param: Int32)    { NSLog("ObjClass->Int32: " + String(param)) }
    func log(param: String)   { NSLog("ObjClass->String: " + param) }
    func log(param: ObjClass) { NSLog("ObjClass->ObjClass") }
}

class SwiftClass {
}

extension SwiftClass {
    func log(param: Int32)    { NSLog("SwiftClass->Int32: " + String(param)) }
    func log(param: String)   { NSLog("SwiftClass->String: " + param) }
    func log(param: ObjClass) { NSLog("SwiftClass->ObjClass") }
}

Now call the overloaded methods with a string, int and object:

var objClass = ObjClass()
objClass.log(10)
objClass.log("string")
objClass.log(objClass)

var swiftClass = SwiftClass()
swiftClass.log(10)
swiftClass.log("string")
swiftClass.log(objClass)

For ObjClass, all the method calls are made to the first method with type Int32. Through use of COpaquePointer, I can see this is related to the object pointer but slightly different.

ObjClass->Int32: 10
ObjClass->Int32: 1881422800
ObjClass->Int32: 1879122512

Swift Extension on a Swift class works as expected:

SwiftClass->Int32: 10
SwiftClass->String: string
SwiftClass->ObjClass

Hopefully this can be supported at some point. Until then, I believe it should fail at compile time rather than give unexpected behaviour or a crash. Does anyone have a workaround to suggest for this?

I know it's calling the first method as if I swap the ObjClass extension methods, like so:

extension ObjClass {
    func log(param: String)   { NSLog("ObjClass->String: " + param) }
    func log(param: Int32)    { NSLog("ObjClass->Int32: " + String(param)) }
    func log(param: ObjClass) { NSLog("ObjClass->ObjClass") }
}

Then the call to objClass.log(str) works fine but the call to objClass.log(10) produces:

main(1): EXC_BAD_ACCESS (code=1, address=0xa)
like image 303
brunobowden Avatar asked Dec 30 '14 20:12

brunobowden


1 Answers

ObjC doesn't support method overloading. When you extend an ObjC class, the interface to your code is the ObjC runtime—even if you're calling your Swift code in that class from other Swift code—so your class' interface can't use features of Swift that aren't also present in ObjC.

As you've noted, this should probably be a compile error. I'd recommend filing a bug about that, especially since you've already put together a handy test case.

like image 184
rickster Avatar answered Sep 19 '22 21:09

rickster