美文网首页swift
swift 协议和类方法中的 Self

swift 协议和类方法中的 Self

作者: ikonan | 来源:发表于2018-08-20 11:26 被阅读4次

我们在看一些协议的定义时,可能会注意到出现了首字母大写的 Self 出现在类型的位置上:

protocol IntervalType {
    //...

    /// Return `rhs` clamped to `self`.  The bounds of the result, even
    /// if it is empty, are always within the bounds of `self`
    func clamp(intervalToClamp: Self) -> Self

    //...
}

比如上面这个 IntervalType 的协议定义了一个方法,接受实现该协议的自身的类型,并返回一个同样的类型。

这么定义是因为协议其实本身是没有自己的上下文类型信息的,在声明协议的时候,我们并不知道最后究竟会是什么样的类型来实现这个协议,Swift 中也不能在协议中定义泛型进行限制。而在声明协议时,我们希望在协议中使用的类型就是实现这个协议本身的类型的话,就需要使用 Self 进行指代。

但是在这种情况下,Self 不仅指代的是实现该协议的类型本身,也包括了这个类型的子类。从概念上来说,Self 十分简单,但是实际实现一个这样的方法却稍微要转个弯。为了说明这个问题,我们假设要实现一个 Copyable 的协议,满足这个协议的类型需要返回一个和接受方法调用的实例相同的拷贝。一开始我们可能考虑的协议是这样的:

protocol Copyable {
    func copy() -> Self
}

这是很直接明了的,它应该做的是创建一个和接受这个方法的对象同样的东西,然后将其返回,返回的类型不应该发生改变,所以写为 Self。然后开始尝试实现一个 MyClass 来满足这个协议:

class MyClass: Copyable {

    var num = 1

    func copy() -> Self {
        // TODO: 返回什么?
        // return
    }
}

我们一开始的时候可能会写类似这样的代码:

这是错误代码

func copy() -> Self {
   let result = MyClass()
  result.num = num
   return result
}

但是显然类型是有问题的,因为该方法要求返回一个抽象的、表示当前类型的 Self,但是我们却返回了它的真实类型 MyClass,这导致了无法编译。也许你会尝试把方法声明中的 Self 改为 MyClass,这样声明就和实际返回一致了,但是很快你会发现这样的话,实现的方法又和协议中的定义不一样了,依然不能编译。

为了解决这个问题,我们在这里需要的是通过一个和当前上下文 (也就是和 MyClass) 无关的,又能够指代当前类型的方式进行初始化。我们可以使用 type(of:) 来获取对象类型,通过它也可以进一步进行初始化,以保证方法与当前类型上下文无关,这样不论是 MyClass 还是它的子类,都可以正确地返回合适的类型满足 Self 的要求:

func copy() -> Self {
    let result = type(of: self).init()
    result.num = num
    return result
}

但是很不幸,单单是这样还是无法通过编译,编译器提示我们如果想要构建一个 Self 类型的对象的话,需要有 required 关键字修饰的初始化方法,这是因为 Swift 必须保证当前类和其子类都能响应这个 init 方法。另一个解决的方案是在当前类类的声明前添加 final 关键字,告诉编译器我们不再会有子类来继承这个类型。在这个例子中,我们选择添加上 required 的 init 方法。最后,MyClass 类型是这样的:

class MyClass: Copyable {

    var num = 1

    func copy() -> Self {
        let result = type(of: self).init()
        result.num = num
        return result
    }

    required init() {

    }
}

我们可以通过测试来验证一下行为的正确性:

let object = MyClass()
object.num = 100

let newObject = object.copy()
object.num = 1

print(object.num)     // 1
print(newObject.num)  // 100

而对于 MyClass 的子类,copy() 方法也能正确地返回子类的经过拷贝的对象了。

另一个可以使用 Self 的地方是在类方法中,使用起来也十分相似,核心就在于保证子类也能返回恰当的类型。

相关文章

  • swift 协议和类方法中的 Self

    我们在看一些协议的定义时,可能会注意到出现了首字母大写的 Self 出现在类型的位置上: 比如上面这个 Inter...

  • 协议和类方法中的Self

    // public protocol NSObjectProtocol协议中定义的'self'返回类型是Self ...

  • oc 中 self 、super 的使用

    self 对象方法中的 self 是当前对象,可以访问属性和方法,不能访问类方法类方法中的 self 是类在代码段...

  • 获取类名

    获取类名类方法中 用self 对象方法object_getClass(self)==self.dynamicTyp...

  • Swift基础知识

    一、self和Self 1、self出现在对象方法中,self就代表对象。self出现在类方法中,就代表类。2、S...

  • Objective-C 类方法中使用self注意事项

    在类(实例)方法中使用self调用方法 在实例方法中self【可以】调用实例方法(最常见)self【不可以】调用类...

  • OC和Swift混编遇到的一些小麻烦

    一. OC中调用swift类中的方法时,编译器有时找不到方法声明 OC调用swift类中的方法,swift类需要有...

  • Self关键字

    Self关键字代表什么 在实例方法中Self指代本类的实例对象。在类方法中Self指代类对象 为什么可以在方法中使...

  • Swift里获得某个类: 类名.self

    Swift里获得某个类: 类名.self

  • Python面向对象

    类中定义方法要带参数self,类中访问属性要用self.属性,self类似于Java中的this __init__...

网友评论

    本文标题:swift 协议和类方法中的 Self

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