Swift的Protocol可不仅仅作为OC时代中的代理去运用,当对Swift的Protocol理解足够深的时候,其实对Swift语言有了足够的了解.
我们这次先从一个简单的例子来说明,我们有一个数组,里面一个字符串(String),一个数字(Int)和一个我们自定义的枚举(MyType).
我们都知道,Swift的数组中的元素,必须都是同一种类型,如果我们在一个数组中放入不同类型的时候,我们必须声明这个数组为[Any]类型,否则会报错.
enum MyType {
case one
case two
}
let array: [Any] = [1, "string", MyType.one]
那么这个Any是什么呢?
protocol Any { }
Any是一个空协议,并且所有的类型都隐式的遵守了这个协议,所以当我们用[Any]来表示这个数组的时候,是可以的.
以下代码是伪代码,只是为了便于理解
struct String: Any {
}
struct Int: Any {
}
enum MyType: Any {
}
这算是遵守协议呢,还是算继承了协议呢?
从协议Any说完之后,我们有第二个需求了,我希望上面的这个array里面的元素,都进行字符串化,然后获得一个新的数组.MyType的枚举是one -> "1", two -> "2"
let array: [Any] = [1, "string", MyType.one]
var stringArray = [String]()
for obj in array {
/*
用一个方法让obj变为string
obj -> String
*/
stringArray.append("加入字符串化的obj")
}
大概就是上面代码的意思,按照一般的逻辑,由于obj已经变成了Any,不再是Int类型,所以我们还需要进行类型转换,还好上面的array数组中只有三个元素,我们可以通过判断来进行类型转换,然后进行元素的字符串化,如果这个数组的元素个数不确定并且类型很多样,这样做真的好吗?
这个是时候协议的作用就体现出来了.
我们先定义一个协议FormatStringPotocol
protocol FormatStringPotocol {
var toSting: String { get }
}
不要惊讶于Swift的协议可以定义属性,它还可以定义属性的读写性.
我们让Int,String,MyType都去遵守这个协议
extension Int: FormatStringPotocol {
var toSting: String {
<#code#>
}
}
extension String: FormatStringPotocol {
var toSting: String {
<#code#>
}
}
extension MyType: FormatStringPotocol {
var toSting: String {
<#code#>
}
}
那么如何去写这些实现呢?
String(Int)即把Int字符串化,而String本身就是String,不用任何的操作,至于枚举我们可以通过对其本身的switch来获取不同的字符串值.
最后的代码应该是这样的:
extension Int: FormatStringPotocol {
var toSting: String {
return String(self)
}
}
extension String: FormatStringPotocol {
var toSting: String {
return self
}
}
extension MyType: FormatStringPotocol {
var toSting: String {
switch self {
case .one:
return "1"
case .two:
return "2"
}
}
}
好了,我们把数组的类型声明稍微改动一下,将Any改为FormatStringPotocol
let array: [FormatStringPotocol] = [1, "string", MyType.one]
var stringArray = [String]()
for obj in array {
let string = obj.toSting
stringArray.append("加入字符串化的obj")
}
这里有一个小小的思维跳跃,既然Any是协议可以在声明数组类型的使用,那么自己定义的协议类型可以使用,另外就是OC到Swift对于Protocol的理解认知的墙,我们在这里既是遵守了FormatStringPotocol协议,也可以说是继承了FormatStringPotocol呀.
而FormatStringPotocol恰好有一个toSting的计算属性,可以让我们做字符串化的工作.
想想看,继续定义了一个新的类型,并将其生成的对象放入数组中想做统一的字符串化处理,那么你只需要遵守这个FormatStringPotocol即可!并行通过这种协议的继承,我们将Int,String,MyType这些毫无关联性的类型统一到了一种类型中,在使用和调用方法的时候,会是多么方便!
Swift是一门面向协议编程的语言,这句话我很早就读到过,但是真正理解,我觉得自己还缺火候.
Protocol是可以理解为遵守,亦可以理解为继承.
在很多知名的Swift框架中,面向协议编程已经成为了主流.
我们举一个简单的例子,Alamofire中URLConvertible协议,这段代码的注释我已经去掉了.
public protocol URLConvertible {
func asURL() throws -> URL
}
extension String: URLConvertible {
public func asURL() throws -> URL {
guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) }
return url
}
}
extension URL: URLConvertible {
public func asURL() throws -> URL { return self }
}
extension URLComponents: URLConvertible {
public func asURL() throws -> URL {
guard let url = url else { throw AFError.invalidURL(url: self) }
return url
}
}
这个URLConvertible的目的是什么,将String/URL/URLComponents的对象转为URL!
这和我们上面的例子是不是非常相似!?
然后让我们看看使用的时候调用:
@discardableResult
public func request(
_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
我们经常在使用的就是这样的:
Alamofire.request("字符串网址")
其实传入一个URL或者URLComponents都是可以,因为它们都遵守URLConvertible!
可能有人会说,搞这么复杂干嘛,能传入字符串不就好了吗.
Alamofire这么设计必然有其理由,再来将几种类型通过协议达成统一,也是化繁为简的思路,这里我们不过多的讲Alamofire的用法和思路.
Swift的Protocol,回调只是很少的一部分功能,而这部分功能往往都和cocoa框架有关,而当Protocol跳出来,为enum,struct,class所用的时候,就好比跳出三界看世界一样.
它用来进行有力的类型的内部约束,用来统一Api,用来将非关联的化为统一...
而Protocol真正的魅力所在是它可以extension
protocol FormatStringPotocol {
var toSting: String { get }
}
extension FormatStringPotocol {
var toString: String {
return "FormatStringPotocol"
}
}
好了,那是下节要说的点.
网友评论