Swift - What's the Difference Between Metatype .Type and .Self

Swift - what's the difference between metatype .Type and .self?

Here is a quick example:

func printType<T>(of type: T.Type) {
// or you could do "\(T.self)" directly and
// replace `type` parameter with an underscore
print("\(type)")
}

printType(of: Int.self) // this should print Swift.Int


func printInstanceDescription<T>(of instance: T) {
print("\(instance)")
}

printInstanceDescription(of: 42) // this should print 42

Let's say that each entity is represented by two things:

  • Type: # entitiy name #

  • Metatype: # entity name # .Type

A metatype type refers to the type of any type, including class types, structure types, enumeration types, and protocol types.

Source.

You can quickly notice that this is recursive and there can by types like (((T.Type).Type).Type) and so on.

.Type returns an instance of a metatype.

There are two ways we can get an instance of a metatype:

  • Call .self on a concrete type like Int.self which will create a
    static metatype instance Int.Type.

  • Get the dynamic metatype instance from any instance through
    type(of: someInstance).

Dangerous area:

struct S {}
protocol P {}

print("\(type(of: S.self))") // S.Type
print("\(type(of: S.Type.self))") // S.Type.Type
print("\(type(of: P.self))") // P.Protocol
print("\(type(of: P.Type.self))") // P.Type.Protocol

.Protocol is yet another metatype which only exisits in context of protocols. That said, there is no way how we can express that we want only P.Type. This prevents all generic algorithms to work with protocol metatypes and can lead to runtime crashes.

For more curious people:

The type(of:) function is actually handled by the compiler because of the inconsistency .Protocol creates.

// This implementation is never used, since calls to `Swift.type(of:)` are
// resolved as a special case by the type checker.
public func type<T, Metatype>(of value: T) -> Metatype { ... }

Swift metatype (Type, self)

No, cls and cls2 are different things. The easiest way to understand the difference will be to extend your example like this:

class SomeClass {
class func doIt() {
print("I'm a class method. I belong to my type.")
}

func doItOnlyIfInstanceOfThisType() {
print("I'm a instance method. I belong to my type instance.")
}
}

And now let's take your cls:

let cls : SomeClass.Type = SomeClass.self
cls.doIt()

That will print I'm a class method. I belong to my type.. But you cannot invoke this:

cls.doItOnlyIfInstanceOfThisType() // causes a compilation error, PRO TIP: actually you can use this method as a func property, but I'll add the explanation for this later

Let's take your cls2. The only visible method of it is doItOnlyIfInstanceOfThisType because it's an instance method (of this type).

let cls2 : SomeClass = SomeClass()
cls2.doItOnlyIfInstanceOfThisType()

So the difference between them is that cls is a type and cls2 is an instance of this type.

A little bit more knowledge about why SomeClass.self and SomeClass()?

The type of a class also exists in memory (it has for example its own methods), as a singleton representing the Type (not an instance of this type - that's something different).
If you call self on a Type like this SomeClass.self you will get a singleton instance representing the SomeClass Type.

SomeClass() invokes the init() method of SomeClass, a constructor that creates an instance of SomeClass.

PRO Tip

You can manipulate a Type instance function (like closures/blocks in ObjC). It's a generated class method. But you must pass an instance of the type that you take this method from as an argument, like this:

let myFunc :()->() = cls.doItOnlyIfInstanceOfThisType(cls2)
myFunc()

Using type as a value, why is the self keyword required here?

Self-answering, with help of comments, I was able to find out the reason:


Using .self after the type name is called Postfix Self Expression:

A postfix self expression consists of an expression or the name of a
type, immediately followed by .self. It has the following forms:

expression.self
type.self

The first form evaluates to the value of the expression. For example, x.self evaluates to x.

The second form evaluates to the value of the type. Use this form to access a type as a value. For example, because SomeClass.self evaluates to the SomeClass type itself, you can pass it to a function or method that accepts a type-level argument.

Thus, the .self keyword is required to consider the type as a value capable of being passed as an argument to functions.

Something.self vs self.something in Swift

Consider code like

JSONDecoder().decode(what, from: myJSONData)

What goes where I have what? We have to tell the decoder what type of thing to expect to decode. Basically, what is the name of a type — the name of a class, struct, or enum that conforms to Decodable.

But how to say the name of a type? Let's suppose that the type of thing you expect to decode is String. Then what do you say here?

JSONDecoder().decode(String, from: myJSONData) // error

No, you can't just say the name of a type out of the blue like that. This is how you do it:

JSONDecoder().decode(String.self, from: myJSONData)

What you're really passing here when you say String.self is the metatype for String. And this example is exactly what it's for, i.e. when you need to pass a type as a parameter.

The declaration of this method signals this by using .Type:

func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable

The expression T.Type tells you that what you pass when you call this method should be Something.self.

Swift leave out .self to invoke a function which needs metatype?

I also ask it in apple official forum, and get an answer there:

This is a famous undocumented feature of Swift, since 1.0 (or former > betas, which I cannot confirm).

When calling a method or function with single type argument, you can omit > .self .
I guess this feature is included into Swift to make sizeof-like functions > neat, but not sure.

https://forums.developer.apple.com/thread/24980

what is T.Type in swift

  • T.Type is used in parameters and constraints to mean "the type of the thing itself, not an instance of the thing".

    For example:

    class Example {
    static var staticVar: String { return "Foo" }
    var instanceVar: String { return "Bar" }
    }

    func printVar(from example: Example) {
    print(example.instanceVar) // "Bar"
    print(example.staticVar) // Doesn't compile, _Instances_ of Example don't have the property "staticVar"
    }

    func printVar(from example: Example.Type) {
    print(example.instanceVar) // Doesn't compile, the _Type_ Example doesn't have the property "instanceVar"
    print(example.staticVar) // prints "Foo"
    }
  • You get a reference to a Type's .Type (the Type object itself) at runtime by calling TheType.self. The syntax TheType.Type is used in type declarations and type signatures only to indicate to the compiler the instance vs. type distinction. You can't actually get a reference to, for example, Int's type at runtime or in your function implementations by calling Int.Type. You would call Int.self

  • In the example code var someValue: Int, the specific notation identifier: Type (in this case, someValue: Int) means that someValue will be an instance of Int. If you wanted someValue to be a reference to the actual type Int, you would write var someValue: Int.Type = Int.self Remember that the .Type notation is only used when declaring types and type signatures to the compiler, and the .self property is used in actual code to retrieve a reference to the type object itself at execution time.

  • The reason why JSONDecoder().decode requires a parameter of T.Type (where T conforms to Decodable) is because any type conforming to Decodable has an initializer init(from decoder: Decoder). The decode method will need to call this init method on a type that conforms to Decodable, not on an instance of a type that conforms to Decodable. For example:

    var someString: String = ""
    someString.init(describing: 5) // Not possible, doesn't compile. Can't call an initializer on an _instance_ of String
    var someStringType: String.Type = String.self
    someStringType.init(describing: 5) // Iniitializes a String instance "5"

Swift - switch on metatypes

Solution:

switch type {
case is [String].Type :
return "storageKey"
default:
return "nothing"
}

What is self used for in Swift?

You will also use self a lot when creating your extensions, example:

extension Int {
func square() -> Int {
return self * self
}

// note: when adding mutating in front of it we don't need to specify the return type
// and instead of "return " whatever
// we have to use "self = " whatever

mutating func squareMe() {
self = self * self
}
}
let x = 3
let y = x.square()
println(x) // 3
printlx(y) // 9

now lets say you want to change the var result itself
you have to use the mutating func to make change itself

var z = 3

println(z) // 3

now lets mutate it

z.squareMe()

println(z) // 9

// now lets see another example using strings :

extension String {
func x(times:Int) -> String {
var result = ""
if times > 0 {
for index in 1...times{
result += self
}
return result
}
return ""
}

// note: when adding mutating in front of it we don't need to specify the return type
// and instead of "return " whatever
// we have to use "self = " whatever

mutating func replicateMe(times:Int){
if times > 1 {
let myString = self
for index in 1...times-1{
self = self + myString
}
} else {
if times != 1 {
self = ""
}
}
}
}


var myString1 = "Abc"
let myString2 = myString1.x(2)

println(myString1) // "Abc"
println(myString2) // "AbcAbc"

now lets change myString1

myString1.replicateMe(3)

println(myString1) // "AbcAbcAbc"


Related Topics



Leave a reply



Submit