Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift calling static methods: type(of: self) vs explicit class name

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?

like image 791
Orion Edwards Avatar asked Feb 15 '17 21:02

Orion Edwards


People also ask

Can static methods be called with self?

You can call static methods on self , because self is an instance of the class that has the static method, and thus has the method. You can also call static methods on classes directly, because a static method doesn't require an object instance as a first argument - which is the point of the static method.

Can you use a class name to call a static method?

Static methods have keyword "static" before the method name, belong to the class and not the instance, and can be accessed through the class name.

Can self be used to refer to static methods Swift?

When self is accessed in a type method ( static func or class func ), it refers to the actual type (rather than an instance). Swift allows to omit self when you want to access instances properties.

How do you call a static method in Swift?

The 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.


2 Answers

There are two main differences.

1. The value of self inside the static method

The 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 

2. The dispatching of the static method

(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 
like image 114
Hamish Avatar answered Sep 29 '22 15:09

Hamish


For a class method it makes a difference if the method is overridden in a subclass:

class Foo {     class func doIt() {         print("Foo doit")     }      func callViaClassname() {         Foo.doIt()     }      func callViaTypeOf() {         type(of: self).doIt()     } }  class Bar: Foo {     override class func doIt() {         print("Bar doit")     }  }  Bar().callViaClassname() // Foo doit Bar().callViaTypeOf() // Bar doit 

This is also documented in "Types" in the Swift Language Reference:

You can use a type(of:) expression with an instance of a type to access that instance’s dynamic, runtime type as a value, ...

I don't know a difference for a static method (which is final and cannot be overridden in a subclass). Correction: See Hamish's answer for the difference in both static and class methods.

like image 25
Martin R Avatar answered Sep 29 '22 15:09

Martin R