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..<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
}
  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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • 132.转换错误成可选值 通过转换错误成一个可选值,你可以使用 try? 来处理错误。当执行try?表达式时,如果...
    无沣阅读 1,235评论 0 3
  • SwiftDay011.MySwiftimport UIKitprintln("Hello Swift!")var...
    smile丽语阅读 3,826评论 0 6
  • 1、范型范型所解决的问题 函数、方法、类型:类,结构体,枚举,元组类型,协议参数,返回值,成员函数参数,成员属性类...
    我是小胡胡分胡阅读 816评论 0 1
  • 转载:http://www.jianshu.com/p/e70bd6645d88前言 熟悉Objective-C语...
    guoshengboy阅读 1,033评论 0 1
  • 【js的作用】 《欣赏js》 Js作用有如下几条: 1.制作网页中的功能; ​2.制作网页中的动画; ​3.制作网...
    黑色的夜阅读 277评论 1 2