part 2:(Nested Types,Extensions,Protocols)




19Nested Types


枚举类型经常被创建类支持某个类或某个结构体的功能。同样的,可以在一个复杂的类型内部方便的定义实用的类和结构体类使用。为了完成这个功能,swift中可以定义内嵌类型,你可以在类型内部定义枚举,类,结构体。


19.1Nested Types in Action


下面这个例子定义BlackjackCard结构体,这是一种纸牌游戏。Ace可以是1,也可以是11。

struct BlackjackCard {

    

    // nested Suit enumeration

   enum Suit: Character {

       case Spades = "♠", Hearts ="♡", Diamonds = "♢", Clubs ="♣"

    }

    

    // nested Rank enumeration

   enum Rank: Int {

       case Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten

       case Jack, Queen, King, Ace

       struct Values {

           let first: Int, second:Int?

        }

       var values: Values {

           switch self {

           case .Ace:

               return Values(first:1, second: 11)

           case .Jack, .Queen, .King:

               return Values(first:10, second: nil)

           default:

               return Values(first:self.rawValue, second:nil)

            }

        }

    }

    

    // BlackjackCard properties and methods

   let rank: Rank, suit:Suit

   var description: String {

       var output = "suit is\(suit.rawValue),"

        output +=" value is \(rank.values.first)"

       if let second =rank.values.second {

            output +=" or \(second)"

        }

       return output

    }

}


let theAceOfSpades =BlackjackCard(rank: .Ace, suit: .Spades)

println("theAceOfSpades:\(theAceOfSpades.description)")

// prints "theAceOfSpades: suit is ♠, value is 1 or 11"


19.2Referring to Nested Types


使用内嵌类型


let heartsSymbol =BlackjackCard.Suit.Hearts.rawValue

// heartsSymbol is "♡"


20Extensions


Extensions增加新功能到已有类,结构体,或枚举类型。即使你没有这些类,结构体,枚举的源代码。

Extensions are similar to categories in Objective-C. (Unlike Objective-C categories, Swift extensions do not have names.)

Extension(扩展)的功能:

1)增加计算属性和静态计算属性

2)定义实例方法和类方法

3)提供新的构造器

4)定义下标方法

5)定义和使用新的内嵌类型

6)实现某个协议


注意:扩展可以增加新的函数,但不能覆写已有函数


20.1Extension Syntax


extension SomeType {

    // new functionality to add to SomeType goes here

}


extension SomeType:SomeProtocol, AnotherProtocol {

    // implementation of protocol requirements goes here

}


注意:一旦定义扩张来增加功能到已有类,这些功能将在所有已有实例中生效,即使它创建时这个扩展还没定义。


20.2Computed Properties


扩展可增加成员计算属性和类型计算属性


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

println("One inch is\(oneInch) meters")

// prints "One inch is 0.0254 meters"

let threeFeet =3.ft

println("Three feet is\(threeFeet) meters")

// prints "Three feet is 0.914399970739201 meters"


let aMarathon =42.km + 195.m

println("A marathon is\(aMarathon) meters long")

// prints "A marathon is 42195.0 meters long"


注意:扩展是不能增加存储属性的,也不能给已有的属性增加观察者



20.3Initializers


扩展可以增加新的构造器。这是你能够增加自定义构造函数参数。

扩展可以增加便捷构造器,但不能增加设定构造器或析构器。设定构造器和析构器必须在原始类实现那边定义。


//自定义Rect结构体

//定义两个辅助的结构体SizePoint,他们的属性都有默认值

struct Size {

   var width = 0.0, height =0.0

}

struct Point {

   var x = 0.0, y =0.0

}

struct Rect {

   var origin = Point()

   var size = Size()

}


//因为Rect结构体的所有属性都有默认值,所以自动有默认构造器和按成员顺序逐个初始化的构造器

let defaultRect =Rect()

let memberwiseRect =Rect(origin: Point(x:2.0, y: 2.0),

    size:Size(width: 5.0, height:5.0))


//扩展Rect结构体,增加构造器

extension Rect {

   init(center: Point, size:Size) {

       let originX = center.x - (size.width /2)

       let originY = center.y - (size.height /2)

       self.init(origin:Point(x: originX, y: originY), size: size)

    }

}


let centerRect =Rect(center: Point(x:4.0, y: 4.0),

    size:Size(width: 3.0, height:3.0))

// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)


20.4Methods


扩展可增加成员方法和类方法。

//下面这个例子在Int类型上增加成员方法repetitions

extension Int {

   func repetitions(task: () -> ()) {

       for i in 0..<self {

            task()

        }

    }

}

//repetitions接收一个参数,参数类型是()->(),一个没有入参没有返回值的函数


//试一把

3.repetitions({

    println("Hello!")

})

// Hello!

// Hello!

// Hello!


//使用trailing closure语法看起来更简单

3.repetitions {

    println("Goodbye!")

}

// Goodbye!

// Goodbye!

// Goodbye!


20.4.1Mutating Instance Methods


扩展所增加的方法也可以修改实例本身。

结构体和枚举方法修改实例或属性需要标注mutating,

extension Int {

   mutating func square() {

       self = self *self

    }

}

var someInt =3

someInt.square()

// someInt is now 9


20.5Subscripts


扩展可以增加新的下标函数。

下面例子为整形增加下标[n],返回右数第n位数据。

  • 123456789[0]返回9 
  • 123456789[1]返回8



extension Int {

   subscript(var digitIndex:Int) -> Int {

       var decimalBase = 1

       while digitIndex > 0 {

            decimalBase *=10

            --digitIndex

        }

       return (self / decimalBase) %10

    }

}

746381295[0]

// returns 5

746381295[1]

// returns 9

746381295[2]

// returns 2

746381295[8]

// returns 7

746381295[9]

// returns 0, as if you had requested:

0746381295[9]



20.6Nested Types

扩展可增加内嵌类型



extension Int {

   enum Kind {

       case Negative, Zero, Positive

    }

   var kind: Kind {

       switch self {

       case 0:

           return .Zero

       case let x where x > 0:

           return .Positive

       default:

           return .Negative

        }

    }

}


func printIntegerKinds(numbers: [Int]) {

   for number in numbers {

       switch number.kind {

       case .Negative:

           print("- ")

       case .Zero:

           print("0 ")

       case .Positive:

           print("+ ")

        }

    }

   print("\n")

}

printIntegerKinds([3,19, -27, 0, -6, 0,7])

// prints "+ + - 0 - 0 +"


21Protocols


协议定义某项任务或功能所需的方法,属性和其他要求的蓝图。

协议不实际提供实现,只描述实现应该是怎么样的。

协议可以被类,结构体,枚举适配。

任何满足协议要求的类型被称为遵从那个协议conform to that protocol.

协议可以要求适配类型有某个成员属性,成员方法,类方法,操作符,和下标。


21.1Protocol Syntax


定义协议

protocol SomeProtocol {

    // protocol definition goes here

}


自定义类型遵循某个协议用冒号隔开,多个协议用逗号隔开

struct SomeStructure:FirstProtocol, AnotherProtocol {

    // structure definition goes here

}


class SomeClass: SomeSuperclass, FirstProtocol,AnotherProtocol {

    // class definition goes here

}


21.2Property Requirements


协议可以要求适配类型提供某个实例属性或类属性。

协议不能指定属性必须是存储属性或是计算属性,只能指定该属性的名称和类型。

协议同时可以指定属性是只读的或是可读可写的。


如果协议要求属性可读可写,那么适配类这个属性就不能是常量存储属性或只读计算属性。

如果协议只要求属性可读,那么适配类这个属性可以是只读的,也可以是可读可写的。


属性要求都是变量,通过get,set指明可读可写性


protocol SomeProtocol {

   var mustBeSettable: Int { get set }

   var doesNotNeedToBeSettable: Int { get }

}


如果是类属性,则在前面加class关键字,计算这个协议后面被结构体或枚举适配,适配时使用static,这边仍然写class


protocol AnotherProtocol {

   class var someTypeProperty:Int { get set }

}


来个例子吧

protocol FullyNamed {

   var fullName: String {get }

}

struct Person:FullyNamed {

   var fullName: String

}

let john =Person(fullName: "John Appleseed")

// john.fullName is "John Appleseed"


21.3Method Requirements


协议可以要求适配类型有特定的成员方法或类方法。

方法的定义与一般类完全相同,可变参数也是可以的


注意:虽然语法相同,但协议里面不能指定参数的默认值。


与属性那边相同,如果是类方法,那么在前面加class,不管适配类型是类还是结构体,枚举。


protocol SomeProtocol {

   class 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()

println("Here's a random number:\(generator.random())")

// prints "Here's a random number: 0.37464991998171"

println("And another one:\(generator.random())")

// prints "And another one: 0.729023776863283"


21.4Mutating Method Requirements


有时候,方法有修改实例的必要。

对于值类型的成员方法,我们在方法前加mutating关键字来指明方法可以修改实例或实例的成员变量。


如果你定义的协议成员方法会修改到实例,那么在方法前面注明mutating


注意:虽然协议注明mutating,但如果是类来实现协议,则不需要写mutating,只有是结构体和枚举类型实现协议时才需要写mutating。


protocol Togglable {

   mutating func 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()

// lightSwitch is now equal to .On


21.5Initializer Requirements


协议可以要求实现特定的构造器

protocol SomeProtocol {

   init(someParameter: Int)

}


21.5.1Class Implementations of Protocol Initializer Requirements


实现协议的构造器要求,可以是设定构造器,也可以是便捷构造器。不管哪种情况,都必须在构造器前加required修饰符:

class SomeClass:SomeProtocol {

   required init(someParameter:Int) {

        // initializer implementation goes here

    }

}


The use of the required modifier ensures that you provide an explicit or inherited implementation of the initializer requirement on all subclasses of the conforming class, such that they also conform to the protocol.

required修饰符确保你在适配类的所有子类上都要求显示的或继承的实现那个构造器的要求,以便他们也都是适配类


注意:在final类上就不需要标注required了。


如果一个子类覆写了设定构造器,并实现了协议上的构造器要求,那么这个构造器请标注required 和 override 两个修饰符:


protocol SomeProtocol {

   init()

}


class SomeSuperClass {

   init() {

        // initializer implementation goes here

    }

}


class SomeSubClass:SomeSuperClass, SomeProtocol {

    // "required" from SomeProtocol conformance; "override" from SomeSuperClass

    required override init() {

        // initializer implementation goes here

    }

}


21.5.2Failable Initializer Requirements


协议可定义可失败构造器。

适配类可用是失败构造器或非可失败构造器来实现其要求。


21.6Protocols as Types


虽然协议没有真正实现任何功能。但是,协议是一个完整的类型。

许多其他类型可以使用的地方,用协议也可以:

1)作为函数,方法,或构造器的参数类型或返回值类型

2)作为常量变量或属性的类型

3)作为数组,字典或其他容器的子项类型


//骰子

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

    }

}


var d6 =Dice(sides: 6, generator: LinearCongruentialGenerator())

for_ in 1...5 {

    println("Random dice roll is\(d6.roll())")

}

// Random dice roll is 3

// Random dice roll is 5

// Random dice roll is 4

// Random dice roll is 5

// Random dice roll is 4


21.7Delegation


代理是一种设计模式,它使得一个类或结构所该处理的一些事情能交给另一个类。

这种设计模式通过定义协议来封装代理责任来实现。

代理可以用来响应某个特殊的事件,或是获取外部资源


//下面例子定义两个协议,以便一个骰子游戏用

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)

}


//下面是蛇与梯子的游戏,实现了骰子游戏协议

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 newSquarewhere newSquare > finalSquare:

               continue gameLoop

           default:

               square += diceRoll

               square += board[square]

            }

        }

       delegate?.gameDidEnd(self)

    }

}


//骰子游戏追踪者

class DiceGameTracker:DiceGameDelegate {

   var numberOfTurns = 0

   func gameDidStart(game: DiceGame) {

        numberOfTurns =0

       if game is SnakesAndLadders {

            println("Started a new game of Snakes and Ladders")

        }

        println("The game is using a\(game.dice.sides)-sided dice")

    }

   func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll:Int) {

        ++numberOfTurns

       println("Rolled a \(diceRoll)")

    }

   func gameDidEnd(game: DiceGame) {

        println("The game lasted for\(numberOfTurns) turns")

    }

}


//游戏一下

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


21.8Adding Protocol Conformance with an Extension


即使没有源代码,你也可以扩展一个类使其适配新的协议。

扩展可以增加属性,方法,下标,因此可以满足协议的任何要求。

//可文本化描述的,类似description

protocol TextRepresentable {

   func asText() -> String

}


//扩展Dice使其适配这个协议

extension Dice:TextRepresentable {

   func asText() -> String {

        return"A \(sides)-sided dice"

    }

}


let d12 =Dice(sides: 12, generator:LinearCongruentialGenerator())

println(d12.asText())

// prints "A 12-sided dice"


//扩展蛇和梯的游戏使其适配这个协议

extension SnakesAndLadders:TextRepresentable {

   func asText() -> String {

        return"A game of Snakes and Ladders with \(finalSquare) squares"

    }

}

println(game.asText())

// prints "A game of Snakes and Ladders with 25 squares"


21.8.1Declaring Protocol Adoption with an Extension


如果一个类型已经实现了协议的要求,但还没有标注适配这个协议,那么你可以用空扩展来标注一下。


struct Hamster {

   var name: String

   func asText() -> String {

        return"A hamster named \(name)"

    }

}

//标注一下

extension Hamster:TextRepresentable {}


let simonTheHamster =Hamster(name: "Simon")

let somethingTextRepresentable:TextRepresentable = simonTheHamster

println(somethingTextRepresentable.asText())

// prints "A hamster named Simon"


注意:虽然类型已经符合协议,但不会自动适配,你必须手动声明它适配。



21.9Collections of Protocol Types


协议类型的集合


let things: [TextRepresentable] = [game,d12, simonTheHamster]


for thingin things {

   println(thing.asText())

}

// A game of Snakes and Ladders with 25 squares

// A 12-sided dice

// A hamster named Simon


21.10Protocol Inheritance


协议可以继承一个或多个协议,并增加新的要求,多个协议用逗号隔开,如下:


protocol InheritingProtocol:SomeProtocol, AnotherProtocol {

    // protocol definition goes here

}


来个例子吧

protocol PrettyTextRepresentable:TextRepresentable {

   func asPrettyText() -> String

}


extension SnakesAndLadders:PrettyTextRepresentable {

   func asPrettyText() -> String {

       var output = asText() +":\n"

       for index in1...finalSquare {

           switch board[index] {

           case let ladderwhere ladder > 0:

                output +="▲ "

           case let snakewhere snake < 0:

                output +="▼ "

           default:

                output +="○ "

            }

        }

       return output

    }

}

println(game.asPrettyText())

// A game of Snakes and Ladders with 25 squares:

// ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○


21.11Class-Only Protocols


你可以限定一个协议只能被类类型适配,结构体或枚举不能适配。做法是在协议的继承列表前加class关键字。


protocol SomeClassOnlyProtocol:class, SomeInheritedProtocol {

    // class-only protocol definition goes here

}


21.12Protocol Composition


有时候有必要要求一个类型同时适配多个协议。这时候你可以把他们联合起来,做成一个合成协议。

下面是一个合成协议作为参数类型的例子


protocol Named {

   var name: String {get }

}

protocol Aged {

   var age: Int {get }

}

struct Person:Named, Aged {

   var name: String

   var age: Int

}

func wishHappyBirthday(celebrator:protocol<Named,Aged>) {

   println("Happy birthday\(celebrator.name) - you're\(celebrator.age)!")

}

let birthdayPerson =Person(name: "Malcolm", age:21)

wishHappyBirthday(birthdayPerson)

// prints "Happy birthday Malcolm - you're 21!"


21.13Checking for Protocol Conformance


isas来检查和转型到指定的协议。

1is,如果有适配则返回true,否则返回false

2) as? ,如果转型成功则返回可选协议类型,失败则返回nil

3) as,强制转型到协议类型,失败则触发运行时异常


@objc protocol HasArea {

   var area: Double {get }

}


注意:

1)只有标识 @objc ,你才能对其进行协议适配检查。这个属性标明这个协议要暴露给Objective-C。即使你并没有操作Objective-C,如果你想检查协议适配,你就需要加@objc

2) @objc的协议只能被类类型实现,不能被结构体或枚举实现。一旦你标注协议是@objc,你就只能将其应用在类上


当时学习的时候xcode的版本是6.1,当时就纳闷在swift里面不加@objc的协议如何判断是否适配,如何转型。

昨天在看这些备忘时,又想起这事,于是打开新版本(6.3.1)的文档看了下,发现已经不一样了。

上面一小段在新版本里面是这样的,也没有“注意”部分了。


你可以使用is as 操作符来检查是否适配协议,和转换到特定的协议。

检查和转换到协议的语法和检查和转换到类型的语法相同:

1) 如果实例适配了协议则返回true,否则false

2) as? 向下转型返回协议类型的可选值,如果实例没有适配协议则返回nil

3) as! 强制转型,如果失败则触发异常


//定义一个协议,要求有可读Double属性area

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 }

}

//圆通过计算属性实现,国家通过存储属性实现。


//一个动物类,不实现那个协议

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)

]


//遍历,检查是否适配HasArea协议

for objectin objects {

   if let objectWithArea = objectas? HasArea {

       println("Area is \(objectWithArea.area)")

    }else {

        println("Something that doesn't have an area")

    }

}

// Area is 12.5663708

// Area is 243610.0

// Something that doesn't have an area


for object in objects {

    if object is HasArea{

        let hasArea = object as! HasArea

        println("\(hasArea.area)")

    }else{

        let animal = object as! Animal

        println("\(animal.legs)")

    }

}

//打印 12.5663708

//打印 243610.0

//打印 4




21.14Optional Protocol Requirements


可以定义可选要求,这些要求不一定非得被实现。可选要求在定义前加optional修改符。


可选协议要求可以在可选链中调用。


检查可选要求是否实现的办法是调用时加问号。可选属性,可选方法的返回值返回的也是可选值。


注意:

1)可选协议要求协议必须是@objc的。即使你不操作Objective-C,如果你想表示可选要求,你就必须把协议标注成@objc。

2@objc协议只能被类类型适配,不能被结构体或枚举适配


//下面例子定义一个计数器,使用外部数据源提供计数幅度


//外部数据源协议定义如下

@objcprotocol CounterDataSource {

   optional func incrementForCount(count:Int) -> Int

   optional var fixedIncrement:Int { get }

}


//计数器定义如下

@objcclass 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

        }

    }

}


//外部数据源的一个实现

class ThreeSource:CounterDataSource {

   let fixedIncrement = 3

}


//计数器使用外部数据源计数

var counter =Counter()

counter.dataSource =ThreeSource()

for_ in 1...4 {

    counter.increment()

    println(counter.count)

}

// 3

// 6

// 9

// 12


//外部数据源实现趋0的功能

class TowardsZeroSource:CounterDataSource {

   func incrementForCount(count: Int) -> Int {

       if count == 0 {

           return 0

        }else if count <0 {

           return 1

        }else {

           return -1

        }

    }

}


//0的效果

counter.count = -4

counter.dataSource =TowardsZeroSource()

for_ in 1...5 {

    counter.increment()

    println(counter.count)

}

// -3

// -2

// -1

// 0

// 0


本人的一个疑问,这边说协议有可选要求是要@objc

可是在开发库中NSObjectProtocol是没有加@objc的,why?

protocol NSObjectProtocol {

    

   func isEqual(object: AnyObject?) -> Bool

   var hash: Int {get }

    

   var superclass: AnyClass? {get }

    

   func `self`() -> Self!

    

   func isProxy() -> Bool

    

   func isKindOfClass(aClass: AnyClass) -> Bool

   func isMemberOfClass(aClass: AnyClass) -> Bool

   func conformsToProtocol(aProtocol: Protocol) -> Bool

    

   func respondsToSelector(aSelector: Selector) -> Bool

    

   var description: String {get }

    

   optional var debugDescription:String { get }

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值