swift中的协议(get,set)介绍

swift中的协议(get,set)介绍
Protocol(协议)⽤于统⼀⽅法和属性的名称,⽽不实现任何功能。协议能够被类,枚举,结构体实现,满⾜协议要求的类,枚举,结构体被
称为协议的遵循者。
遵循者需要提供协议指定的成员,如属性,⽅法,操作符,下标等。
协议的语法
协议的定义与类,结构体,枚举的定义⾮常相似,如下所⽰:
protocol SomeProtocol { // 协议内容 }
在类,结构体,枚举的名称后加上协议名称,中间以冒号:分隔即可实现协议;实现多个协议时,各协议之间⽤逗号,分隔,如下所⽰:
struct SomeStructure: FirstProtocol, AnotherProtocol { // 结构体内容 }
当某个类含有⽗类的同时并实现了协议,应当把⽗类放在所有的协议之前,如下所⽰:
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol { // 类的内容 }
属性要求
协议能够要求其遵循者必须含有⼀些特定名称和类型的实例属性(instance property)或类属性 (type property),也能够要求属性的(设置权
限)settable 和(访问权限)gettable,但它不要求属性是存储型属性(stored property)还是计算型属性(calculate property)。
通常前置var关键字将属性声明为变量。在属性声明后写上{ get set }表⽰属性为可读写的。{ get }⽤来表⽰属性为可读的。即使你为可读的属
性实现了setter⽅法,它也不会出错。
protocol SomeProtocol { var musBeSettable : Int { get set } var doesNotNeedToBeSettable: Int { get } }
⽤类来实现协议时,使⽤class关键字来表⽰该属性为类成员;⽤结构体或枚举实现协议时,则使⽤static关键字来表⽰:
protocol AnotherProtocol { class var someTypeProperty: Int { get set } } protocol FullyNamed { var fullName: String { get } }
FullyNamed协议含有fullName属性。因此其遵循者必须含有⼀个名为fullName,类型为String的可读属性。
struct Person: FullyNamed{ var fullName: String } let john = Person(fullName: "John Appleseed") //john.fullName 为 "John Appleseed"
Person结构体含有⼀个名为fullName的存储型属性,完整的遵循了协议。(若协议未被完整遵循,编译时则会报错)。
如下所⽰,Startship类遵循了FullyNamed协议:
class Starship: FullyNamed { var prefix: String? var name: String init(name: String, prefix: String? = nil ) { self.anme = name self.prefix = prefix } var fullName
Starship类将fullName实现为可读的计算型属性。它的每⼀个实例都有⼀个名为name的必备属性和⼀个名为prefix的可选属性。当prefix存
在时,将prefix插⼊到name之前来为Starship构建fullName。
⽅法要求
协议能够要求其遵循者必备某些特定的实例⽅法和类⽅法。协议⽅法的声明与普通⽅法声明相似,但它不需要⽅法内容。
注意:
协议⽅法⽀持变长参数(variadic parameter),不⽀持默认参数(default parameter)。
前置class关键字表⽰协议中的成员为类成员;当协议⽤于被枚举或结构体遵循时,则使⽤static关键字。如下所⽰:
一次特别的拜访protocol SomeProtocol { class func someTypeMethod() } protocol RandomNumberGenerator { func random() -> Double }
建三江
RandomNumberGenerator协议要求其遵循者必须拥有⼀个名为random,返回值类型为Double的实例⽅法。(我们假设随机数在[0,1]区间内)。
LinearCongruentialGenerator类遵循了RandomNumberGenerator协议,并提供了⼀个叫做线性同余⽣成器(linear congruential generator)的
伪随机数算法。
class LinearCongruentialGenerator: RandomNumberGenerator { var lastRandom = 42.0 let m = 139968.0 let a = 3877.0 let c = 29573.0 func random() -> Double 突变⽅法要求
能在⽅法或函数内部改变实例类型的⽅法称为突变⽅法。在值类型(Value Type)(译者注:特指结构体和枚举)中的的函数前缀加上mutating关
键字来表⽰该函数允许改变该实例和其属性的类型。这⼀变换过程在章节中有详细描述。
寡核苷酸(译者注:类中的成员为引⽤类型(Reference Type),可以⽅便的修改实例及其属性的值⽽⽆需改变类型;⽽结构体和枚举中的成员均为值类
型(Value Type),修改变量的值就相当于修改变量的类型,⽽Swift默认不允许修改类型,因此需要前置mutating关键字⽤来表⽰该函数中能
够修改类型)
注意:
⽤class实现协议中的mutating⽅法时,不⽤写mutating关键字;⽤结构体,枚举实现协议中的mutating⽅法时,必须写mutating关键字。
如下所⽰,Togglable协议含有toggle函数。根据函数名称推测,toggle可能⽤于切换或恢复某个属性的状态。mutating关键字表⽰它为突变
⽅法:
protocol Togglable { mutating func toggle() }
当使⽤枚举或结构体来实现Togglabl协议时,必须在toggle⽅法前加上mutating关键字。
如下所⽰,OnOffSwitch枚举遵循了Togglable协议,On,Off两个成员⽤于表⽰当前状态
enum OnOffSwitch: Togglable { case Off, On mutating func toggle() { switch self { case Off: self = On case On: self = Off } } } var lightSwitch = OnOffSwitch.Off
协议类型
协议本⾝不实现任何功能,但你可以将它当做类型来使⽤。
使⽤场景:
作为函数,⽅法或构造器中的参数类型,返回值类型
作为常量,变量,属性的类型
作为数组,字典或其他容器中的元素类型
注意:
协议类型应与其他类型(Int,Double,String)的写法相同,使⽤驼峰式
class Dice { let sides: Int let generator: RandomNumberGenerator init(sides: Int, generator: RandomNumberGenerator) { self.sides = ator = generato 这⾥定义了⼀个名为 Dice的类,⽤来代表桌游中的N个⾯的骰⼦。
Dice含有sides和generator两个属性,前者⽤来表⽰骰⼦有⼏个⾯,后者为骰⼦提供⼀个随机数⽣成器。由于后者
为RandomNumberGenerator的协议类型。所以它能够被赋值为任意遵循该协议的类型。
此外,使⽤构造器(init)来代替之前版本中的setup操作。构造器中含有⼀个名为generator,类型为RandomNumberGenerator的形参,使得
它可以接收任意遵循RandomNumberGenerator协议的类型。
roll⽅法⽤来模拟骰⼦的⾯值。它先使⽤generator的random⽅法来创建⼀个[0-1]区间内的随机数种⼦,然后加⼯这个随机数种⼦⽣成骰⼦的
⾯值。
保皇派如下所⽰,LinearCongruentialGenerator的实例作为随机数⽣成器传⼊Dice的构造器
var d6 = Dice(sides: 6,generator: LinearCongruentialGenerator()) for _ 5 { println("Random dice roll is \(d6.roll())") } //输出结果 //Random dice roll is 3 //Random 委托(代理)模式
委托是⼀种设计模式,它允许类或结构体将⼀些需要它们负责的功能交由(委托)给其他的类型。
委托模式的实现很简单:定义协议来封装那些需要被委托的函数和⽅法,使其遵循者拥有这些被委托的函数和⽅法。
委托模式可以⽤来响应特定的动作或接收外部数据源提供的数据,⽽⽆需要知道外部数据源的类型。
下⽂是两个基于骰⼦游戏的协议:
protocol DiceGame { var dice: Dice { get } func play() } protocol DiceGameDelegate { func gameDidStart(game: DiceGame) func game(game: DiceGame, didStartNe DiceGame协议可以在任意含有骰⼦的游戏中实现,DiceGameDelegate协议可以⽤来追踪DiceGame的游戏过程。
如下所⽰,SnakesAndLadders是Snakes and Ladders(译者注:章节有该游戏的详细介绍)游戏的新版本。新版本使⽤Dice作为骰⼦,并且
实现了DiceGame和DiceGameDelegate协议
class SnakesAndLadders: DiceGame { let finalSquare = 25 let dic = Dice(sides: 6, generator: LinearCongruentialGenerator()) var square = 0 var board: Int[]
游戏的初始化设置(setup)被SnakesAndLadders类的构造器(initializer)实现。所有的游戏逻辑被转移到了play⽅法中。
注意:
因为delegate并不是该游戏的必备条件,delegate被定义为遵循DiceGameDelegate协议的可选属性
DicegameDelegate协议提供了三个⽅法⽤来追踪游戏过程。被放置于游戏的逻辑中,即play()⽅法内。分别在游戏开始时,新⼀轮开始时,
游戏结束时被调⽤。
因为delegate是⼀个遵循DiceGameDelegate的可选属性,因此在play()⽅法中使⽤了可选链来调⽤委托⽅法。若delegate属性为nil,则委
托调⽤优雅地失效。若delegate不为nil,则委托⽅法被调⽤
如下所⽰,DiceGameTracker遵循了DiceGameDelegate协议
class DiceGameTracker: DiceGameDelegate { var numberOfTurns = 0 func gameDidStart(game: DiceGame) { numberOfTurns = 0 if game is SnakesAndLadders
DiceGameTracker实现了DiceGameDelegate协议的⽅法要求,⽤来记录游戏已经进⾏的轮数。当游戏开始时,numberOfTurns属性被赋值
为0;在每新⼀轮中递加;游戏结束后,输出打印游戏的总轮数。
gameDidStart⽅法从game参数获取游戏信息并输出。game在⽅法中被当做DiceGame类型⽽不是SnakeAndLadders类型,所以⽅法中只能
访问DiceGame协议中的成员。
DiceGameTracker的运⾏情况,如下所⽰:
“let tracker = DiceGameTracker() let game = SnakesAndLadders() game.delegate = tracker game.play() // Started a new game of Snakes and Ladders // The game 在扩展中添加协议成员
即便⽆法修改源代码,依然可以通过扩展(Extension)来扩充已存在类型(译者注:类,结构体,枚举等)。扩展可以为已存在的类型添加属
性,⽅法,下标,协议等成员。详情请在章节中查看。
注意:
通过扩展为已存在的类型遵循协议时,该类型的所有实例也会随之添加协议中的⽅法
TextRepresentable协议含有⼀个asText,如下所⽰:
protocol TextRepresentable { func asText() -> String }
通过扩展为上⼀节中提到的Dice类遵循TextRepresentable协议
extension Dice: TextRepresentable { cun asText() -> String { return "A \(sides)-sided dice" } }
从现在起,Dice类型的实例可被当作TextRepresentable类型:
let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator()) println(d12.asText()) // 输出 "A 12-sided dice"
SnakesAndLadders类也可以通过扩展的⽅式来遵循协议:
extension SnakeAndLadders: TextRepresentable { func asText() -> String { return "A game of Snakes and Ladders with \(finalSquare) squares" } } println(game
通过延展补充协议声明
当⼀个类型已经实现了协议中的所有要求,却没有声明时,可以通过扩展来补充协议声明:
struct Hamster { var name: String func asText() -> String { return "A hamster named \(name)" } } exte
nsion Hamster: TextRepresentabl {}
从现在起,Hamster的实例可以作为TextRepresentable类型使⽤
let simonTheHamster = Hamster(name: "Simon") let somethingTextRepresentable: TextRepresentabl = simonTheHamester println(somethingTextRepresentable 注意:
即时满⾜了协议的所有要求,类型也不会⾃动转变,因此你必须为它做出明显的协议声明
集合中的协议类型
协议类型可以被集合使⽤,表⽰集合中的元素均为协议类型:
let things: TextRepresentable[] = [game,d12,simoTheHamster]
如下所⽰,things数组可以被直接遍历,并调⽤其中元素的asText()函数:
for thing in things { println(thing.asText()) } // A game of Snakes and Ladders with 25 squares // A 12-sided dice // A hamster named Simon
thing被当做是TextRepresentable类型⽽不是Dice,DiceGame,Hamster等类型。因此能且仅能调⽤
asText⽅法
协议的继承
协议能够继承⼀到多个其他协议。语法与类的继承相似,多个协议间⽤逗号,分隔
protocol InheritingProtocol: SomeProtocol, AnotherProtocol { // 协议定义 }
如下所⽰,PrettyTextRepresentable协议继承了TextRepresentable协议
protocol PrettyTextRepresentable: TextRepresentable { func asPrettyText() -> String }
遵循``PrettyTextRepresentable协议的同时,也需要遵循TextRepresentable`协议。
如下所⽰,⽤扩展为SnakesAndLadders遵循PrettyTextRepresentable协议:
extension SnakesAndLadders: PrettyTextRepresentable { func asPrettyText() -> String { var output = asText() + ":\n" for index finalSquare { switch board
在for in中迭代出了board数组中的每⼀个元素:
当从数组中迭代出的元素的值⼤于0时,⽤▲表⽰
当从数组中迭代出的元素的值⼩于0时,⽤▼表⽰
当从数组中迭代出的元素的值等于0时,⽤○表⽰
任意SankesAndLadders的实例都可以使⽤asPrettyText()⽅法。
println(game.asPrettyText()) // A game of Snakes and Ladders with 25 squares: // ○○▲○○▲○○▲▲○○○▼○○○○▼○○▼○▼○
协议合成
⼀个协议可由多个协议采⽤protocol<SomeProtocol, AnotherProtocol>这样的格式进⾏组合,称为协议合成(protocol composition)。
举个例⼦:
protocol Named { var name: String { get } } protocol Aged { var age: Int { get } } struct Person: Named, Aged { var name: String var age: Int } func wishHappyBirthda Named协议包含String类型的name属性;Aged协议包含Int类型的age属性。Person结构体遵循了这两个协议。
wishHappyBirthday函数的形参celebrator的类型为protocol<Named,Aged>。可以传⼊任意遵循这两个协议的类型的实例
注意:
协议合成并不会⽣成⼀个新协议类型,⽽是将多个协议合成为⼀个临时的协议,超出范围后⽴即失效。
检验协议的⼀致性
使⽤is检验协议⼀致性,使⽤as将协议类型向下转换(downcast)为的其他协议类型。检验与转换的语法和之前相同(详情查看):is操作符⽤来检查实例是否遵循了某个协议。
as?返回⼀个可选值,当实例遵循协议时,返回该协议类型;否则返回nil
as⽤以强制向下转换型。
@objc protocol HasArea { var area: Double { get } }
注意:
@objc⽤来表⽰协议是可选的,也可以⽤来表⽰暴露给Objective-C的代码,此外,@objc型协议只对类有效,因此只能在类中检查协议
的⼀致性。详情查看。
class Circle: HasArea { let pi = 3.1415927 var radius: Double var area:≈radius } init(radius: Double) { self.radius = radius } } class Country: HasArea { var area
Circle和Country都遵循了HasArea协议,前者把area写为计算型属性(computed property),后者则把area写为存储型属性(stored property)。
如下所⽰,Animal类没有实现任何协议
class Animal { var legs: Int init(legs: Int) { self.legs = legs } }
Circle,Country,Animal并没有⼀个相同的基类,所以采⽤AnyObject类型的数组来装载在他们的实例,如下所⽰:
let objects: AnyObject[] = [ Circle(radius: 2.0), Country(area: 243_610), Animal(legs: 4) ]
如下所⽰,在迭代时检查object数组的元素是否遵循了HasArea协议:
for object in objects { if let objectWithArea = object as? HasArea { println("Area is \(objectWithArea.area)") } else { println("Something that doesn't have an area"当数组中的元素遵循HasArea协议时,通过as?操作符将其可选绑定(optional binding)到objectWithArea常量上。
objects数组中元素的类型并不会因为向下转型⽽改变,当它们被赋值给objectWithArea时只被视为HasArea类型,因此只有area属性能够被
访问。
可选协议要求
可选协议含有可选成员,其遵循者可以选择是否实现这些成员。在协议中使⽤@optional关键字作为前缀来定义可选成员。
可选协议在调⽤时使⽤可选链,详细内容在章节中查看。
像someOptionalMethod?(someArgument)⼀样,你可以在可选⽅法名称后加上?来检查该⽅法是否被实现。可选⽅法和可选属性都会返回⼀
个可选值(optional value),当其不可访问时,?之后语句不会执⾏,并返回nil。
注意:
可选协议只能在含有@objc前缀的协议中⽣效。且@objc的协议只能被类遵循。
Counter类使⽤CounterDataSource类型的外部数据源来提供增量值(increment amount),如下所⽰:
@objc protocol CounterDataSource { @optional func incrementForCount(count: Int) -> Int @optional var fixedIncrement: Int { get } } CounterDataSource含有incrementForCount的可选⽅法和fiexdIncrement的可选属性。
注意:
CounterDataSource中的属性和⽅法都是可选的,因此可以在类中声明但不实现这些成员,尽管技术上允许这样做,不过最好不要这样
白粉妹下载
写。
Counter类含有CounterDataSource?类型的可选属性dataSource,如下所⽰:
@objc class Counter { var count = 0 var dataSource: CounterDataSource? func increment() { if let a
mount = dataSource?.incrementForCount?(count) { count count属性⽤于存储当前的值,increment⽅法⽤来为count赋值。
increment⽅法通过可选链,尝试从两种可选成员中获取count。
1. 由于dataSource可能为nil,因此在dataSource后边加上了?标记来表明只在dataSource⾮空时才去调⽤incrementForCount`⽅法。
2. 即使dataSource存在,但是也⽆法保证其是否实现了incrementForCount⽅法,因此在incrementForCount⽅法后边也加有?标记。
在调⽤incrementForCount⽅法后,Int型可选值通过可选绑定(optional binding)⾃动拆包并赋值给常量amount。
当incrementForCount不能被调⽤时,尝试使⽤可选属性``fixedIncrement来代替。
ThreeSource实现了CounterDataSource协议,如下所⽰:
class ThreeSource: CounterDataSource { let fixedIncrement = 3 }
使⽤ThreeSource作为数据源开实例化⼀个Counter:
var counter = Counter() counter.dataSource = ThreeSource() for _ 4 { counter.increment() unt) } // 3 // 6 // 9 // 12 TowardsZeroSource实现了CounterDataSource协议中的incrementForCount⽅法,如下所⽰:
class TowardsZeroSource: CounterDataSource { func incrementForCount(count: Int) -> Int { if count == 0 { return 0 } else if count < 0 { return 1 } else { return 下边是执⾏的代码:
电影白棉花

本文发布于:2024-09-22 10:36:58,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/244940.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:协议   类型   遵循
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议