Protocols and Extensions
github:Swift基础实例
github:SwiftBasicTableView
协议
一个协议定义了一个包含有一些方法(methods
),属性(properties
)以及其它需求的蓝图,这个蓝图适合特定的任务或一块功能。协议可以被 类、结构体、枚举采用,来实现那些需求。满足一个协议需求的任何类型,可以称为遵守这个协议。
- 用
protocol
声明一个协议
protocol FirstProtocol {
// protocol definition goes here
}
protocol AnotherProtocol {
// protocol definition goes here
}
自定义类型通过把协议的名字放在类型名的后面,用冒号:
隔开,来表示这个类型采用这个协议,如果有多个协议,协议之间用逗号,
隔开
struct SomeStructure: FirstProtocol,AnotherProtocol {
// structure definition goes here
}
如果一个类有个父类,那么把这些协议列在父类名字的后面,也是用逗号,
隔开
class SomeClass: Shape,FirstProtocol,AnotherProtocol {
// class definition goes here
}
- 属性需求
protocol
中的属性,必须指定属性的类型和名称,并且要指定这个属性是gettable
或者gettable 和 settable
。属性通常被声明为变量属性,前缀关键字var
。用{ get set }
表示这个属性是gettable 和 settable
,用{ get }
表示这个属性是gettable
:
protocol SomeProtocol {
var mustBeSettable: Int {get set}
var doNotNeedToBeSettable: Int {get}
}
下面看一个例子,定义一个类 Person
,遵守协议 FullyNamed
,FullyNamed
有一个单一变量,所以,Person
必须实现这个变量,不然会报错:
protocol FullyNamed {
var fullName: String {get}
}
struct Person: FullyNamed {
var fullName: String
}
let john = Person(fullName: "John")
print(john.fullName) //John
再看一个复杂的例子:
class Starship: FullyNamed {
var prefix: String?
var name: String
init(name: String, prefix: String? = nil) {
self.name = name
self.prefix = prefix
}
var fullName: String {
return (prefix != nil ? prefix! + " " : "" ) + name
}
}
var starShip = Starship(name: "Enterprise", prefix: "USS")
print(starShip.fullName) //USS Enterprise
- 方法需求
在protocol
中定义方法和定义普通的方法一样,但不需要写大括号,不需要在protocol
中去实现,下面在协议SomeProtocol
中添加一个方法someTypeMethod
:
protocol SomeProtocol {
var mustBeSettable: Int {get set}
var doNotNeedToBeSettable: Int {get}
func someTypeMethod()
}
再看一个例子,线性同余随机数,生成伪随机数:
protocol RandomNumberGenerator {
func random() ->Double
}
class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 2973.0
func random() -> Double {
lastRandom = ((lastRandom*a + c) % m)
return lastRandom / m
}
}
let generator = LinearCongruentialGenerator()
print("Random number : \(generator.random())") // Random number : 0.184606481481481
print("Another Random number: \(generator.random())") // Another Random number: 0.74056927297668
扩展
扩展会为现有的class
,structure
,enumeration
,protocol
, 添加新的功能,而且不需要你进入源码,功能和OC
的类别相似。
- 用关键字
extension
声明一个扩展:
extension Starship {
// new Functionality to add to Starship goes here
}
我们来扩展基本类型 Double
,给它增加5个计算的实例属性,使它可以支持距离单位:
extension Double {
var km: Double {
return self * 1_000.0
}
var m: Double {
return self
}
var cm: Double {
return self/100.0
}
var mm: Double {
return self/1_000.0
}
var ft: Double {
return self/3.28084
}
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters") // prints "One inch is 0.0254 meters”
print("Three feet is \(threeFeet) meters") // prints "Three feet is 0.914399970739201 meters”
通过扩展之后,任何Double
类型的值都具有了5个属性,都可以通过打点(.
)调用这些属性,这5个计算属性都是只读的read-only
,省略了关键字get
。
- 扩展构造器(
initializers
)
扩展可以为现有类型增加新的构造器。这使得你可以扩展其它类型来访问你的自定义类型(自定义类型作为构造器参数),或者为类型的原始实现,增加额外的初始化选项。下面我们自定义一个结构体Rect
:
struct RectSize {
var width = 0.0, height = 0.0
}
struct RectPoint {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = RectPoint()
var size = RectSize()
}
// Rect 会有个默认构造器和一个成员构造器
let defaultRect = Rect()
let memberwiseRect = Rect(origin: RectPoint(x:2.0, y:2.0), size: RectSize(width: 3.0, height: 3.0))
我们来扩展 Rect
的构造器:
extension Rect {
init(center: RectPoint, size: RectSize) {
let originX = center.x - (size.width/2)
let originY = center.y - (size.height/2)
self.init(origin: RectPoint(x: originX, y: originY), size: size)
}
}
let newRect = Rect(center: RectPoint(x: 3.0,y: 3.0), size: RectSize(width: 1.0, height: 1.0))
print(newRect.origin)
- 新的构造器先计算原点坐标,然后又使用了成员构造器
init(origin:size:)
来存储新的原点值origin
和size
- 扩展方法
扩展可以为现有类添加新的实例方法instance methods
和类型方法type methods
,我们为类型Int
扩展一个方法:
extension Int {
func repetitions(task:() -> Void) {
for _ in 0..<self {
task()
}
}
}
- 方法
repetition(_:)
只有一个参数,类型为() -> Void
,这个参数是一个函数,且这个函数没有参数和返回值。
你可以用任意一个Int
类型的值来调用这个扩展的方法:
1.repetitions { () -> Void in
print("Hello Swift") //打印一次 Hello Swift
}
2.repetitions({
print("Hello") // 打印两次 Hello
})
3.repetitions() {
print("Hello Swift") // 打印3次 Hello Swift
}
- 扩展
protocol
使用extension
可以为某个protocol
添加一些其他的属性和方法,并在这个extension
中把这些方法和属性实现出来(要知道在protocol
中定义的属性和方法是需要adopt to
这个协议的类去实现的),这样protocol
也可以自己为自己定义行为了:
extension RandomNumberGenerator {
func randomBool() ->Bool
return random() > 0.5
}
通过给 protocol
增加 extension
,凡遵守这个协议的类都会自动增加一个这种方法,不需要额外声明。然后,我们打印:
print("Here's a random number : \(generator.random())")
// Here's a random number : 0.384606485634324
print("And here's a random Boolean: \(generator.randomBool())")
// And here's a random Boolean: false
- 为
protocol
提供方法实现
除了上面的使用,extension
还可以为protocol
中的属性和方法提供默认的实现
Tips:(
容易出笔试题
)如果在extension
中为某个方法或属性提供了默认实现,那么遵守该协议的类不需要再提供实现。当然,遵守该协议的类也可以提供实现,这样的话,该类相当于重写了这个方法或属性的实现,当该类的实例对象调用这个方法或属性的时候,就会调动本类的实现,而不是extension
中的实现
protocol LineProtocol {
var lineName :String {get}
func lineNumber(count: Int) -> Int
}
extension LineProtocol {
func lineNumber(count: Int) -> Int {
return count/2
}
}
class Line: LineProtocol {
var lineName: String = "Red line"
func lineNumber(count: Int) -> Int {
return count
}
func lineSubscripte() -> String {
return String(lineNumber(count: 5)) + "个" + lineName
}
}
let lineObject = Line()
let lineScripte = lineObject.lineSubscripte()
print(lineScripte)
let lineObjectTwo: LineProtocol = Line()
let lineObjectThree: Line = Line()
let lineNumberTwo = lineObjectTwo.lineNumber(count:5)
let lineNumberThree = lineObjectTwo.lineNumber(count:5)
print(lineNumberTwo)
print(lineNumberThree)
上面的 lineScripte
打印出来是 5个Red line
。因为类 Line
重写了方法 lineNumber(_: Int) -> Int
。同理,lineNumberTwo
和 lineNumberThree
打印出来都是 5
。
我们注释掉 LineProtocol
中的方法 lineNumber(_: Int) -> Int
,猜猜看 lineNumberTwo
和 lineNumberThree
打印出来的结果是?
由于该方法被注释掉之后,对象 lineObjectTwo
不会再去 Line
中找该协议方法的实现,而是去 extension
中找,因为这个对象是 LineProtocol
类型。对象 lineNumberThree
是 Line
类型,因此他们打印出来分别是:2
和 5