Swift 属性和方法

在Swift中,构建结构体或者类属性和方法中,它们分为:存储型属性、计算型属性和静态属性。

计算型属性

struct Point {
    var x = 0.0 // 存储型属性
    var y = 0.0
}

struct Size {
    var width = 0.0 // 存储型属性
    var height = 0.0
}


class Rectangle {
    var origin = Point() // 存储型属性
    var size = Size()

    // 计算性属性,必须声明称变量,因为它的值是根据其他属性的值计算得出。
    // 另外,计算型属性必须声明数据类型,否则将会报错 (error: computed property must have an explicit type)
    var center: Point {
        // getter
        get {
            let centerX = origin.x + size.width / 2
            let centerY = origin.y + size.height / 2
            return Point(x: centerX , y: centerY)
        }
        // setter
        set {
            origin.x = newValue.x - size.width / 2 // newValue 是要传递给center计算性属性的新值
            origin.y = newValue.y - size.height / 2
        }
    }

    // 计算性属性,只有 getter 即只读属性(无setter)。
    var area: Double {
        return size.width * size.height
    }

    init (origin: Point , size: Size){
        self.origin = origin
        self.size = size
    }
}


var rect = Rectangle( origin: Point(), size: Size(width: 10 , height: 5) )

rect.center

rect.origin = Point(x: 10 , y: 10)

print( rect.center ) // 改变计算型属性值

rect.center = Point()
rect

静态属性

使用 static 关键字声明静态属性,该属性定义在类型属性上的Type Property。静态属性存储在类型中,不是存储在类的实例化对象上

class Player {
    var name: String // 存储型属性
    var score: UInt32 = 0
    static var highestScore: UInt32 = 0 // 使用关键字static声明静态属性,存储最高分

    init(name: String) { // 构造函数中给存储型属性赋值
        self.name = name
    }

    func play() {

        let score = arc4random() % 100
        print("\(self.name) played and got \(score) scores.")

        self.score += score

        print("Total score of \(self.name) is \(self.score)")

        if self.score > Player.highestScore { // 使用类自身调用静态变量,而不是使用self调用自己的静态属性并且也不能省略Player的书写
            Player.highestScore = self.score
        }
        print("Highest scores is \(Player.highestScore)")
    }
}

let player1 = Player(name: "Player1")
let player2 = Player(name: "Player2")

player1.play() // 玩一局得分
player1.play() // 玩第二局得分

player2.play()
player2.play()
player2.play()

类型方法 静态方法

定义在整个类型上的静态方法。

struct Matrix{
    var m: [[Int]]
    var row: Int
    var col: Int

    init?(_ arr2d: [[Int]]){
        guard arr2d.count > 0 else{
            return nil
        }
        let row = arr2d.count
        let col = arr2d[0].count
        for i in 1..<row{
            if arr2d[i].count != col{
                return nil
            }
        }
        self.m = arr2d
        self.row = row
        self.col = col
    }

    func printMatrix() {
        for i in 0 ..< row {
            for j in 0 ..< col {
                print(m[i][j],terminator:"\t")
            }
            print()
        }
    }

    static func identityMatrix(n: Int) -> Matrix? { // 静态方法
        if n <= 0 {
            return nil
        }

        var arr2d:[[Int]] = []
        for i in 0 ..< n {
            var row = [Int](repeating: 0, count: n)
            row[i] = 1
            arr2d.append(row)
        }
        return Matrix(arr2d)!
    }
}

if let m = Matrix([[1,2,],[3,4]]){
    m.printMatrix()
}

if let e = Matrix.identityMatrix(n: 8) {
    e.printMatrix()
}

属性观察器

如果外部修改了类中的成员属性操作类的 static 静态属性大小,可以通过关键字限制。可以通过关键字 didSetwillSet 进行逻辑判断。

class LightBulb{
    static let maxCurrent = 30
    var current = 0 {
        willSet { // 在对象中赋值 current 之前,{} 中的逻辑代码将执行
            print("Current value changed. The Change is \(abs(current - newValue))")
        }

        didSet{ // 在对象中赋值 current 完成,{} 中的逻辑代码将执行

            if self.current == LightBulb.maxCurrent {
                print("Pay attention, the current value get to the maximum point.")
            }else if self.current > LightBulb.maxCurrent{
                print("Current too height , falling back to previous setting.")
                self.current = oldValue
            }


            print("The current is \(self.current)")
        }
    }

}

let bulb = LightBulb()
bulb.current = 20
bulb.current = 30
bulb.current = 40

属性观察器经常应用在保护数据是否合法的场景中。

注意: didSetwillSet 不会在初始化阶段调用。

如下Demo:

enum Theme {
    case DayMode
    case NightMode
}

class UI{
    var fontColor: UIColor!
    var backgroundColor: UIColor!
    var themeMode: Theme = .DayMode {
        didSet{
            switch themeMode {
            case .DayMode:
                fontColor = UIColor.black
                backgroundColor = UIColor.white
            case.NightMode:
                fontColor = UIColor.white
                backgroundColor = UIColor.black
            }
        }
    }

    init(themeMode: Theme) {
        self.themeMode = themeMode
    }

}

let ui = UI(themeMode: Theme.DayMode)

ui.themeMode
ui.fontColor // nil
ui.backgroundColor // nil

我们查看到 ui.fontColorui.backgroundColor 的值为 nil 。说明在执行初始化的时候并没有调用 didSet ,应该进行如下代码改进。

enum Theme {
    case DayMode
    case NightMode
}

class UI{
    var fontColor: UIColor!
    var backgroundColor: UIColor!
    var themeMode: Theme = .DayMode {
        didSet{
            self.chengeTheme(themeMode: themeMode)
        }
    }

    init(themeMode: Theme) {
        self.themeMode = themeMode
        self.chengeTheme(themeMode: themeMode)
    }

    func chengeTheme(themeMode: Theme) {
        switch themeMode {
        case .DayMode:
            fontColor = UIColor.black
            backgroundColor = UIColor.white
        case.NightMode:
            fontColor = UIColor.white
            backgroundColor = UIColor.black
        }
    }
}

let ui = UI(themeMode: Theme.DayMode)

ui.themeMode
ui.fontColor // nil
ui.backgroundColor // nil

惰性加载(延迟加载)

很多时候我们有这种需求,当我们有一个大计算量的属性需要计算,我们会将逻辑放在构造函数中,可能使用这个计算型属性的情况不多。每次实例化都需要重新计算这个属性,这样会导致性能的浪费。

我们也可以把这个计算逻辑放入到计算型属性中,那假如用户经常调用的话就会一次又一次的重新计算。

为了调节这中矛盾。Swift为我们发明了一种 Lazy Property ,延迟属性。

class ClosedRange {
    let start: Int
    let end: Int

    var width: Int{
        return end - start + 1
    }

    lazy var sum: Int = { // 计算型延迟加载
        var res = 0
        for i in self.start ... self.end{
            res += i
        }
        return res
    }() // 延迟性属性闭包的调用


    init?( start: Int , end: Int ) {
        if start > end {
            return nil
        }

        self.start = start
        self.end = end
    }
}

if let range = ClosedRange(start: 0, end: 10_000){
    range.width
    range.sum // 第一次调用会计算
    range.sum // 重复调用不会多次计算属性值
    range.sum

}

lazy 关键字不允许使用在常量属性上。

惰性加载的一些其他场景

// 根据经纬度计算地理位置
class Location {
    let latitude: Double
    let longitude: Double
    lazy var address: String? = {
        return nil
    }()

    init(latitude: Double , longitude: Double){
        self.latitude = latitude
        self.longitude = longitude
    }
}

// 图书、电影、音乐相关App
class Book {
    let name: String
    lazy var content: String? = {
        // 从本地读取书的内容
        return nil
    }()
    init(name: String){
        self.name = name
    }
}

// Web请求
class Web {
    let url: String
    lazy var html: String? = {
        // 从网络读取url对应的html
        return nil
    }()

    init(url: String) {
        self.url = url
    }
}

访问控制

Swift 的访问控制是通过文件为单位控制的。其中:

  • public 可以被模块外访问

  • internal 可以被本模块访问,当我们不显式指定的时候,所有的类、属性或者方法的访问权限都是 internal

  • private 可以被本文件访问

例如:Sources 目录下 Ui.swift 文件内容如下:

enum Theme {
    case DayMode
    case NightMode
}

class UI{
    private var fontColor: UIColor!
    private var backgroundColor: UIColor!
    var themeMode: Theme = .DayMode {
        didSet{
            self.chengeTheme(themeMode: themeMode)
        }
    }

    init(){
        self.themeMode = .DayMode
        self.chengeTheme(themeMode: self.themeMode)
    }

    init(themeMode: Theme) {
        self.themeMode = themeMode
        self.chengeTheme(themeMode: themeMode)
    }

    private func chengeTheme(themeMode: Theme) {
        switch themeMode {
        case .DayMode:
            fontColor = UIColor.black
            backgroundColor = UIColor.white
        case .NightMode:
            fontColor = UIColor.white
            backgroundColor = UIColor.black
        }
    }

    func show() {
        print("The font color is \(self.fontColor == UIColor.white ? "BLACK" : "WHITE")")
        print("The background color is \(self.backgroundColor == UIColor.black ? "WHITE" : "BLACK")")
    }
}

Sources 目录下 App.swift 文件中定义相关的结构或者类,内容如下:

import Foundation

public class App{
    private let ui = UI()
    public var name: String

    public init(name: String) {
        self.name = name
    }

    public func switchMode() {
        switch ui.themeMode {
        case Theme.DayMode:
            ui.themeMode = Theme.NightMode
        case Theme.NightMode:
            ui.themeMode = Theme.DayMode
        }
    }

    public func show() {
        print("The App name is \(self.name)")
        ui.show()
    }
}

单例模式

有两个文件,gameManager.swift 是一个单例类。

import Foundation
public class GameManager {
    public var score = 0
    public static let defaltGameManager = GameManager() // 自己初始化自己

    private init(){

    }

    public func addScore(){
        self.score += 10
    }
}

注意: 初始化函数设置为 privatedefaultGameManager 设置为静态常量。

import UIKit

let gameManager = GameManager.defaltGameManager // 不允许实例化 GameManager,只能通过GameManager的静态属性 defaltGameManager 获得 GameManager 的实例

gameManager.addScore()
gameManager.score // 10

let gm = GameManager.defaltGameManager

gm.addScore()

gm.score // 20
Copyright © http://blog.webfsd.com 2017 all right reserved,powered by Gitbook该文件修订时间: 2018-01-02 01:37:26

results matching ""

    No results matching ""