In swift, an instance func
can't call a static/class func
without prefixing the method call with the class name. OR you can use type(of: self)
, e.g
class Foo {
static func doIt() { }
func callIt() {
Foo.doIt() // This works
type(of: self).doIt() // Or this
doIt() // This doesn't compile (unresolved identifier)
}
}
My question is, what's the difference here? Is it just a matter of coding style, or is there some difference e.g. static or dynamic dispatch going on?
If it is just coding style, what's the preferred style?
There are two main differences.
self
inside the static methodThe metatype that you call the static method on is available to you in the method as self
(it's simply passed as an implicit parameter). Therefore if you call doIt()
on type(of: self)
, self
will be the dynamic metatype of the instance. If you call it on Foo
, self
will be Foo.self
.
class Foo {
static func doIt() {
print("hey I'm of type \(self)")
}
func callDoItOnDynamicType() {
type(of: self).doIt() // call on the dynamic metatype of the instance.
}
func classDoItOnFoo() {
Foo.doIt() // call on the metatype Foo.self.
}
}
class Bar : Foo {}
let f: Foo = Bar()
f.callDoItOnDynamicType() // hey I'm of type Bar
f.classDoItOnFoo() // hey I'm of type Foo
This difference can be really important for factory methods, as it determines the type of instance you create.
class Foo {
required init() {}
static func create() -> Self {
return self.init()
}
func createDynamic() -> Foo {
return type(of: self).create()
}
func createFoo() -> Foo {
return Foo.create()
}
}
class Bar : Foo {}
let f: Foo = Bar()
print(f.createDynamic()) // Bar
print(f.createFoo()) // Foo
(Martin has already covered this, but I thought I would add it for the sake of completion.)
For class
methods that are overridden in subclasses, the value of the metatype that you call the method on determines which implementation to call.
If called on a metatype that is known at compile time (e.g Foo.doIt()
), Swift is able to statically dispatch the call. However, if you call the method on a metatype that isn't known until runtime (e.g type(of: self)
), the method call will be dynamically dispatched to the correct implementation for the metatype value.
class Foo {
class func doIt() {
print("Foo's doIt")
}
func callDoItOnDynamicType() {
type(of: self).doIt() // the call to doIt() will be dynamically dispatched.
}
func classDoItOnFoo() {
Foo.doIt() // will be statically dispatched.
}
}
class Bar : Foo {
override class func doIt() {
print("Bar's doIt")
}
}
let f: Foo = Bar()
f.callDoItOnDynamicType() // Bar's doIt
f.classDoItOnFoo() // Foo's doIt