Swift:协议和扩展

Protocols and Extensions

github:Swift基础实例
github:SwiftBasicTableView

协议

一个协议定义了一个包含有一些方法(methods),属性(properties)以及其它需求的蓝图,这个蓝图适合特定的任务或一块功能。协议可以被 类、结构体、枚举采用,来实现那些需求。满足一个协议需求的任何类型,可以称为遵守这个协议。

  1. 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
}
  1. 属性需求
    protocol 中的属性,必须指定属性的类型和名称,并且要指定这个属性是 gettable 或者 gettable 和 settable。属性通常被声明为变量属性,前缀关键字 var。用 { get set } 表示这个属性是 gettable 和 settable,用 { get } 表示这个属性是 gettable
protocol SomeProtocol {
    var mustBeSettable: Int {get set}
    var doNotNeedToBeSettable: Int {get}
}

下面看一个例子,定义一个类 Person,遵守协议 FullyNamedFullyNamed有一个单一变量,所以,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
  1. 方法需求
    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的类别相似。

  1. 用关键字 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

  1. 扩展构造器(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:)来存储新的原点值originsize
  1. 扩展方法
    扩展可以为现有类添加新的实例方法instance methods和类型方法type methods,我们为类型 Int 扩展一个方法:
extension Int {
    func repetitions(task:() -> Void) {
        for _ in 0..
  • 方法 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
}
  1. 扩展 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
  1. 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。同理,lineNumberTwolineNumberThree 打印出来都是 5

我们注释掉 LineProtocol 中的方法 lineNumber(_: Int) -> Int ,猜猜看 lineNumberTwolineNumberThree 打印出来的结果是?

由于该方法被注释掉之后,对象 lineObjectTwo 不会再去 Line 中找该协议方法的实现,而是去 extension 中找,因为这个对象是 LineProtocol 类型。对象 lineNumberThreeLine 类型,因此他们打印出来分别是:25

你可能感兴趣的:(Swift:协议和扩展)