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
?
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
?
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.
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 { ... }
.dynamicType
. This one is to get the last level type of your instance at runtime. Lets say you have this: let instance : UIView = CustomUIView()
then you will call instance.dynamicType
somewhere and then you'll get CustomUIView
and not UIView
. ;) –
Ashlieashlin struct A {}
then A.self
will return A
. –
Ashlieashlin .Type
is not magic, it says that you will pass/have a metatype for a specific type and not it's instance. .self
is there to get this metatype. It's kinda hard to explain with all these type suffixes everywhere. :D –
Ashlieashlin .Type
for parameters and .self
for arguments? (https://mcmap.net/q/53110/-what-39-s-the-difference-between-an-argument-and-a-parameter) –
Sapless A.self
returns A.Type
and the .Type
would then return the type of A which in the end would be A
? –
Lucio First and foremost see Apple docs on type(of:)
The function's 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
.
Normally a function which requires a type, is a function that instantiates objects for you. I can think of a few examples:
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.
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
func popBackTo<T>(type: T.Type, in nav: UINavigationController? = nil, completion: ((T?) -> Void)? = nil) {
let nav = window?.rootViewController as? UINavigationController
guard let nav = nav, let destinationVC = nav.viewControllers.first(where: { $0 is T }) else {
return
}
nav.popToViewController(destinationVC, animated: true)
}
# Example call site:
popBackTo(LoginVC.self)
More about the internals and how it works:
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
isSomeClass.Type
and the metatype of the protocolSomeProtocol
isSomeProtocol.Protocol
.
From Apple : metaType Type
Under the hood AnyClass
is
typealias AnyClass = AnyObject.Type // which is why you see T.Type
Basically wherever 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)
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()
/* | |
compile time runtime
| |
To extract, use: .self type(of)
Check the runtime type of someInstance using `type(of:)`: */
print(type(of: someInstance) == SubClass.self) // True
print(type(of: someInstance) == BaseClass.self) // False
/* Check the compile time type of someInstance using `is`: */
print(someInstance is SubClass) // True
print(someInstance is BaseClass) // True
I highly recommend to read Apple documentation on Types. Also see here
tableView.register(Class, forCellReuseIdentifier: "CustomTableViewCell")
is what a novice programmer would try instead of Class.self
to pass a type into a function? Why is Class.self
needed when Class
seems syntactically simpler? –
Manslayer Table.Type
is used for when a function expects a type, not an instance. Table
is used when a function expects an instance of that type. Respectfully Table.self
and Table()
satisfy such functions expectations. These 4 distinct purposes hence you can’t do what you say –
Randell Table.self
, why not just Table
, as it seems simpler and can also be possible. I don't see the purpose of the type as a value
phenomenon. –
Manslayer Table
on its own would just signify a 'return type', rather than be usable as a value, so using type as a value
is important. Thanks –
Manslayer Class
, and my previous comment hopes to answer this. It seems like if swift had a smarter compiler it could be possible to use Class
in place of Class.Type
and Class.self
, and it uses the syntax to determine which what you meant. –
Manslayer 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.
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 { ... }
.dynamicType
. This one is to get the last level type of your instance at runtime. Lets say you have this: let instance : UIView = CustomUIView()
then you will call instance.dynamicType
somewhere and then you'll get CustomUIView
and not UIView
. ;) –
Ashlieashlin struct A {}
then A.self
will return A
. –
Ashlieashlin .Type
is not magic, it says that you will pass/have a metatype for a specific type and not it's instance. .self
is there to get this metatype. It's kinda hard to explain with all these type suffixes everywhere. :D –
Ashlieashlin .Type
for parameters and .self
for arguments? (https://mcmap.net/q/53110/-what-39-s-the-difference-between-an-argument-and-a-parameter) –
Sapless A.self
returns A.Type
and the .Type
would then return the type of A which in the end would be A
? –
Lucio 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.
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.
Class.self
is to Class.Type
as "Hello World\n"
is to String
. –
Dimercaprol Metatype <>.Type
Metatype
is a type which allows you to access to parts of Class and Struct[About] type(not instance) like:
//instance.self -> instance
let x1: SomeClass = SomeClass().self
//SomeType.self -> SomeType.Type //Metatype
let x2: SomeClass.Type = SomeClass.self
//SomeType.Type.self -> SomeType.Type //Metatype
let x3: SomeClass.Type = SomeClass.self.self
let var1: String = HelloWorld
let var2: String.Type = HelloWorld.self
Some experiments:
class SomeClass {
required init() { }
class func classFunc() { }
static func staticFunc() { }
func instanceFunc() { }
}
class SubClass: SomeClass { }
//work with instance
let a1: SomeClass = SomeClass()
let a2: SomeClass = a1
let a3: SomeClass = a1.self
assert(a1 === a2 && a1 === a3)
//static. metatype by Type(class name) <Class_Name>.self <Structure_Name>.self
let c1: SomeClass.Type = SomeClass.self
//dynamic. metatype by Instance
let c2: SomeClass.Type = type(of: a1)
assert(c1 == c2)
//access to init
let d0: SomeClass.Type = SomeClass.self.self
let d1: SomeClass = d0.init() //SomeClass.Type.self.init
let d2: SomeClass = c1.init()
assert(d1 !== d2)
//call func
SomeClass.classFunc()
c1.classFunc()
SomeClass.staticFunc()
c1.staticFunc()
// c1.foo3() //Error: Instance member 'foo3' cannot be used on type 'SomeClass'
//work with subclass, class and struct
// <class_name>.Type allows to save class and subclass
var e1: SomeClass.Type = SomeClass.self //class
assert("SomeClass" == "\(e1)")
e1 = SubClass.self //sub class
assert("SubClass" == "\(e1)")
//Any.Type allows to work with class and struct
var e2: Any.Type = SomeClass.self //class
e2 = String.self //struct
//AnyObject.Type allows to work only with class
var e3: AnyObject.Type = SomeClass.self //class
e3 = NSString.self //class
let f1: SomeClass = SubClass()
assert("SubClass" == stringOf(instance: f1))
get String
let typeString = "\(SomeType.Type)" //"SomeType"
//or
func stringOf<T>(instance: T) -> String {
let result = String(describing: type(of: instance))
return result
}
stringOf(instance: SomeType()) //"SomeType"
© 2022 - 2024 — McMap. All rights reserved.