Home Swift Tips (3) - Protocol
Post
Cancel

Swift Tips (3) - Protocol

protocol 组合

其实 Any 是 protocol<> 的同名写法,可以用来表示任意类型。protocol<> 的标准语法形式是:protocol<ProtocolA, ProtocalB>。protocol 组合是可以使用 typealias 来命名的,这样可以提高我们的代码可读性:

1
typealise ServerType = protocol<URLDestination, DataResolver>

可选接口 (optional protocol)

一种方法是标记 protocol 为@objc, 则可以使用 Objective-C 中的关键字optional:

1
2
3
@objc protocol OptionalProtocol {
  optional func optionalMethod()
}

但是缺点是,@objc修饰的 protocol 只能提供给类使用,struct 和 enum 不能继承。我们可以用另一种办法 extension protocol,给 protocol 的方法提供默认实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protocol OptionalProtocol {
  func optionalMethod()
}

extension OptionalProtocol {
  func optionalMethod() {
    ...
  }
}

Class A: OptionalProtocol { }

let a = A()
a.optionalMethod()

接口扩展之后,类实现接口的默认方法实现,即是扩展里的实现方法。这个写法也有个坏处,其他的使用者可能会不了解接口已经做过扩展,所以协议和扩展尽量写在同一个文件中,并注释清楚。

protocol 扩展和方法覆盖

protocol 扩展可以为 protocol 类型提供额外的方法实现,就如 optional 实现原理一样。我们也可以在具体类的实现中覆盖这些方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protocol Progress {
  func start()
}

extension Progress {
  func start() {
    print("nothing")
  }
}

class Request: Progress {
  func start() {
    print("request")
  }
}

let req = Request()
req.start()   
// Print:
// request

那现在再给 Progress 扩展一个定义中不含有的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
extension Progress {
  func cancel() {
    print("nothing")
  }
}

class Request: Progress {
  func start() {
    print("request")
  }

  func cancel() {
    print("cancel")
  }
}

let req = Request()
req.cancel()                  ==>  cancel
(req as Progress).cancel()  ==>  nothing

可以看到,对于类和接口里都实现了方法,且方法不在接口定义中时,实际调用时根据当前类型判断的,如果是 Request 则调用类中的方法,如果是 Protocol 则调用协议扩展中的方法。

Protocol Assosiated Type

我们可以在 protocol 中添加一个 associatedtype 类型关联,实现协议的类型需要完成协议中的类型指定。

1
2
3
4
5
6
7
8
protocol StorableInfo {
  associatedtype InfoKey
}

class Request: StorableInfo {
  typealias InfoKey: String
  var storedInfo: [InfoKey: Any]
}

在 Swift 4.0 中引入了 where 条件语句, 这样就可以更加灵活的运用一些泛型类型。

1
2
3
4
5
6
7
8
protocol StorableInfo {
  associatedtype InfoKey where InfoKey: Hashable
}

// 在以前是编译不通过的,因为 M.InfoKey 不一定符合 Hashable
class Request<M: StorableInfo> {
  var stroedInfo: [M.InfoKey: Any]
}

Self、.self 和 .Type

  • .self 实际上是返回当前对象,如果是类型就返回当前类的元类型:
1
2
3
4
let user = User()
user.self == user  // ==> true

let userType = User.self  // User.Type
  • Self 一般使用在接口中,用来表明实现接口本身的对象:
1
2
3
4
5
6
7
8
9
protocol Producer {
    static func instance() -> Self
}

extension User: Producer {
    static func instance() -> Self {
        return self.init()
    }
}

这里有个问题,使用 self.init() 返回才能符合 Self,而不能使用 User() ,虽然这里 User 和 Self 等价。原因是在 non-final class 中实现方法,这个类是有可能被继承的,而如果使用 User() 这种写法,显然子类调用之后就不会返回子类的对象了。

  • .Type 是表明一个类的元类型,现在可以使用type(of:)方法来获得指定实例的元类型:
1
2
let type: User.Type = User.self
let metaType = type(of: user)    ===> User.Type
This post is licensed under CC BY 4.0 by the author.