Part 2: (Enumerations, Classes and Structures, Properties)

8Enumerations



An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code.


8.1Enumeration Syntax


example


enum CompassPoint {

    case North

    case South

    case East

    case West

}


NOTE

Unlike C and Objective-C, Swift enumeration members are not assigned a default integer value when they are created. In the CompassPoint example above, North, South, East and West do not implicitly equal 0, 1, 2 and 3. Instead, the different enumeration members are fully-fledged values in their own right, with an explicitly-defined type of CompassPoint.


也可以这么写

  • enum Planet {
  •     case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
  • }


声明与赋值

var directionToHead = CompassPoint.West

directionToHead = .East


8.2Matching Enumeration Values with a Switch Statement


directionToHead = .South

switch directionToHead {

case .North:

    println("Lots of planets have a north")

case .South:

    println("Watch out for penguins")

case .East:

    println("Where the sun rises")

case .West:

    println("Where the skies are blue")

}

// prints "Watch out for penguins"


let somePlanet = Planet.Earth

switch somePlanet {

case .Earth:

    println("Mostly harmless")

default:

    println("Not a safe place for humans")

}

// prints "Mostly harmless"



8.3Associated Values


有时候我们需要吧相关的值放在一起

swift的枚举可以保存任何类型,而且类型可以是不同的

类似的我们称之为discriminated unions, tagged unions, or variants


假设一个进销存系统的产品由条形码和二维码标识

在swift上,可以这样定义枚举



enum Barcode {

    case UPCA(Int, Int, Int, Int)

    case QRCode(String)

}

//This can be read as:

//

//“Define an enumeration type called Barcode, which can take either a value of UPCA with an associated value of type (Int, Int, Int, Int), or a value of QRCode with an associated value of type String.”


//创建

var productBarcode = Barcode.UPCA(8, 85909, 51226, 3)


//重新赋值

productBarcode = .QRCode("ABCDEFGHIJKLMNOP")


//通过let var提取值的细项

switch productBarcode {

case .UPCA(let numberSystem, let manufacturer, let product, let check):

    println("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")

case .QRCode(let productCode):

    println("QR code: \(productCode).")

}

// prints "QR code: ABCDEFGHIJKLMNOP."


//let 也可放在前面,这时所有细项都是let

switch productBarcode {

case let .UPCA(numberSystem, manufacturer, product, check):

    println("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")

case let .QRCode(productCode):

    println("QR code: \(productCode).")

}

// prints "QR code: ABCDEFGHIJKLMNOP."




8.4Raw Values


//“原始值姑且这么叫吧

// enumeration members can come prepopulated with default values (called raw values), which are all of the same type

enum ASCIIControlCharacter: Character {

    case Tab = "\t"

    case LineFeed = "\n"

    case CarriageReturn = "\r"

}

//用整形原值重新定义Planet

enum Planet: Int {

    case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune

}

//原值可以是strings,characters,or any of the integer or floating-point number.

//如果是整形,那么值会自动递增

let earthsOrder = Planet.Earth.rawValue

// earthsOrder is 3


//看看原值为string的例子

enum SEnum: String{

    case s1 = "SEnum1",s2 = "SEnum2"

}

let se1 = SEnum.s1.rawValue

// se1 is "SEnum1"


8.4.1Initializing from a Raw Value


let possiblePlanet = Planet(rawValue: 7)

// possiblePlanet is of type Planet? and equals Planet.Uranus


//不是所有的整型值都能找到对应的Planet值,所以这种构造方式返回的是optional值,上例中返回的是Planet?(optional planet)

let positionToFind = 9

if let somePlanet = Planet(rawValue: positionToFind) {

    println("somePlanet=\(somePlanet.rawValue)")

} else {

    println("There isn't a planet at position \(positionToFind)")

}

// prints "There isn't a planet at position 9"



9Classes and Structures

9.1Comparing Classes and Structures

Classes and structures in Swift have many things in common. Both can:

  • Define properties to store values 
  • Define methods to provide functionality 
  • Define subscripts to provide access to their values using subscript syntax 
  • Define initializers to set up their initial state 
  • Be extended to expand their functionality beyond a default implementation 
  • Conform to protocols to provide standard functionality of a certain kind
  • Classes have additional capabilities that structures do not:
  • Inheritance enables one class to inherit the characteristics of another. 
  • Type casting enables you to check and interpret the type of a class instance at runtime. 
  • Deinitializers enable an instance of a class to free up any resources it has assigned. 
  • Reference counting allows more than one reference to a class instance.

NOTE

Structures are always copied when they are passed around in your code, and do not use reference counting.


虽然类和结构体都能定义属性、方法、角标、初始化函数,可以扩展功能以及实现协议。

但是类毕竟是类,他有比结构更多的功能,他可以继承、类型转换、定义析构函数,而且它可以引用计数。

note

结构体没使用引用计数,所以每次传递都是值传递的




9.1.1Definition Syntax


  • struct Resolution {
  •     var width = 0
  •     var height = 0
  • }
  • class VideoMode {
  •     var resolution = Resolution()
  •     var interlaced = false
  •     var frameRate = 0.0
  •     var name: String?
  • }

9.1.2Class and Structure Instances


let someResolution = Resolution()

let someVideoMode = VideoMode()


9.1.3Accessing Properties


someVideoMode.resolution.width = 1280

println("The width of someVideoMode is now \(someVideoMode.resolution.width)")

// prints "The width of someVideoMode is now 1280"


NOTE

Unlike Objective-C, Swift enables you to set sub-properties of a structure property directly. In the last example above, the width property of the resolution property of someVideoMode is set directly, without your needing to set the entire resolution property to a new value.


swift中,你可以设置类中结构体的属性,在oc中,你需要重新设置整个结构体



9.1.4Memberwise Initializers for Structure Types


let vga = Resolution(width: 640, height: 480)


结构体中会自动生成这样的构造方法

但是类class是没有的


9.2Structures and Enumerations Are Value Types


结构体和枚举都是值类型(赋值和传递都是值拷贝)

实际上所有的swift基本类型都是值类型,包括(integers, floating-point numbers, Booleans, strings, arrays and dictionaries


9.3Classes Are Reference Types


类是引用类型,赋值和传递时不产生拷贝,而是产生指向同一实例的引用


9.3.1Identity Operators


It can sometimes be useful to find out if two constants or variables refer to exactly the same instance of a class. To enable this, Swift provides two identity operators:

  • Identical to (===
  • Not identical to (!==)

有时候需要这只两个类对象是不是同一实例,这是用(===,!==)


let videoMode1 = VideoMode()

let videoMode2 = VideoMode()

let videoMode3 = videoMode1

let b12 = videoMode1 === videoMode2  //false

let b13 = videoMode1 === videoMode3  //true



Note that “identical to” (represented by three equals signs, or ===) does not mean the same thing as “equal to” (represented by two equals signs, or ==):

  • “Identical to” means that two constants or variables of class type refer to exactly the same class instance. 
  • “Equal to” means that two instances are considered “equal” or “equivalent” in value, for some appropriate meaning of “equal”, as defined by the type’s designer. 

When you define your own custom classes and structures, it is your responsibility to decide what qualifies as two instances being “equal”. 


注意“identical to(===)”与 “Equal to(==)”的区别

“Identical to”意思是两个类类型的常量或变量的引用指向同一个实例

“Equal to” 意思是这两个实例可以认为是相等的,至于什么条件下认为相等,需要在类上面进行定义


9.3.2Pointers


创建对象引用时,不需要写星号


9.4Choosing Between Classes and Structures


结构体实例是值传递,类是引用传递,所以要慎重考虑到底用结构体还是类


As a general guideline, consider creating a structure when one or more of these conditions apply:


  • The structure’s primary purpose is to encapsulate a few relatively simple data values. 
  • It is reasonable to expect that the encapsulated values will be copied rather than referenced when you assign or pass around an instance of that structure. 
  • Any properties stored by the structure are themselves value types, which would also be expected to be copied rather than referenced. 
  • The structure does not need to inherit properties or behavior from another existing type.

作为指导,当以下条件有一个或更多符合时,使用结构体

1,结构体的主要目的是封装一些相关的简单的数据

2,有理由相信在赋值或传递时,他应该是值传递,而不是引用传递

3,所有的属性都是值类型属性

4,不需要从别的类型中继承属性或方法


9.5Assignment and Copy Behavior for Strings, Arrays, and Dictionaries


swift中String,Array,和Dictionary的实现都是结构体类型。这意味着他们在赋值或传递时是会被拷贝的。

相对的,NSString,NSArray,和NSDictinary是class,他们在赋值或传递时都是引用,而不是拷贝

NOTE

The description above refers to the “copying” of strings, arrays, and dictionaries. The behavior you see in your code will always be as if a copy took place. However, Swift only performs an actual copy behind the scenes when it is absolutely necessary to do so. Swift manages all value copying to ensure optimal performance, and you should not avoid assignment to try to preempt this optimization.

实际上,虽然说是值传递,swift还是会进行优化的,只有真正需要值传递时才产生新拷贝




10Properties


存储属性保存常量和变量作为实例的一部分

而计算属性通过计算得到一个值

计算属性可以在类,结构体,和枚举中有

而存储属性只在类和结构体中有


另外,可以定义属性观察者


10.1Stored Properties


struct FixedLengthRange {

    var firstValue: Int

    let length: Int

}

var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)

// the range represents integer values 0, 1, and 2

rangeOfThreeItems.firstValue = 6

// the range now represents integer values 6, 7, and 8


length初始化后就不能再修改了,因为它使let


10.1.1Stored Properties of Constant Structure Instances


let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)

// this range represents integer values 0, 1, 2, and 3

rangeOfFourItems.firstValue = 6

// this will report an error, even though firstValue is a variable property


虽然firstValue是var,但是整个rangOfFourItems是let,所以firstValue还是不能修改的。

会产生这样效果的原因是因为结构体是值类型的,当实例为常量,里面的所有属性就变成都是常量了。

然而对于类就不同了,类是引用类型,虽然引用是常量,但仍然可改变实例的变量属性


10.1.2Lazy Stored Properties


懒存储属性,只在第一次使用到时才初始化

懒属性必须为变量,因为它是在实例初始化后才初始化的,而常量属性必须在实例初始化完成前赋值,所以常量属性不能是懒属性



class DataImporter {

    /*

    DataImporter is a class to import data from an external file.

    The class is assumed to take a non-trivial amount of time to initialize.

    */

    var fileName = "data.txt"

    // the DataImporter class would provide data importing functionality here

}


class DataManager {

    lazy var importer = DataImporter()

    var data = [String]()

    // the DataManager class would provide data management functionality here

}


let manager = DataManager()

manager.data.append("Some data")

manager.data.append("Some more data")

// the DataImporter instance for the importer property has not yet been created


println(manager.importer.fileName)

// the DataImporter instance for the importer property has now been created

// prints "data.txt"



10.1.3Stored Properties and Instance Variables


在oc里面,有属性,有成员变量,往往用成员变量作为属性的内在存储空间。

在swift中,不再有成员变量了,都统一成属性了,类型,名称,和其它特征都在声明时标明。



10.2Computed Properties


除了存储属性,类,结构体,和枚举类型还可以定义计算属性,这是一个不需要实际保存值,而是提供getter和setter方法来获取和改变其他属性的值



struct Point {

    var x = 0.0, y = 0.0

}

struct Size {

    var width = 0.0, height = 0.0

}

struct Rect {

    var origin = Point()

    var size = Size()

    var center: Point {

        get {

            let centerX = origin.x + (size.width / 2)

            let centerY = origin.y + (size.height / 2)

            return Point(x: centerX, y: centerY)

        }

        set(newCenter) {

            origin.x = newCenter.x - (size.width / 2)

            origin.y = newCenter.y - (size.height / 2)

        }

    }

}

var square = Rect(origin: Point(x: 0.0, y: 0.0),

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

let initialSquareCenter = square.center

square.center = Point(x: 15.0, y: 15.0)

println("square.origin is now at (\(square.origin.x), \(square.origin.y))")

// prints "square.origin is now at (10.0, 10.0)"




10.2.1Shorthand Setter Declaration


如果计算属性的setter函数没有给新值定义名字,那么会有一个默认的名字 “newValue”


struct AlternativeRect {

    var origin = Point()

    var size = Size()

    var center: Point {

        get {

            let centerX = origin.x + (size.width / 2)

            let centerY = origin.y + (size.height / 2)

            return Point(x: centerX, y: centerY)

        }

        set {

            origin.x = newValue.x - (size.width / 2)

            origin.y = newValue.y - (size.height / 2)

        }

    }

}



10.2.2Read-Only Computed Properties


只读计算属性,即只有getter没有setter的计算属性,必须是var,不能是let

只读计算属性可以简化,可以去掉get和大括号,如下定义一个立方体,其有体积这个只读计算属性


struct Cuboid {

    var width = 0.0, height = 0.0, depth = 0.0

    var volume: Double {

        return width * height * depth

    }

}

let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)

println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")

// prints "the volume of fourByFiveByTwo is 40.0"



10.3Property Observers


属性观察者在每次设置属性时都会被调用,即使设置的新值与旧值相同

除了懒存储属性,你可以为任意的存储属性设置观察者。

你也可以增加属性观察者到继承来的属性上(不管是存储的还是计算的)

注意:你没必要为一个非继承来的计算属性定义属性观察者,因为你可以直接把它写在计算属性的setter函数里


如果实现willSet观察,则将传递一个新的常量属性值,如果没有为其指定名称,则又默认名称“newValue”

相似的,如果实现didSet观察,将传递旧的常量属性值,你可以为其指定名称,否则默认为“oldValue”


当在初始化函数中,代理产生前,willSet和didSet观察是不会被触发的


//为存储属性设置观察者

class StepCounter {

    var totalSteps: Int = 0 {

        willSet(newTotalSteps) {

            println("About to set totalSteps to \(newTotalSteps)")

        }

        didSet {

            if totalSteps > oldValue  {

                println("Added \(totalSteps - oldValue) steps")

            }

        }

    }

}

let stepCounter = StepCounter()

stepCounter.totalSteps = 200

// About to set totalSteps to 200

// Added 200 steps

stepCounter.totalSteps = 360

// About to set totalSteps to 360

// Added 160 steps

stepCounter.totalSteps = 896

// About to set totalSteps to 896

// Added 536 steps


如果在didSet里面给这个值再重新赋值,那么最终值是这个新值


下面是我写的测试用例

class PropertyObservers {

    //存储属性可以设置观察者

    var property1: Int = 0{

        willSet(newPro){

            println("PropertyObservers property1 willSet \(newPro)")

        }

        didSet{

            println("PropertyObservers property1 didSet \(oldValue)")

            property1 = 3

        }

    }

    //计算属性不能设置观察者

    var computedPro11: Int {

        get{

            return property1

        }

        set{

            println("PropertyObservers computedPro11 set")

            property1 = newValue

        }

//        willSet{ //Error: WillSet variable may not also have a set specifier

//            println("computedPro11 didSet")

//            

//        }

//        didSet{//Error: DidSet variable may not also have a set specifier

//            println("computedPro11 didSet")

//        }

    }

    

    init(Property1:Int){

        self.property1 = Property1  //初始化时不触发属性观察

    }

}


class PropertyObservers2: PropertyObservers {

    //为继承下来的计算属性设置观察者

    override var computedPro11: Int{

        willSet{ //继承后这里却可以willSet

            println("PropertyObservers2 computedPro11 willSet")

        }

    }

}

 println("初始化时不触发属性观察")

let p2_1 = PropertyObservers2(Property1:100)

println("------------------")

p2_1.computedPro11 = 100

println("------------------\(p2_1.computedPro11)")



打印如下


初始化时不触发属性观察

------------------

PropertyObservers2 computedPro11 willSet

PropertyObservers computedPro11 set

PropertyObservers property1 willSet 100

PropertyObservers property1 didSet 100

------------------3


从打印结果来看didSet里面重新赋值时不再重新触发观察者



10.4Global and Local Variables



上面的计算属性和观察者对于全局变量同样适用。

全局变量是指在所有函数,方法,闭包或类型上下文之外的变量。

局部变量是指定义在函数,方法,或闭包上下文中得变量


下面是我写的小测试


var globalPro: Int = 1{

willSet{

    println("globalPro willSet newValue=\(newValue)")

}

didSet{

    println("globalPro didSet oldValue=\(oldValue)")

}

}


var globalComputedPro: Int {

get{

    return globalPro + 1

}

set{

    globalPro = newValue - 1

}

}


globalPro = 3

println("globalComputedPro=\(globalComputedPro)")

globalComputedPro = 100

println("globalPro=\(globalPro)")


打印:

globalPro willSet newValue=3

globalPro didSet oldValue=1

globalComputedPro=4

globalPro willSet newValue=99

globalPro didSet oldValue=3

globalPro=99


10.5Type Properties


类属性,实现别的语言中类变量的功能,因为swift中变量属性统一成属性了,所以只能是类属性了


NOTE

Unlike stored instance properties, you must always give stored type properties a default value. This is because the type itself does not have an initializer that can assign a value to a stored type property at initialization time.


类属性,你需要给默认值



10.5.1Type Property Syntax


在c和oc中,我们定义全局变量来辅助类,在swift中,我们可以把它定义在类里面


值类型的类属性用static关键字,类类型的类属性用class关键字


struct SomeStructure {

    static var storedTypeProperty = "Some value."

    static var computedTypeProperty: Int {

        // return an Int value here

        return 1

    }

}

enum SomeEnumeration {

    static var storedTypeProperty = "Some value."

    static var computedTypeProperty: Int {

        // return an Int value here

        return 1

    }

}

class SomeClass {

    class var computedTypeProperty: Int {

        // return an Int value here

        return 1

    }

}


上面的计算属性都是只读属性写法的简化版,当然他们也可以是可写的,语法参照实例属性那边写


10.5.2Querying and Setting Type Properties


类属性使用类名和点语法访问,不能用实例加点语法来访问



println(SomeClass.computedTypeProperty)

// prints “1”


println(SomeStructure.storedTypeProperty)

// prints "Some value."

SomeStructure.storedTypeProperty = "Another value."

println(SomeStructure.storedTypeProperty)

// prints "Another value."


let someStructure = SomeStructure()

SomeStructure.storedTypeProperty = "1"

someStructure.storedTypeProperty = "1"  //Error: ’SomeStructure' does not have a member named 'storedTypeProperty'


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值