Swift 元类型和.self

作者: YYYYYY25 | 来源:发表于2018-06-27 16:48 被阅读883次

    一、前言

    也许 AnyAnyObject 你并不陌生,但是你不一定熟悉 AnyClass

    1.1 Any 和 AnyObject

    AnyObject 可以代表任何 class 类型的实例
    Any 可以表示任意类型,甚至包括方法(func)类型
    概念不多做介绍了,可以参考:王巍 Any和AnyObject

    类型判断和应用:

    class Boy {
    }
    
    class Girl {
    }
    
    func enter(_ child: AnyObject) {
        // 方法1
        switch child {
        case let boy as Boy:
            print("boy")
        case let girl as Girl:
            print("girl")
        default:
            break
        }
        
        // 方法2
        if child is Boy {
            print("boy")
        }
        if child is Girl {
            print("girl")
        }
    }
    
    enter(Boy())
    

    二、什么是 AnyClass(元类型)

    2.1 概念

    通过 AnyObject.Type 这种方式得到的就是一个元类型(Meta),也就是 AnyClass。在 Swift 中被一个 typealias 定义:

    typealias AnyClass = AnyObject.Type
    

    比如下面代码中,Meta.Type 代表 Meta 这个类型的类型,也就是一个用来存储 Meta 类型的元类型。然后用 .self 从 Meta 中取出其类型:

    class Meta {
        var title = ""
        required init(name: String) {
            self.title = name
        }
        
        func method1() {
            print(title)
        }
        
        class func method2() {
            print("class method")
        }
    }
    
    let typeMeta : Meta.Type = Meta.self
    

    ps: .self 用在类型后面取得类型本身,用在实例后面取得实例本身

    2.2 类方法调用

    这样做的第一个好处就是可以通过元类型直接调用 类方法,上面代码中 Meta 中声明了一个实例方法(method1)和一个类方法(method2):

    let typeMeta1 : Meta.Type = Meta.self
    typeMeta1.method2()
    
    let typeMeta2: AnyClass = Meta.self
    (typeMeta2 as! Meta.Type).method2()
    
    2.3 实例方法调用

    顾名思义,实例方法的调用要先声明实例,直接通过 init 方法获取实例固然可以,但是我们这里介绍如果使用元类型获取实例,甚至调用实例方法:

    let typeMeta : Meta.Type = Meta.self
    typeMeta.method1(Meta(name: "new meta"))()
    // print: new meta
    

    *ps:上面代码中 typeMeta 是 Meta.Type 类型,通过它调用实例方法 method1需要传入一个实例变量,然后系统会返回这个实例方法本身。如果上面第二行代码不好理解,看这里 ⬇️

    let f = typeMeta.method1(Meta(name: "new meta"))
    f()
    

    还有一种写法:

    let meta = typeMeta.init(name: "type init")
    meta.method1()
    

    *ps:上面代码中 typeMeta 是 Meta.Type 类型,通过它调用初始化方法创建实例,然后通过实例去调用实例方法。

    这里就是问题所在了,调用实例方法为什么不能直接创建实例再调用呢?例如:Meta().method1()。确实,对于一个独立的类型来说我们完全没有必要关心它的元类型,但是元类型或者元编程的概念的理解可以让你在设计框架时避免不断改动代码。下面这段代码便可看出元类型的优势:

    class vc1: UIViewController {
    }
    class vc2: UIViewController {
    }
    
    func setupViewControllers(_ vcTypes: [AnyClass]) {
        for vctype in vcTypes {
            if vctype is UIViewController.Type {
                let vc = vctype.init()
                print(vc)
            }
        }
    }
    
    let usingvcTypes: [AnyClass] = [vc1.self, vc2.self]
    setupViewControllers(usingvcTypes)
    

    类似这样的用法,就可以避免你在开发中不断修改代码,也迫使你在封装代码时不断严格要求自己。

    三、总结

    通过这篇文章,对Swift中 Any, AnyObject和AnyClass 有一个初步了解即可,我们最常见的,在tableview注册cell时,就有一个AnyClass的应用。

    tableView.register(<cellClass: AnyClass?>, forCellReuseIdentifier: <String>)
    

    还有上面提到的对 .self 的使用有两个:
    1 用在类型后面,获取类型的元类型
    2 用在实例后面,获取实例本身
    其中第2个用法,获取实例本身一般直接通过init方法创建即可,所以这种用法不推荐使用。
    此外,这里的 .self 和协议、类方法中的 Self 完全不同。注意区分

    四、应用

    在项目开发中经常遇到各种各样 UIViewController 跳转的需求,往往是通过一些封装好的模型去跳转不同的控制器。获取mode的时候可能会接收各种各样的类,即 AnyObject。此外,赋值时接收的数据类型不统一,即用 Any 接收。最后,返回的控制器都是 UIViewController.Type,即 AnyClass

    相关文章

      网友评论

        本文标题:Swift 元类型和.self

        本文链接:https://www.haomeiwen.com/subject/pbubyftx.html