Swift - what's the difference between metatype .Type and .self?
SwiftSwift Problem Overview
What's the difference between metatype .Type
and .self
in Swift?
Do .self
and .Type
return a struct
?
I understand that .self
can be used to check with dynamicType
. How do you use .Type
?
Swift Solutions
Solution 1 - Swift
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 likeInt.self
which will create a static metatype instanceInt.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 { ... }
Solution 2 - Swift
First and foremost see Apple docs on type(of:)
The functions signature is interesting:
func type<T, Metatype>(of value: T) -> Metatype
Where is it used?
If you are writing/creating a function that accepts a type e.g. UIView.Type
, not an instance e.g. UIView()
then to you would write T.Type
as the type of the parameter. What it expects as a parameter can be: String.self
, CustomTableView.self
, someOtherClass.self
.
But why would a function ever need a type?
Normally a function which requires a type, is a function that instantiates objects for you. I can think of two good examples:
- register function from tableview
tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "CustomTableViewCell")
Notice that you passed CustomTableViewCell.self
. If later on you try to dequeue a tableView of type CustomTableViewCell
but didn't register CustomTableViewCell
type then it would crash because the tableView hasn't dequeued/instantiated any tableviewcells of CustomTableViewCell
type.
- decode function from JSONDecoder. Example is from the link
struct GroceryProduct: Codable {
var name: String
var points: Int
var description: String?
}
let json = """
{
"name": "Durian",
"points": 600,
"description": "A fruit with a distinctive scent."
}
""".data(using: .utf8)!
let decoder = JSONDecoder()
let product = try decoder.decode(GroceryProduct.self, from: json)
print(product.name)
Notice try decoder.decode(GroceryProduct.self, from: json)
. Because you passed GroceryProduct.self
it knows that it needs to instantiate an object of type GroceryProduct
. If it can't then it would throw an error. For more on JSONDecoder
see this well written answer
- As an alternate workaround for where types are needed see the following question: Swift can't infer generic type when generic type is being passed through a parameter. The accepted answer offers an intersting alternative.
More about the internals and how it works:
.Type
> The metatype of a class, structure, or enumeration type is the name of
> that type followed by .Type. The metatype of a protocol type—not the
> concrete type that conforms to the protocol at runtime—is the name of
> that protocol followed by .Protocol. For example, the metatype of the
> class type SomeClass
is SomeClass.Type
and the metatype of the
> protocol SomeProtocol
is SomeProtocol.Protocol
.
> From Apple : metaType Type
Under the hood AnyClass
is
typealias AnyClass = AnyObject.Type // which is why you see T.Type
Basically where ever you see AnyClass
, Any.Type
, AnyObject.Type
, its because it's in need of a type. A very very common place we see it is when we want to register a class for our tableView using register
func.
func register(_ cellClass: Swift.AnyClass?, forCellReuseIdentifier identifier: String)
If you are confused as to what does 'Swift.' do then above, then see the comments from here
The above could have also been written as:
func register(_ cellClass: AnyObject.Type, forCellReuseIdentifier identifier: String)
.self
> You can use the postfix self expression to access a type as a value.
> For example, SomeClass.self returns SomeClass itself, not an instance
> of SomeClass. And SomeProtocol.self returns SomeProtocol itself, not
> an instance of a type that conforms to SomeProtocol at runtime. You
> can use a type(of:)
expression with an instance of a type to access
> that instance’s dynamic, runtime type as a value, as the following
> example shows:
> From Apple : metaType Type
Playground code:
Easy example
struct Something {
var x = 5
}
let a = Something()
type(of:a) == Something.self // true
Hard example
class BaseClass {
class func printClassName() {
print("BaseClass")
}
}
class SubClass: BaseClass {
override class func printClassName() {
print("SubClass")
}
}
let someInstance: BaseClass = SubClass()
/* | |
compileTime Runtime
| |
To extract, use: .self type(of)
Check the runtime type of someInstance use `type(of:)`: */
print(type(of: someInstance) == SubClass.self) // True
print(type(of: someInstance) == BaseClass.self) // False
/* Check the compile time type of someInstance use `is`: */
print(someInstance is SubClass) // True
print(someInstance is BaseClass) // True
I highly recommend to read Apple documentation on Types. Also see here
Solution 3 - Swift
They appear in different places syntactically.
In a place syntactically where you have to specify a type, Something.Type
is a valid type, corresponding to the type that is the metatype (which is metaclass for classes) of Something
. Something.self
is not a valid syntax for a type.
In a place syntactically where you have to write an expression, Something.self
is a valid expression. It's an expression of type Something.Type
, and the value is the thing ("class object" in the case of classes) that represents the type Something
. Something.Type
is not a valid expression syntax.
Solution 4 - Swift
This was one of those topics that confused the hell out of me today.
I was writing a generic function:
func foo<T: Protocol>(ofType: T.Type) {
T.bar()
}
And tried calling it as follows:
foo(ofType: ClassImplementingProtocol.Type) // Compiler error
Spent about 30 min researching why it wasn't working. Then I tried this:
foo(ofType: ClassImplementingProtocol.self) // Works
Turns out Xcode's code completion is very bad at showing the difference between meta types and types... From the code completion pop-up it looks like .self and .Type are the same thing:
But the "explain like im 5" of it is, when you have a method parameter of Class.Type, it is expecting an instance of Class.Type.
Class.self returns an instance of Class.Type, whereas Class.Type is referring to Class.Type...
Very unclear if you ask me.
Solution 5 - Swift
Metatype .Type
Metatype
is a type which allows you to access to parts of Class and Struct[About] type(not instance) like initializers class and static[About] properties and methods
let var1: String = HelloWorld
let var2: String.Type = HelloWorld.self
Some experiments:
class SomeClass {
required init() { }
class func foo1() { }
static func foo2() { }
func foo3() { }
}
class SomeSubClass: SomeClass { }
let a1: SomeClass = SomeClass()
let a2: SomeClass = a1
let a3: SomeClass = a1.self
SomeClass.self.foo1() //class func
SomeClass.foo1() //class func
//static. metatype by type(class name) <class_name/structure_name>.self
let c1: SomeClass.Type = SomeClass.self
//dynamic. metatype by instance
let c2: SomeClass.Type = type(of: a1)
//access to type's init, class, static throught Metatype
let d1: SomeClass = c1.self.init()
let d2: SomeClass = c1.init()
//call
c1.foo1() //class func
c1.foo2() //static func
// c1.foo3() //instance func. Instance member 'foo3' cannot be used on type 'SomeClass'
// c1.foo3(SomeClass()) //Expression resolves to an unused function
//Sub
// <class_name>.Type allows to save class and sunclass
var e1: SomeClass.Type = SomeClass.self //class
e1 = SomeSubClass.self //sub class
//Any.Type allows to save class and struct
var e2: Any.Type = SomeClass.self //class
e2 = String.self //struct
//AnyObject.Type allows to save only class
var e3: AnyObject.Type = SomeClass.self //class
e3 = NSString.self //class
get String
let typeString = "\(SomeType.Type)"
func register<T>(instance: T) {
instanceString = String(describing: type(of: instance))
}