大刘的 iOS 自学笔记

Swift原类型

2022-06-28  本文已影响0人  大刘

Created by 大刘 liuxing8807@126.com

参考:
SwiftRocks
Apple
Apple

什么是元类型

元类型,即类型的类型type of type,描述类型的元数据类型. 看一个普通的示例:

struct SwiftRocks {
    static let author = "Bruno Rocha"
    func postArticle(name: String) {}
}

let blog: SwiftRocks = SwiftRocks()

blog, 即SwiftRocks()是一个实例,SwiftRocks本身是对这个实例的描述:type of an instance
blog可以调用实例方法postArticle, 但是无法访问类属性author,如何访问类属性author?
可以通过:SwiftRocks.author
也可以:type(of: blog).author
type(of:)方法的原型为:public func type<T, Metatype>(of value: T) -> Metatype
type(of: blog)可以调用SwiftRocks所有的类方法,说明type(of: blog)方法通过实例blog得到了某个东西可以访问所有的类属性,这个东西是什么呢?
不妨打印下type(of: blog)是什么:

let something = type(of: blog)
print(something) // SwiftRocks

这里type(of: blog)打印的结果是SwiftRocks, 其实,这里应该是SwiftRocks.Type, 只是打印出的东西显示为SwiftRocks,来看:

print(SwiftRocks.self) // SwiftRocks.self, SwiftRocks.self返回的是SwiftRocks.Type, 但print打它打印成了SwiftRocks
if type(of: blog) == SwiftRocks.self {
    print("Equal") // Equal, 可以看到,SwiftRocks.self即:SwiftRocks.Type, 所以type(of: blog)即SwiftRocks.Type
}

type(of: blog)SwiftRocks.self是相同的,它们返回的都是SwiftRocksMetaType, 即元类型,即SwiftRocks.TypeSwiftRocks的原类型metatype.

可以理解为,对于 let blog: SwiftRocks = SwiftRocks(), blog是实例,SwiftRocks是对这个实例的描述,即SwiftRocks是实例blogType, 而SwiftRocks本身的typeSwiftRocks.Type:

Type instance
SwiftRocks blog, 即SwiftRocks()对象
SwiftRocks.Type SwiftRocks.self 或 typeof(blog)

You can define the metatype of any type, including classes, structs, enums and protocols as being the name of that type followed by .Type. In short, while SwiftRocks refers to the type of an instance (which only lets you use instance properties), the metatype SwiftRocks.Type refers to the type of class itself, which lets you use the SwiftRocks's class properties. "type of a type" makes a lot more sense now, right?

对元类型的引用可以访问类的所有属性和方法,包括init()方法:

let something = type(of: blog) // something的类型是元类型SwiftRocks.Type
let object = something.init()  // 可以通过元类型的引用调用类方法,调用init()方法得到实例
object.postArticle(name: "haha")

可以通过元类型访问类属性和类方法是很有用的,比如我们可以把原类型作为参数传递,以新建UITableViewCell为例:

class MyCell: UITableViewCell {
    ...
}
func createCustomCell<T: UITableViewCell>(ofType: T.Type) -> T {
 let cell = T.init()
 // ...
 return cell
}

createCustomCell(ofType: MyCell.self)

Metatypes也可以用在判断是否相等的比较中:

class Animal {
    public func think() {
        print("Animal think")
    }
}
class Cat: Animal {
    public override func think() {
        print("Cat think")
    }
}
class Dog: Animal {
    public override func think() {
        print("Dog think")
    }
}
class Person: Animal {
    public override func think() {
        print("Person deep think")
    }
}
func create<T: Animal>(animalType: T.Type) -> T {
    switch animalType {
    case is Cat.Type:
        print("It's cat")
        return Cat() as! T
    case is Dog.Type:
        print("It's dog")
        return Dog() as! T
    case is Person.Type:
        print("It's person")
        return Person() as! T
    default:
        break
    }
    return Animal() as! T
}
create(animalType: Person.self).think()
let p: Person = Person()
create(animalType: type(of: p)).think()

type(of:) Dynamic Metatypes vs .self Static Metatypes

在上面的示例中:

let blog: SwiftRocks = SwiftRocks()
let something = type(of: blog)

type(of: blog)返回的就是SwiftRocks的元类型,即SwiftRocks.Type, 如果没有对象,只有类SwiftRocks本身,可以通过SwiftRocks.self返回它的原类型。typeof(of: blog)和SwiftRocks.self是一个东西。

再举个示例:
String是 type, "Hello World" 是 value, String.Type是 type, String.self是 value.

let personMetatype: Person.Type = Person.self
create(animalType: personMetatype).think()

func create<T: Animal>(animalType: T.Type) -> T {
    ...
}

.self被Apple称为static metatype, 这是一个在编译期就可以确定的类型。我们平常调用比如SwiftRocks.author其实相当于SwiftRocks.self.author

Static metatypes 在Swift中无处不在, 当每次访问类属性或类方法时,你都在隐式的使用它们。
在tableView的注册cell方法中就有一个AnyClass:

open func register(_ cellClass: AnyClass?, forCellReuseIdentifier identifier: String)

我们查看AnyClass的源码发现:

public typealias AnyClass = AnyObject.Type

AnyClass只是任意对象的元类型的别名而已。

.self返回的是编译期就可以确定的static metatype, type(of: anObject)返回的是运行时确定的dynamic metatype

Protocol Metatypes

上面所说的同样适用于协议protocols, 但是有一点区别,比如下面代码就无法工作:

protocol MyProtocol {
}

let metatype: MyProtocol.Type = MyProtocol.self // compile error: Cannot convert value of type 'MyProtocol.Protocol' to specified type 'MyProtocol.Type'

The reason for that is that in the context of protocols, MyProtocol.Type doesn't refer to the protocol's own metatype, but the metatype of whatever type is inheriting that protocol. Apple calls this an existential metatype.

即:在protocol的上下文中,MyProtocol.Type并不是指向protocol自己的metatype, 而是指向实现protocol协议的其他Type:

protocol MyProtocol {
}
struct MyType: MyProtocol {
}
let metatype: MyProtocol.Type = MyType.self // OK
上一篇下一篇

猜你喜欢

热点阅读