Yuan type

A metatype is the type of a type. For example, if we say that 5 is an Int, then 5 is a value of Int. But if I ask how much memory an Int takes up, it doesn’t have to do with a particular value, it has to do with information about the type. If you are writing a function, return the size of the instance memory of a type. So the argument in this case is a type of data, and the type of data can be either directly specified like an Int, or it can be taken from a value, like the type of the value 5. The type data, in this case, is the type of a type, expressed in the term metaType.

The Type and the self

Meta types in Swift are represented by.type. For example, int. Type is the meta-type of Int. Types and values have different forms, like the relationship between Int and 5. The same is true of metatypes..type is a Type, and.self of the Type is the value of the mettype.

let intMetatype: Int.Type = Int.self
Copy the code

A metatype has only one corresponding value, so it is often written incorrectly:

 types.append(Int.Type)
 types.append(Int.self)
Copy the code

Int.Type is the name of a Type, not a value. Because you wouldn’t write:

 numbers.append(Int)
Copy the code

AnyClass

Once you get the metatype, you can access static variables and static methods. We use metatypes a lot, but sometimes Xcode helps us hide these details. For example, one method we often use for tableView:

func register(AnyClass? , forCellReuseIdentifier: String)

tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
Copy the code

AnyClass is a meta-type:

typealias AnyClass = AnyObject.Type
Copy the code

AnyClass is an alias for a meta-type of any type. When we access static variables, we also access them through metatype, but Xcode helps us omit.self. The following two ways are equivalent. I don’t think anyone would want to write self if it wasn’t confusing.

Int.max
Int.self.max
Copy the code

type(of:) vs .self

We mentioned earlier that we can get metatype values from both type(of:) and.self. So what’s the difference between these two approaches?

let instanceMetaType: String.Type = type(of: "string")
let staicMetaType: String.Type = String.self
Copy the code

.self fetches a static metatype, whatever type is declared. Type (of:) takes the runtime metatype, which is the type of this instance.

let myNum: Any = 1 
type(of: myNum) // Int.type
Copy the code

Protocol

Many people get the Protocol meta-type wrong. Protocol itself is not a type, and only an object that implements Protocol has a type object. So protocol. self is not equal to protocol. Type. If you write down the code below you will get an error:

protocol MyProtocol {}let metatype: MyProtocol.Type = MyProtocol.self
Copy the code

The correct understanding is that myprotocol. Type is also a valid metatype, so it needs to be a hosted Type metatype. So this is fine:

struct MyType: MyProtocol {}let metatype: MyProtocol.Type = MyType.self 
Copy the code

So what type is protocol. self? To satisfy your curiosity, Apple has created a category for you:

let protMetatype: MyProtocol.Protocol = MyProtocol.self
Copy the code

A practical

Let me give you an example just to familiarize you with the use of meta-types. Suppose we have two Cell classes and want a factory method that initializes objects by type. Here are two Cell classes:

protocol ContentCell {}class IntCell: UIView.ContentCell {
    required init(value: Int) {
        super.init(frame: CGRect.zero)
    }
    
    required init? (coder aDecoder:NSCoder) {
        fatalError("init(coder:) has not been implemented")}}class StringCell: UIView.ContentCell {
    required init(value: String) {
        super.init(frame: CGRect.zero)
    }
    
    required init? (coder aDecoder:NSCoder) {
        fatalError("init(coder:) has not been implemented")}}Copy the code

The implementation of the factory method looks like this:

func createCell(type: ContentCell.Type) -> ContentCell? {
    if let intCell = type as? IntCell.Type {
        return intCell.init(value: 5)}else if let stringCell = type as? StringCell.Type {
        return stringCell.init(value: "xx")}return nil
}

let intCell = createCell(type: IntCell.self)
Copy the code

Of course we can also use type inference, combined with generics:

func createCell<T: ContentCell>(a) -> T? {
    if let intCell = T.self as? IntCell.Type {
        return intCell.init(value: 5) as? T
    } else if let stringCell = T.self as? StringCell.Type {
        return stringCell.init(value: "xx") as? T
    }
    return nil
}

// Now infer the metatype to be used based on the return type
let stringCell: StringCell? = createCell()
Copy the code

The Dequeue for the tableView in Reusable uses a similar implementation:

func dequeueReusableCell<T: UITableViewCell>(for indexPath: IndexPath, cellType: T.Type = T.self) -> T
    where T: Reusable {
      guard let cell = self.dequeueReusableCell(withIdentifier: cellType.reuseIdentifier, for: indexPath) as? T else {
        fatalError("Failed to dequeue a cell")}return cell
  }
Copy the code

When dequeued, we can infer from the target type, no need to declare additional metatype:

 class MyCustomCell: UITableViewCell.Reusable 
tableView.register(cellType: MyCustomCell.self)

let cell: MyCustomCell = tableView.dequeueReusableCell(for: indexPath)
Copy the code

Reference

  • Whats Type And Self Swift Metatypes
  • ANYCLASS, metatype and.self

  • Weibo: @Zhuo without a story
  • If you want to communicate with me more closely, you can also join my knowledge planet: iOS Programmer Protection Society