2017-03-08 16:47:22 CoCo_GG 阅读数 292

该记录采用的是Swift 3.0版本,Xcode8.2.1,配合某机构教学视频。


Swift中协议的使用和OC中协议的使用,大同小异。

Swift协议

定义协议

// Swift中定义协议:
protocol VisitorViewDelegate: class {
    /// 登录回调
    func loginBtnWillClick()

    /// 注册回调
    func registerBtnWillClick()
}

定义代理属性

// Swift也需要加上weak,避免循环引用。注意如果协议不遵守class,直接用weak修饰会报错
    weak var delegate:VisitorViewDelegate?

这里写图片描述

从报错信息我们可以看到,weak只能用来修饰类类型,和类绑定的协议(我理解为在类中声明的协议)。在Swift中不单单只有类类型能遵守协议,结构体或枚举都可以遵守,查阅官方文档(中文版)后,

当协议定义的要求需要遵循协议的类型必须是引用语义而非值语义时,应该采用类类型专属协议

但是这里为什么写的是:class而不是:NSObjectProtocol。关于这一点暂时还有明白,查看UITableViewDelegate和DataSource可以看到都是遵守NSObjectProtocol。看到比较靠谱的说法,

1.使用:NSObjectProtocol是继承自NSObject的类
2.Swift中的协议方法是必须全部实现的,如果要声明成为可选方法,那就使用:NSObjectProtocol,配合@objc。

留待后续处理。如果有知道的朋友,也可以在下方留言评论。

代理执行方法

// Swift中可选项delegate为nil时,不会执行loginBtnWillClick(),所以不会造成崩溃,这点区别于OC,需要判断delegate是否为空、能否响应代理方法
delegate?.loginBtnWillClick()

使用到协议的控制器需要VisitorViewDelegate

// 遵守协议
class BaseTableViewController: UITableViewController,VisitorViewDelegate

// 设置代理
private func setupVisitorView() {
    let customView = VisitorView()
    view = customView;
    customView.delegate = self
    visitorView = customView
}

// 实现协议方法
func loginBtnWillClick() {
    print(#function)
}
2016-10-18 00:24:06 u012903898 阅读数 2410

1、遍历字符串

// for-in 遍历String
for c in str.characters{
    print(c)
}
//c是Character类型

2、Character声明

// Character和String的相互转换
//let mark = "!" //此时mark是String类型
let mark: Character = "!" //Character必须要显示声明
//str + mark  //类型不同拼接会报错
str + String(mark)  //进行类型转换后再拼接

// append 相当于 +=
str.append(mark)
//var类型才可以调用append方法,let类型不可以

3、Character类型的特性

// Characters是基于Unicode的
let englishLetter: Character = "a"
let chineseLetter: Character = "哈"
let dog: Character = "��"
let coolGuy = "\u{1F60E}"

和别的语言不一样,englishLetter、chineseLetter、dog、coolGuy这几个Character的单独都为1

// String对Unicode的支持
var coolLetters = "abc"  //abc
coolLetters.characters.count  //3

var chineseLetters = "你好吗"  //你好吗
chineseLetters.characters.count  //3

var coolGuys = "\u{1F60E}\u{1F60E}\u{1F60E}"  //������
coolGuys.characters.count  //3

swift 的Character 非常智能,便于人类对字符的理解

var cafe = "café"  //café
var cafe2 = "cafe\u{0301}"  //café
cafe.characters.count  //4
cafe2.characters.count  //4


// String的比较
cafe == cafe2
2017-04-14 16:09:34 ChasingDreamsCoder 阅读数 293

协议定义了一个蓝图,规定了用来实现某一特定任务或者功能的方法、属性,以及其他需要的东西。类、结构体或枚举都可以遵循协议,并未协议定义的这些要求提供具体实现。某个类型能都满足某个协议的要求,就可以说该类型遵循这个协议。

除了遵循协议的类型必须实现在要求外,还可以对协议进行扩展,通过扩展来实现一部分要求或者实现一些附加功能,这样遵循协议的类型就能够使用这些功能。

属性要求

协议可以要求遵循协议的类型提供特定名称和类型的实例属性或类型属性。协议不指定属性是存储类型属性还是计算型属性,它只指定属性的名称和类型。此外,协议还指定属性是可读的还是可读写的。
如果协议要求属性是可读可写的,那么该属性不能是常量属性或只读的计算型属性。如果协议只要求属性是可读的,如果代码需要的话,还可以是可写的。
协议总是用var关键字来声明变量属性,在类型声明后加上{set get}来表示属性是可读可写的,可读属性则用{get}来表示:

protocol SomeProtocol {
    var mustBeSettable: Int { get set }
    var doesNotNeedToBeSettable: Int { get }
}
在协议中定义类型属性时,总是使用static关键字作为前缀。当类类型遵循协议时,除了static关键字,还可以使用class关键字来声明类型属性:

 protocol AnotherProtocol {
     static var someTypeProperty: Int { get set }
}
这是一个只含有一个实例属性要求的协议:
 protocol FullyNamed {
     var fullName: String { get }
}
FullyNamed协议除了要求遵循协议的类型提供fullName属性外,并没有其他特别的要求。这个协议表示,任何遵循FullyNamed的类型,都必须有一个可读的String类型的实例属性fullName。

下面是一个遵循FullyNamed协议的简单结构体:

 struct Person: FullyNamed {
     var fullName: String
}
let john = Person(fullName: "John Appleseed")
这个例子中定义了一个叫Person的结构体,用来表示一个具有名字的人。从第一行代码可以看出,它遵循了FullyNamed协议。

Person结构体的每一个实例都有一个String类型的存储型属性fullName。这正好满足了FullyNamed协议的要求,也就意味着Person结构体正确的符合了协议。(如果协议要求未被完全满足,在编译时会报错)。

下面是一个更为复杂的类,它适配并遵循了FullyNamed协议:

 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 ncc1701 = Starship(name: "Enterprise", prefix: "USS")

方法要求

协议可以要求遵循协议的类型实现某些指定的实例方法或类方法。这些方法作为协议的一部分,像普通方法一样放在协议的定义中,但是不需要大括号和方法体。可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。但是,不支持为协议中的方法的参数提供默认值。
正如属性要求中所述,在协议中定义类方法的时候,总是使用static关键字作为前缀。当类类型遵循协议时,除了static关键字,还可以使用class关键字作为前缀:
protocol SomeProtocol {
    static func someTypeMethod()
}
下面的例子定义了一个只含有一个实例方法的协议:
protocol RandomNumberGenerator {
    func random() -> Double
}
如下所示,下边是一个遵循并符合上面的协议的类。
class LinearCongruentialGenerator: RandomNumberGenerator {
    var lastRandom = 42.0
    let m = 139968.0
    let a = 3877.0
    let c = 29573.0
    func random() -> Double {
        lastRandom = ((lastRandom * a + c) % m)
        return lastRandom / m
    }
}
let generator = LinearCongruentialGenerator() 
print("Here's a random number: \(generator.random())") 
// 打印 “Here's a random number: 0.37464991998171” 
print("And another one: \(generator.random())")
// 打印 “And another one: 0.729023776863283”

Mutating 方法要求

有时需要在方法中改变方法所属的实例。例如,在值类型的实例方法中,mutating关键字作为方法的前缀,写在func关键字之前,表示可以在该方法中修改它所属的实例以及实例的任意属性的值。
如果你在协议中定义了一个实例方法,该方法会改变遵循该协议的类型的实例,那么在定义协议时需要在方法前加mutating关键字。这使得结构体和枚举能够遵循此协议并满足此方法要求。
protocol Togglable {
    mutating func toggle()
}
当使用枚举或结构体来实现Togglable协议时,需要提供一个带有mutating前缀的toggle()方法。
enum OnOffSwitch: Togglable {
    case Off, On
    mutating func toggle() {
        switch self {
        case Off:
            self = On
        case On:
            self = Off
} }
}
var lightSwitch = OnOffSwitch.Off lightSwitch.toggle()

构造器要求

协议可以要求遵循协议的类型实现指定的构造器。可以像编写普通构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器实体:

protocol SomeProtocol {
    init(someParameter: Int)
}

构造器要求在类中的实现

可以在遵循协议的类中实现构造器,无论是作为指定构造器,还是作为便利构造器,无论哪种情况,都必须为构造器实现标上required修饰符:
class SomeClass: SomeProtocol {
    required init(someParameter: Int) {
// 这里是构造器的实现部分 }
}
使用required修饰符可以确保所有子类也必须提供此构造器实现,从而也能符合协议。
protocol SomeProtocol {
    init()
}
class SomeSuperClass {
    init() {
// 这里是构造器的实现部分 }
}
class SomeSubClass: SomeSuperClass, SomeProtocol { // 因为遵循协议,需要加上 required
// 因为继承自父类,需要加上 override
required override init() {
// 这里是构造器的实现部分 }
}

可失败构造器要求

遵循协议的类型可以通过失败构造器或非可失败构造器来满足协议中定义的可失败构造器要求。协议中定义的非可失败构造器要求可以通过非可失败构造器或隐式解包可失败构造器来满足。

协议作为类型

尽管协议本身并未实现任何功能,但是协议可以被当做一个成熟的类型来用。
协议可以像其他普通类型一样使用,使用场景如下:
作为函数、方法或构造器中的参数类型或返回值类型
作为常量、变量或属性的类型
作为数组、字典或其他容器中的元素类型
下面是将协议作为类型使用的例子:
class Dice {
    let sides: Int
    let generator: RandomNumberGenerator
    init(sides: Int, generator: RandomNumberGenerator) {
        self.sides = sides
        self.generator = generator
    }
    func roll() -> Int {
        return Int(generator.random() * Double(sides)) + 1
} }
generator属性的类型为RandomNumberGenerator,因此任何遵循了RandomNumberGenerator协议的类型的实例都可以赋值给generator,除此之外并无其他要求。
 var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
 for _ in 1...5 {
     print("Random dice roll is \(d6.roll())")
 }

委托模式

委托是一种设计模式,它允许类或结构体将一些需要他们负责的功能委托给其他类型的实例。委托模式的实现很简单:定义协议来封装那些需要被委托的功能,这样就能保证遵循协议的类型能提供这些功能。委托模式可以用来响应特定的动作,或者接受外部数据源提供的数据,而无需关心外部数据源的类型。
下面的例子定义了两个基于骰子游戏的协议:
protocol DiceGame {
    var dice: Dice { get }
    func play()
}
protocol DiceGameDelegate {
    func gameDidStart(_ game: DiceGame)
    func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
    func gameDidEnd(_ game: DiceGame)
}
如下所示:
 
Dice
class SnakesAndLadders: DiceGame {
    let finalSquare = 25
    let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
    var square = 0
  var board: [Int]
    init() {
        board = [Int](count: finalSquare + 1, repeatedValue: 0)
        board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
        board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
    }
     var delegate: DiceGameDelegate?
     func play() {
         square = 0
         delegate?.gameDidStart(self)
         gameLoop: while square != finalSquare {
             let diceRoll = dice.roll()
             delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
             switch square + diceRoll {
             case finalSquare:
                 break gameLoop
             case let newSquare where newSquare > finalSquare:
                 continue gameLoop
             default:
                 square += diceRoll
                 square += board[square]
             }
}
         delegate?.gameDidEnd(self)
     }
}
该类遵循了DiceGame协议,并且提供了相应的可读的dice属性和play()方法。
游戏使用SnakesAndLadders类的init()构造器来初始化游戏。所有的游戏逻辑转移到了协议中的play()方法,play() 方法使用协议要求的dice属性提供骰子摇出的值。
delegate不是游戏的必备条件,因此delegate被定义为DiceGameDelegate类型的可选属性。因为delegate是可选值,因此会自动赋予初始值nil。随后,可以在游戏中为delegate设置适当的值。
DiceGameDelegate协议提供了三个方法用来追踪游戏过程。这三个方法被放置于游戏的逻辑中,即play()方法内。分别在游戏开始时,新一轮开始时,以及游戏结束时被调用。因为delegate是一个DiceGameDelegate类型的可选属性,因此在play()方法中通过可选链式调用它的方法。若delegate属性为nil,则调用方法会优雅的 失败,并不会产生错误。若delegate并为nil,则方法能够被调用,并传递SnakesAndLadders实例作为参数。
class DiceGameTracker: DiceGameDelegate {
    var numberOfTurns = 0
    func gameDidStart(_ game: DiceGame) {
        numberOfTurns = 0
        if game is SnakesAndLadders {
            print("Started a new game of Snakes and Ladders")
        }
        print("The game is using a \(game.dice.sides)-sided dice")
    }
    func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
        numberOfTurns += 1
        print("Rolled a \(diceRoll)")
    }
    func gameDidEnd(_ game: DiceGame) {
        print("The game lasted for \(numberOfTurns) turns")
    }
}
DiceGameTracker实现了DiceGameDelegate协议要求的三个方法,用来记录游戏已经进行的轮数。当游戏开始时,numberOfTurns属性被赋值为0,然后在每新一轮中递增,游戏结束后,打印游戏的总轮数。
gameDidStart(_:)方法从game参数获取游戏信息并打印。game参数是DiceGame类型而不是SnakeAndLadders类型,所以在gameDidStart(_:)方法中只能访问DiceGame协议中的内容。当然了,SnakeAndLaddders的方法也可以在类型转换之后调用。
let tracker = DiceGameTracker()
let game = SnakesAndLadders()
game.delegate = tracker
game.play()
// Started a new game of Snakes and Ladders
// The game is using a 6-sided dice
// Rolled a 3
// Rolled a 5
// Rolled a 4
// Rolled a 5
// The game lasted for 4 turns

通过扩展添加协议一致性

即便无法修改源代码,依然可以通过扩展令已有类型遵循并符合协议。扩展可以为已有类型添加属性、方法、下标以及构造器,因此可以符合协议中的相应要求。
protocol TextRepresentable {
    var textualDescription: String { get }
}
extension Dice: TextRepresentable {
    var textualDescription: String {
        return "A \(sides)-sided dice"
    }
}
通过扩展遵循并符合协议,和在原始定义中遵循并符合协议的效果完全相同。协议名称写在类型名之后,以冒号隔开,然后在扩展的大括号内实现协议要求的内容。
let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator()) print(d12.textualDescription)
extension SnakesAndLadders: TextRepresentable {
    var textualDescription: String {
        return "A game of Snakes and Ladders with \(finalSquare) squares"
    }
}
print(game.textualDescription)

通过扩展遵循协议

当一个类型已经符合了某个协议中的所有要求,却还没有声明遵循该协议时,可以通过空扩展体的扩展来遵循该协议:
struct Hamster {
    var name: String
       var textualDescription: String {
        return "A hamster named \(name)"
} }
extension Hamster: TextRepresentable {}
从现在起,Hamster的实例可以作为TextRepresentable类型使用:
let simonTheHamster = Hamster(name: "Simon")
let somethingTextRepresentable: TextRepresentable = simonTheHamster

协议的继承

协议能够继承一个或多个其他协议,可以在继承的协议的基础上添加新的要求。协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔:
protocol PrettyTextRepresentable: TextRepresentable {
    var prettyTextualDescription: String { get }
}
例子中定义了一个新的协议,它继承自TextRepresentable协议。任何遵循PrettyTextRepresentable协议的类型在满足该协议的要求时,也必须满足TextRepresentable协议的要求。在这个例子中。PrettyTextRepresentable协议额外要求遵循协议的类型提供一个返回值为String类型的prettyTextualDescription属性。
 extension SnakesAndLadders: PrettyTextRepresentable {
     var prettyTextualDescription: String {
         var output = textualDescription + ":\n"
         for index in 1...finalSquare {
             switch board[index] {
             case let ladder where ladder > 0:
output += "▲ "
case let snake where snake < 0:
output += "▼ " default:
output += "○ " }
}
         return output
     }
}

类类型专属协议

可以在协议的继承列表中,通过添加class关键字来限制协议只能被类类型遵循,而结构体或枚举不能遵循该协议。class关键字必须第一个出现在协议的继承列表中,在其他继承的协议之前:
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol { // 这里是类类型专属协议的定义部分
}

协议合成

有时候需要同时遵循多个协议,可以将多个协议采用&进行组合
protocol Named {
    var name: String { get }
}
protocol Aged {
    var age: Int { get }
}
struct Person: Named, Aged {
    var name: String
    var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
    print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21) wishHappyBirthday(to: birthdayPerson)
Named协议包含String类型的name属性。Aged协议包含Int类型的age属性。Person结构体遵循了这两个协议。
wishHappyBirthday(to:)函数的参数celebrator的类型为Named&Aged。这意味着它不关心参数的具体类型,只要参数符合这两个协议即可。

检查协议一致性

可以使用类型转换中的描述的is和as操作符来检查协议一致性,即是否符合某协议,并且可以转换到指定的协议类型。检查和转换到某个协议类型在语法上和类型的检查和转换完全相同:
is用来检查实例是否符合某个协议,若符合则返回true,否则返回false。
as?返回一个可选值,当实例符合某个协议时,返回类型为协议类型的可选值,否则返回nil。
as!将实例强制向下转换到某个协议类型,如果强转失败,会引发运行时错误。
下面的例子:
protocol HasArea {
    var area: Double { get }
}
下面都遵循了上面的协议:
class Circle: HasArea {
    let pi = 3.1415927
    var radius: Double
    var area: Double { return pi * radius * radius }
    init(radius: Double) { self.radius = radius }
}
class Country: HasArea {
    var area: Double
    init(area: Double) { self.area = area }
}
Circle类把area属性实现为基于存储型属性radius的计算型属性。country类则把area属性实现为存储型属性。这两个类都正确的符合了HasArea协议。
如下:
class Animal {
    var legs: Int
    init(legs: Int) { self.legs = legs }
}
上面的三个类并没有一个共同的基类,尽管如此,他们都是类,他们的实例都可以作为AnyObject类型的值,存储在同一个数组中:
let objects: [AnyObject] = [
    Circle(radius: 2.0),
    Country(area: 243_610),
    Animal(legs: 4)
]
如下所示,objects数组可以被迭代,并对迭代出的每一个元素进行检查,看它是否符合HasArea协议:
 for object in objects {
     if let objectWithArea = object as? HasArea {
         print("Area is \(objectWithArea.area)")
     } else {
         print("Something that doesn't have an area")
     }
 }
 // Area is 12.5663708
 // Area is 243610.0
 // Something that doesn't have an area
当迭代出的元素符合HasArea协议时,将as?操作符返回的可选值通过可选绑定,绑定到objectWithArea常量上。objectWithArea是HasArea协议类型的实例,因此area属性可以被访问和打印。

可选的协议要求

协议可以定义可选要求,遵循协议的类型可以选择是否实现这些要求。在协议中使用optional关键字作为前缀来定义可选要求。可选要求用在你需要和oc打交道的代码中,协议和可选要求都必须带上@objc属性。标记@objc特性的协议只能被继承自oc类的类或者@objc类遵循,其他类以及结构体和枚举均不能遵循这种协议。
使用可选要求时,他们的类型会自动变成可选的。比如,一个类型为(Int) ->String 的方法会变成((Int)->String)?。需要注意的是整个函数类型是可选的,而不是函数的返回值。
协议中的可选要求可通过可选链式调用来使用,因为遵循协议的类型可能没有实现这些可选要求。类似someOptonalMethod?(someArgument)这样,可以在可选方法名称后加上?来调用可选方法。
下面的例子定义了一个名为Counter的用于整数技术的类:
@objc protocol CounterDataSource {
    optional func incrementForCount(count: Int) -> Int
    optional var fixedIncrement: Int { get }
}
这个协议定义了一个可选方法和一个可选属性,他们使用了不同的方法来从数据源中获取适当的增量值。
Counter类含有CounterDataSource?类型的可选属性:
 class Counter {
     var count = 0
     var dataSource: CounterDataSource?
     func increment() {
         if let amount = dataSource?.incrementForCount?(count) {
             count += amount
         } else if let amount = dataSource?.fixedIncrement {
             count += amount
} }
}
下面的例子展示了CounterDataSource的简单实现。ThreeSource类遵循了CounterDataSource协议,它实现了可选属性fixedIncrement,每次回返回3:
 class ThreeSource: NSObject, CounterDataSource {
     let fixedIncrement = 3
}
可以使用ThreeSource的实例作为Counter实例的数据源:
 var counter = Counter()
 counter.dataSource = ThreeSource()
 for _ in 1...4 {
     counter.increment()
     print(counter.count)
 }
下面是一个更为复杂的数据源,它将使得最后的值变为0:
 @objc class TowardsZeroSource: NSObject, CounterDataSource {
     func increment(forCount count: Int) -> Int {
         if count == 0 {
             return 0
         } else if count < 0 {
             return 1
         } else {
             return -1
} }
}
 counter.count = -4
 counter.dataSource = TowardsZeroSource()
 for _ in 1...5 {
     counter.increment()
     print(counter.count)
 }

协议扩展

协议可以通过扩展来为遵循协议的类型提供属性、方法以及下标的实现。通过这种方式,可以基于协议本身来实现这些功能,而无需在每个遵循协议的类型中都重复同样的实现,也无需使用全局函数。
extension RandomNumberGenerator {
    func randomBool() -> Bool {
        return random() > 0.5
    }
}
通过协议扩展,所有遵循协议的类型,都能自动获取这个扩展所增加的方法实现,无需任何额外修改:
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// 打印 “Here's a random number: 0.37464991998171”
print("And here's a random Boolean: \(generator.randomBool())") // 打印 “And here's a random Boolean: true”

提供默认实现

可以通过协议扩展来为协议要求的属性、方法以及下标提供默认的实现。如果遵循协议的类型为这些要求提供了自己的实现,那么这些自定义实现将会替代扩展中默认实现被使用。
extension PrettyTextRepresentable  {
    var prettyTextualDescription: String {
        return textualDescription
    }
}

为协议扩展添加限制条件

在扩展协议的时候,可以指定一些限制条件,只有遵循协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用where子句来描述。
 extension CollectionType where Generator.Element: TextRepresentable {
     var textualDescription: String {
         let itemsAsText = self.map { $0.textualDescription }
         return "[" + itemsAsText.joinWithSeparator(", ") + "]"
     }
}
现在来看看先前的Hamster结构体,它符合TextRepresentable协议,同时这里还有个装有Hamster的实例的数组:
 let murrayTheHamster = Hamster(name: "Murray")
 let morganTheHamster = Hamster(name: "Morgan")
 let mauriceTheHamster = Hamster(name: "Maurice")
 let hamsters = [murrayTheHamster, morganTheHamster, mauriceTheHamster]


选择《the swift programming language》中文版





2015-08-29 09:54:50 lwjok2007 阅读数 1696

Swift中的协议 在object-c也称之为协议,但是在其他语言中可能被称之为接口Interface(比如说java中)还有可能被称之为纯虚函数。实际上都是一个意思。

协议主要用来规定统一的方法和属性名称,但是不实现任何功能。只有实现了协议的类才需要实现他的功能。

协议说通俗一点可以这样理解。比如USB接口在生活中很常见,但是大家有没有注意到,USB其实出现的很早,但是后来出现的一些USB带动的小风扇,甚至于一些电饭煲都可以使用USB来通电,那么请问,USB很明显比这些小风扇,电饭煲生产时间要早很多,难道设计生产USB的人有未卜先知的能力,早就知道我们会生产USB带动的风扇吗?

其实不是的,这里就可以类比我们的协议,其实我生产USB只需要规定你有什么方法,有什么属性就行,至于方法里面该实现什么功能,我不需要规定,当你要使用USB的时候你再去规定,他是要输出多大的电压,还是要传输数据还是要干什么。我只规定你有哪几个方法,只要你实现了我的这几个方法就行,至于功能是什么我不管。这样就可以满足我风扇也可以使用USB,电饭煲也可以使用USB,甚至于以后我灯泡,电脑什么都要使用USB。


下面我们来看一下Swift中的协议是怎么定义的


protocol MyProtocol{
    //协议内容
    
    //比如,此处我们规定一个方法
    func myProtocolFunc()
}

我们可以看到 规定的方法是没有实现的 值规定了方法名。


我们看一个实现这个协议的类

class MyTestProtocol: MyProtocol {
    func myProtocolFunc() {
        println("这是协议的方法")
    }
}


我们可以看到如果一个类要实现某一个协议(或者多个协议),这个类可以这样定义
class  类名:协议名1,协议名2{

这样我们会发现这个和继承写法类似,只不过继承是但继承 而实现协议会有多个

如果即有继承又有协议那么该如何做了?

其实很多简单 我们只需要吧继承写在最前面即可


class 类名:继承类名,协议1,协议2{

按照这种方式即可



苹果开发群 :414319235  欢迎加入  欢迎讨论问题




苹果开发群 :414319235  欢迎加入  欢迎讨论问题
2019-10-25 16:53:50 jike_yangyujing 阅读数 9

Swift 类型转换

Swift 语言类型转换可以判断实例的类型。也可以用于检测实例类型是否属于其父类或者子类的实例。

Swift 中类型转换使用 is 和 as 操作符实现,is 用于检测值的类型,as 用于转换类型。

类型转换也可以用来检查一个类是否实现了某个协议。

检查类型

类型转换用于检测实例类型是否属于特定的实例类型。

你可以将它用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。

类型检查使用 is 关键字。

操作符 is 来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 true,否则返回 false。

//超类携带公共属性name
class Subject {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class ManKind: Subject {
    var gender: String
    init(gender: String, name: String) {
        self.gender = gender
        super.init(name: name)
    }
}

class Creature: Subject {
    var fauna: String
    init(fauna: String, name: String) {
        self.fauna = fauna
        super.init(name: name)
    }
}

        let array = [ManKind(gender: "Female", name: "lily"),
                    ManKind(gender: "Male", name: "Jack"),
                    Creature(fauna: "Dog", name: "haha"),
                    Creature(fauna: "Fish", name: "jinjin"),
                    Creature(fauna: "Tree", name: "apple")]
        var manKindCount = 0
        var creatureCount = 0
        for item in array {
            //类型检查使用 is 关键字
            if item is ManKind {manKindCount += 1}
            else if item is Creature {creatureCount += 1}
        }
        print("数组中共有\(manKindCount)个人类,\(creatureCount)个动植物")

运行结果:
数组中共有2个人类,3个动植物

向下转型

向下转型,用类型转换操作符(as? 或 as!)

当你不确定向下转型可以成功时,用类型转换的条件形式(as?)。条件形式的类型转换总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 nil。

只有你可以确定向下转型一定会成功时,才使用强制形式(as!)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误

        for item in array {
            if let people = item as? ManKind {
                print("\(people.name) is \(people.gender)")
            }else if let creature = item as? Creature {
                print("\(creature.name) is \(creature.fauna)")
            }
        }

运行结果:

lily is Female
haha is Dog
Jack is Male
jinjin is Fish
apple is Tree

Any和AnyObject的类型转换

Swift为不确定类型提供了两种特殊类型别名:

  • AnyObject可以代表任何class类型的实例。
  • Any可以表示任何类型,包括方法类型(function types)。

注意:
只有当你明确的需要它的行为和功能时才使用Any和AnyObject。在你的代码里使用你期望的明确的类型总是更好的。

AnyObject 实例
        //数组存放class类型实例,不能存int double string等类型
        let array: [AnyObject] = [ManKind(gender: "Female", name: "lily"),
                    ManKind(gender: "Male", name: "Jack"),
                    Creature(fauna: "Dog", name: "haha"),
                    Creature(fauna: "Fish", name: "jinjin"),
                    Creature(fauna: "Tree", name: "apple")]
Any 实例
        //Any可以表示任何类型,包括方法类型(function types)。
        var arrayM = [Any]()
        arrayM.append(80)
        arrayM.append(3.1415)
        arrayM.append("字符串")
        arrayM.append(ManKind(gender: "Male", name: "Jason"))
        arrayM.append(Creature(fauna: "Pest", name: "蚊子"))
        
        for item in arrayM {
            switch item {
            case let people as ManKind:
                print("\(people.name) is \(people.gender)")
            case let creature as Creature:
                print("\(creature.name) is \(creature.fauna)")
            case let number as Int:
                print(number)
            case let string as String:
                print(string)
            case let pai as Double:
                print(pai)
            default:
                print("None")
            }
        }

运行结果:

80
3.1415
字符串
Jason is Male
蚊子 is Pest

在一个switch语句的case中使用强制形式的类型转换操作符(as, 而不是 as?)来检查和转换到一个明确的类型


Swift 扩展

扩展就是向一个已有的类、结构体或枚举类型添加新功能。

扩展可以对一个类型添加新的功能,但是不能重写已有的功能

Swift 中的扩展可以:

  • 添加计算型属性和计算型静态属性(不能添加存储型)
  • 定义实例方法和类型方法
  • 提供新的构造器
  • 定义下标
  • 定义和使用新的嵌套类型
  • 使一个已有类型符合某个协议

语法

扩展声明使用关键字 extension:

extension SomeType {
    // 加到SomeType的新功能写到这里
}

一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议,语法格式如下:

extension SomeType: SomeProtocol, AnotherProctocol {
    // 协议实现写到这里
}

计算型属性

扩展可以向已有类型添加计算型实例属性和计算型类型属性。(不能添加存储型)

extension Int {
    var ADD: Int {return self + 10}
    var Minus: Int {return self - 10}
    var Plus: Int {return self * 10}
    var Devid: Int {return self / 10}
}

        let number = 10.ADD.Minus.Plus.Devid
        print(number)  //10

构造器

扩展可以向类中添加新的便利构造器 init(),但是它们不能向类中添加新的指定构造器或析构函数 deinit() 。

class Subject {
    var name: String
    init(name: String) {
        self.name = name
    }
}

extension Subject {
    
    convenience init(name2: String) {
        self.init(name: name2)
        self.name = "----"
    }
    
}

        let subject = Subject(name2: "hahaha")
        print(subject.name)

运行结果:----

扩展可以向已有类型添加新的构造器。

这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。

struct Sum {
    var num1 = 10, num2 = 10
}
struct Diff {
    var NO1 = 20, NO2 = 20
}
struct Mult {
    var a = Sum()
    var b = Diff()
    var PlusSum: Int
    var PlusDiff: Int
}
extension Mult {
    init(x: Sum, y: Diff) {
        a = x
        b = y
        PlusSum = x.num1 + x.num2
        PlusDiff = y.NO1 + y.NO2
    }
}


        let sum = Sum(num1: 50, num2: 100)
        let diff = Diff(NO1: 60, NO2: 80)
//        let getMult = Mult(a: <#T##Sum#>, b: <#T##Diff#>, PlusSum: <#T##Int#>, PlusDiff: <#T##Int#>)
        let getMultExt = Mult(x: sum, y: diff)
        print("getMultExt sum\(getMultExt.a.num1),\( getMultExt.a.num2) PlusSum:\(getMultExt.PlusSum)")
        print("getMultExt diff\(getMultExt.b.NO1),\(getMultExt.b.NO2) PlusDiff:\(getMultExt.PlusDiff)")

运行结果:

getMultExt sum50,100 PlusSum:150
getMultExt diff60,80 PlusDiff:140

方法

扩展可以向已有类型添加新的实例方法和类型方法。

extension Int {
    func DoItSeveralTimes(doIt: () -> ()) {
        for _ in 0..<self {
            doIt()
        }
    }
}

        3.DoItSeveralTimes {
            print("打印了一段代码")
        }
        2.DoItSeveralTimes(doIt: {
            print("又打印了一段代码")
        })

运行结果:

打印了一段代码
打印了一段代码
打印了一段代码
又打印了一段代码
又打印了一段代码

可变实例方法

通过扩展添加的实例方法也可以修改该实例本身。

结构体和枚举类型中修改self或其属性的方法必须将该实例方法标注为mutating,正如来自原始实现的修改方法一样。

extension Double {
    mutating func square() {
        let pi = 3.141592654
        self = self * self * pi
    }
}
        var area = 7.0
        area.square()
        print(String(format: "圆的面积为%.2f", area))

运行结果:
圆的面积为153.94

下标

扩展可以向一个已有类型添加新下标。

class AnimalsInZoo {
    var animals = ["dog","lion","cat","leopard","crocodile","walf","monkey"]
}
extension AnimalsInZoo {
    subscript (index: Int) -> String{
        set{
            animals[index] = newValue
        }
        get{
            return "第\(index)个动物是\(animals[index])"
        }
    }
}

        let animals = AnimalsInZoo()
        animals[1] = "horse"
        print(animals[2])
        print(animals[1])

运行结果:

2个动物是cat
第1个动物是horse

嵌套类型

扩展可以向已有的类、结构体和枚举添加新的嵌套类型:

//嵌套类型extension
extension Int {
   enum calc
   {
      case add
      case sub
      case mult
      case div
      case anything
   }
    
    struct ssss {
        var num1: String
        init(num: String) {
            self.num1 = num
        }
    }

    var print: calc {
      switch self
      {
         case 0:
            return .add
         case 1:
            return .sub
         case 2:
            return .mult
         case 3:
            return .div
         default:
            return .anything
       }
   }
    
    var print2: ssss{
        if self >= 0 {
            return ssss(num: "大于等于0")
        }else{
            return ssss(num: "小于0")
        }
    }
    
}


//定义方法
func result(numb: [Int]) {
   for i in numb {
      switch i.print {
         case .add:
            print(" add ")
          case .sub:
            print(" sub ")
         case .mult:
         print(" mult ")
         case .div:
         print(" div ")
         default:
         print(" 未知 ")
      }
    
    print(i.print2.num1)
   }
}


        //调用方法
        result(numb: [-2,-1,0,1,2,3,4])

运行结果:

 未知 
小于0
 未知 
小于0
 add 
大于等于0
 sub 
大于等于0
 mult 
大于等于0
 div 
大于等于0
 未知 
大于等于0

Swift 协议

协议规定了用来实现某一特定功能所必需的方法和属性。

任意能够满足协议要求的类型被称为遵循(conform)这个协议。

类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。

语法

协议的语法格式如下:

protocol SomeProtocol {
    // 协议内容
}

要使类遵循某个协议,需要在类型名称后加上协议名称,中间以冒号:分隔,作为类型定义的一部分。遵循多个协议时,各协议之间用逗号,分隔。

struct SomeStructure: FirstProtocol, AnotherProtocol {
    // 结构体内容
}

如果类在遵循协议的同时拥有父类,应该将父类名放在协议名之前,以逗号分隔。

class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
    // 类的内容
}

对属性的规定

协议用于指定特定的实例属性或类属性,而不用指定是存储型属性或计算型属性。此外还必须指明是只读的还是可读可写的。

协议中的通常用var来声明变量属性,在类型声明后加上{ set get }来表示属性是可读可写的,只读属性则用{ get }来表示。

protocol protocolA {
    var marks: Int{ set get } //get set 可读可写
    var result: Bool{ get }  //可读
    func attendence() -> String
    func marksSecured() -> String
}
//继承协议A
protocol protocolB: protocolA {
    var subJect: String{ get set }
    var stuName: String{ get set }
}
//遵守协议
class StudentMarks: protocolB {
    var subJect: String = "match"
    var stuName: String = "jack"
    var marks: Int = 80
    let result: Bool = true
        
    func attendence() -> String {
        return "The \(stuName) has secured 99% attendance"
    }
    func marksSecured() -> String {
        return "\(stuName) has scored \(marks)"
    }
    
}

        let stu = StudentMarks()
        stu.stuName = "rose"
        print(stu.stuName, stu.subJect)
        print(stu.attendence())

运行结果:

rose match
The rose has secured 99% attendance

对 Mutating 方法的规定

有时需要在方法中改变它的实例。

例如,值类型(结构体,枚举)的实例方法中,将mutating关键字作为函数的前缀,写在func之前,表示可以在该方法中修改它所属的实例及其实例属性的值

protocol changeMyself {
    mutating func reVser()
}

enum dayOrNight: changeMyself{
    case day,night
    
    mutating func reVser() {
        switch self {
        case .day:
            self = .night
            print("change myself to \(self)")
        case .night:
            self = .day
            print("change myself to \(self)")
        default:
            print("no such day")
        }
    }

}

        var dayEn = dayOrNight.day
        dayEn.reVser()
        print(dayEn)

运行结果:

change myself to night
night

对构造器的规定

协议可以要求它的遵循者实现指定的构造器。

你可以像书写普通的构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体
你可以在遵循该协议的类中实现构造器,并指定其为类的指定构造器或者便利构造器。在这两种情况下,你都必须给构造器实现标上"required"修饰符

语法如下:
protocol SomeProtocol {
   init(someParameter: Int)
}

class SomeClass: SomeProtocol {
   required init(someParameter: Int) {
      // 构造器实现
   }
}

使用required修饰符可以保证:所有的遵循该协议的子类,同样能为构造器规定提供一个显式的实现或继承实现。

如果一个子类重写了父类的指定构造器,并且该构造器遵循了某个协议的规定,那么该构造器的实现需要被同时标示required和override修饰符:

protocol numProtocol {
    init(num: Int)
}
class TheMain {
    let num: Int
    init(num: Int) {
        self.num = num
    }
}
class TheSubClass: TheMain, numProtocol {
    let num2: Int
    
    init(num1: Int, num2: Int) {
        self.num2 = num2
        super.init(num: num1)
    }
    // 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override"
    required override convenience init(num: Int) {
        self.init(num1: num, num2: 0)
    }

}

        let subC = TheSubClass(num: 30)
        let subC2 = TheSubClass(num1: 20, num2: 40)
        print(subC.num, subC.num2)
        print(subC2.num, subC2.num2)

运行结果:

30 0
20 40

协议类型

尽管协议本身并不实现任何功能,但是协议可以被当做类型来使用。

协议可以像其他普通类型一样使用,使用场景:

  • 作为函数、方法或构造器中的参数类型或返回值类型
  • 作为常量、变量或属性的类型
  • 作为数组、字典或其他容器中的元素类型

在扩展中添加协议成员

我们可以可以通过扩展来扩充已存在类型( 类,结构体,枚举等)。

扩展可以为已存在的类型添加属性,方法,下标脚本,协议等成员。

protocol AgeClasificationProtocol {
   var age: Int { get }
   func agetype() -> String
}
class Personal {
   let firstname: String
   let lastname: String
   var age: Int
   init(firstname: String, lastname: String) {
      self.firstname = firstname
      self.lastname = lastname
      self.age = 10;
   }
}
extension Personal : AgeClasificationProtocol{
    func agetype() -> String {
        return "年龄"
    }
}

        let per = Personal(firstname: "lele", lastname: "zhu")
        print(per.age, per.agetype())

运行结果:10 年龄

协议的继承

协议能够继承一个或多个其他协议,可以在继承的协议基础上增加新的内容要求。

协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔:

protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
    // 协议定义
}

实例:

protocol protocolFirst {
    func printSomeThing(str: String)
}
protocol ProtocolSecond {
    func  clculLateNumber(no1: Int, no2: Int) -> Int
}
protocol ProtocolThird {
    var result: Int { set get}
}
protocol protoColSum: ProtocolSecond, ProtocolThird {
    init (num1: Int, num2: Int, print: protocolFirst)
}

class classFirst: protocolFirst {
    func printSomeThing(str: String) {
        print(str)
    }
}
class classSecond: protoColSum {
    var result: Int = 0
    
    required init(num1: Int, num2: Int, print: protocolFirst) {
        self.result = self.clculLateNumber(no1: num1, no2: num2)
        let string = "get calculated number \(self.result)"
        print.printSomeThing(str: string)
    }
    func clculLateNumber(no1: Int, no2: Int) -> Int {
        return no1 + no2
    }
}

        let clasS = classSecond(num1: 50, num2: 100, print: classFirst())
        print(clasS.result)

result:

get calculated number 150
150

类专属协议

你可以在协议的继承列表中,通过添加class关键字,限制协议只能适配到类(class)类型。

该class关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。格式如下:

protocol SomeClassOnlyProtocol: class,SomeInheritedProtocol
{
    // 协议定义
}

实例

protocol protoColSum: class, ProtocolSecond, ProtocolThird {
    init (num1: Int, num2: Int, print: protocolFirst)
}

协议合成

Swift 支持合成多个协议,这在我们需要同时遵循多个协议时非常有用。

protocol Stname {
    var name: String { get }
}
protocol Stage {
    var age: Int { get }
}
struct Personality: Stname, Stage {
    var name: String
    var age: Int
}
func show(celebrator: Stname&Stage) {
    print("\(celebrator.name) is \(celebrator.age) years old")
}

        project_konwledge_complement.show(celebrator: Personality(name: "lily", age: 40))

结果:
lily is 40 years old

检验协议的一致性

你可以使用is和as操作符来检查是否遵循某一协议或强制转化为某一类型。

  • is操作符用来检查实例是否遵循了某个协议。
  • as?返回一个可选值,当实例遵循协议时,返回该协议类型;否则返回nil。
  • as用以强制向下转型,如果强转失败,会引起运行时错误。
protocol AreaProtocol {
    var area: Double { get }
}
class circles: AreaProtocol {
    let radius: Double
    private let pi = 3.1415926
    var area: Double {
        return radius * radius * pi
    }
    init(radius: Double) {
        self.radius = radius
    }
}

class rectangle: AreaProtocol {
    var lenght: Double
    var area: Double {
        return lenght * lenght
    }
    init(lineL: Double) {
        self.lenght = lineL
    }
}

class singleLine {
    let lineLongtitude: Double
    init(lineL: Double) {
        self.lineLongtitude = lineL
    }
}

        let objectsArray: [AnyObject] = [circles(radius: 3.0),rectangle(lineL: 4.0),singleLine(lineL: 10.0)]
        for object in objectsArray {
            if let area = object as? AreaProtocol {
                print("面积是\(area.area)")
            }else{
                print("没有面积")
            }
        }

运行结果:

面积是28.2743334
面积是16.0
没有面积

Swift之协议

阅读数 1953

没有更多推荐了,返回首页