Override multiple overloaded init() methods in Swift
It would seem that your declarations (with override) should be sufficient, however, they seem to need the @objc declarations as well. This works:
class CustomHighlightTextFieldCell : NSTextFieldCell {
required init(coder aCoder: NSCoder!) {
super.init(coder: aCoder)
}
@objc(initImageCell:)
override init(imageCell anImage: NSImage!) {
super.init(imageCell: anImage)
}
@objc(initTextCell:)
override init(textCell aString: String!) {
super.init(textCell: aString)
}
}
Swift Overload init()
Updated Answer
class Y { }
class X : Y {
var title: String?
var imageName: String?
convenience override init() {
self.init(title: nil, imageName: nil)
}
convenience init(title:String?) {
self.init(title: title, imageName: nil)
}
init(title: String?, imageName: String?) {
self.title = title
self.imageName = imageName
super.init()
}
}
Use the most complete initializer as the designated initializer.
In this case
init(title: String?, imageName: String?)
is the only initializer that sets all its properties, so it should be the designated initializer.Initialize your properties before calling
super.init()
.My old answer only worked because
title
andimageName
were bothvar
and optional.In Two-Phase Initialization section of The Swift Programming Language: Initialization
Safety check 1
A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.
Old Answer
I plugged the sample into a playground, here is how I got it to work:
class Y { }
class X : Y {
var title: String?
var imageName: String?
override init() {
}
init(aTitle:String?) {
super.init()
self.title = aTitle
}
convenience init(aTitle:String?, aImageName:String?) {
self.init(aTitle: aTitle)
self.imageName = aImageName
}
}
init(aTitle:String?, aImageName:String?)
cannot callinit(aTitle:)
and still be a designated initializer, it must be a convenience initializer.self.init
must be before anything else ininit(aTitle:String?, aImageName:String?)
.- initializer must be passed the parameter name
self.init(aTitle)
must beself.init(aTitle: aTitle)
.
As a side note, I removed the unneeded semicolons and put super.init()
first for style reasons.
Hope that helps.
UPDATE
To follow Apple's advice, there should only be one designated, the rest should be convenience initializers. For example, if you decide init(aTitle:String?)
should be the designated initializer, then the code should look like:
convenience override init() {
self.init(aTitle: nil) // A convenience initializer calls the designated initializer.
}
init(aTitle:String?) {
super.init() // Only the designated initializer calls super.init.
self.title = aTitle
}
convenience init(aTitle:String?, aImageName:String?) {
self.init(aTitle: aTitle) // A convenience initializer calls the designated initializer.
self.imageName = aImageName
}
There are times when you might want more than once designated initializer, for example, UIView
, but that should be an exception, not the rule.
UPDATE 2
Classes should have one designated initializer. Convenience initializer will (eventually) call the the designated initializer.
Initialization
A class may have multiple initializers. This occurs when the initialization data can take varied forms or where certain initializers, as a matter of convenience, supply default values. In this case, one of the initialization methods is called the designated initializer, which takes the full complement of initialization parameters.
Multiple initializers
The Designated Initializer
The initializer of a class that takes the full complement of initialization parameters is usually the designated initializer. The designated initializer of a subclass must invoke the designated initializer of its superclass by sending a message to super. The convenience (or secondary) initializers—which can include init—do not call super. Instead they call (through a message to self) the initializer in the series with the next most parameters, supplying a default value for the parameter not passed into it. The final initializer in this series is the designated initializer.
How a class has several methods with the same name?
Others are calling this "overloading," but it isn't. Overloading is when functions are distinguished only by their types (and Swift supports this). That's not what is happening here. The difference between these functions is their names, just like in "normal" functions. Swift is similar to Objective-C (and Smalltalk, but unlike most languages) in how it names functions. The names of these functions are (following the ObjC tradition):
centralManager(_:didDiscover:advertisementData:rssi:)
centralManager(_:didConnect:)
centralManager(_:didDisconnectPeripheral:error:)
As you can see, these are three completely different names, and so are three different functions without anything complex like overloads getting involved.
In most languages, the names of the parameters are not part of the functions name. But in Smalltalk, ObjC, and Swift, they are. This is not like "named parameters" in Python. The name of the function includes these parameter names, in order, and easily distinguish one from another. If Swift hadn't carried this over from ObjC, it would have been extremely difficult to bridge the two languages as well as they are.
Note that this kind of function naming, where the "base" part of the function name is the name of the caller ("centralManager"), is from the ObjC/Cocoa tradition. While it's common in Swift because of Cocoa (i.e. iOS), it is not actually common in "pure Swift," which generally doesn't use the delegate pattern like this (you never see this in SwiftUI, for example). So what you're really seeing here is ObjC automatically transliterated into Swift.
But distinguishing Swift functions by their full (including parameters) name is extremely common and "Swifty." You see it most commonly in init
methods, but it happens all over.
Function overload with subclass in swift
Polymorphism is exactly how you get rid of the if
and is
that you so dread. That, in fact, is what polymorphism is. The problem is that you use the word "polymorphic" but you forgot to use polymorphism. In effect, there is no polymorphism in your code. Polymorphism means that each class or subclass acts for itself. Thus:
class A {
func who() -> String {return "A"}
}
class B : A {
override func who() -> String {return "B"}
}
class Example {
func action(_ param : A) -> String {
return param.who()
}
func test() -> String {
let t : A = B()
return self.action(t) // returns B
}
}
The alternative would be to say for example
return String(describing:type(of:param))
but I assume that you know that and that you count that as the same as saying is
and as
.
Overloading generic functions in iOS Swift
You haven't quite focussed on the actual issue. Let's eliminate everything irrelevant from the example. This compiles and works as expected:
struct TestFinder {
func doSomething<T,U>(_ function: (T,U) -> Void) -> Void {
print("two")
}
func doSomething<T,U,V>(_ function: (T,U,V) -> Void) -> Void {
print("three")
}
func doSomething<T,U,V,W>(_ function: (T,U,V,W) -> Void) -> Void {
print("four")
}
}
And here we'll test it:
func f(_ s1: String, _ s2: String, _ s3: String, _ s4: String) -> Void {}
TestFinder().doSomething(f) // "four"
But if you add the version with one passed function parameter, everything breaks down:
struct TestFinder {
func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
}
func doSomething<T,U>(_ function: (T,U) -> Void) -> Void {
print("two")
}
func doSomething<T,U,V>(_ function: (T,U,V) -> Void) -> Void {
print("three")
}
func doSomething<T,U,V,W>(_ function: (T,U,V,W) -> Void) -> Void {
print("four")
}
}
Now we can't compile, because the first version is seen as a candidate. And indeed, if we remove the other versions, we still compile!
struct TestFinder {
func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
}
}
That's the weird part. We still compile, even though we are saying:
func f(_ s1: String, _ s2: String, _ s3: String, _ s4: String) -> Void {}
TestFinder().doSomething(f)
Evidently, this function with four parameters is seen by the compiler as "fitting" the declaration with just one generic parameter.
I regard this as a bug. I think I can guess what might cause it; it could have to do with the legacy of function parameter list as tuples. This function f
is "equivalent" to a function taking a single parameter consisting of a four-string tuple. Nevertheless, you cannot actually call the function
inside doSomething
with a four-string tuple; I cannot find a way to call it at all.
So, I would say, regard this as a bug, and work around it for now by removing the first version of your generic.
UPDATE: On the advice of the Swift team, I tested with the May 4, 2020 Swift 5.3 Development toolchain. With it, your code compiles and behaves as expected. This was indeed a bug, and it was fixed as part of
https://bugs.swift.org/browse/SR-8563
Returning for a moment to my version, my code, too, compiles and behaves as expected, with all four versions of doSomething
present. However, note that if you delete all but the first version of doSomething
, it still compiles and runs. Moreover, you can call function
with four parameters by bundling them into a tuple and force casting, like this:
struct TestFinder2 {
func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
function(("manny", "moe", "jack", "henry") as! T)
}
}
That seems to confirm my guess that what you're seeing is a consequence of the hidden tuple-nature of a function's parameter list. One can draw the same conclusion from the discussion of the bug, which refers to "tuple-splatting".
Multiple Initializers in a single class (swift)
Here's how you can add another initializer to the same class that will use two parameters to create an Isosceles triangle:
class Triangle {
var sideA: Int
var sideB: Int
var sideC: Int
init(sideA: Int, sideB: Int, sideC: Int) {
self.sideA = sideA
self.sideB = sideB
self.sideC = sideA
}
init(sideA: Int, sideB: Int) {
self.sideA = sideA
self.sideB = sideB
self.sideC = sideB
}
convenience init(equilateralWithEdge edge:Int) {
self.init(sideA: edge, sideB: edge, sideC:edge)
}
}
Swift Extension fails adding overloaded methods to Objective-C class
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.
Overload Single Equals in swift
No dice, I’m afraid. From the language reference
The tokens =, ->, //, /*, */, ., the prefix operators <, &, and ?, the infix operator ?, and the postfix operators >, !, and ? are reserved. These tokens can’t be overloaded, nor can they be used as custom operators.
Instead, if you want ConvertibleUnit
to always be creatable from an Int
, give the protocol an init(_ val: Int)
method so people can write let unit = T(42)
. Or maybe even consider having it conform to IntegerLiteralConvertible
.
Generally, the preferred style in Swift is not to have automatic/implicit conversions between different types. This is why, for example, you must cast an Int
to a Double
before you can add it to another Double
.
Of course, you can write things like this:
func +(lhs: Int, rhs: Double) -> Double {
return Double(lhs) + rhs
}
let i = 1
let f = 1.2
i + f // = 2.2
but this is in general not considered good practice.
Related Topics
Avcapturevideopreviewlayer Is Not Visible on The Screenshot
Use of Nspathcontrol to Represent Virtual Path
Override Multiple Overloaded Init() Methods in Swift
Sidebar Menu for MACos in Swiftui
Swiftui Classes That Conforms Observableobject Should Be Singleton
Swift: +[Catransaction Synchronize] Called Within Transaction While Decoding HTML Entities
How to Restore Window Position in an Osx Application
How to Use a Protocol with Optional Class Methods in an Extension with Generic in Swift
How to Prevent Multiple Instances of the Same Window from Opening in MACos
Calling Stop() on Avaudioplayernode After Finished Playing Causes Crash
How to Add a Loading View for Apple Watch
Compound Key in Realm with Lazy Property
Passing Property Type as Parameter
How to Compare Cgpoints in Swift
Gmail API: How to Send Attachments to The Drafts on Swift