zoukankan      html  css  js  c++  java
  • Swift 学习笔记 (属性)

    属性可以将值与特定的类 结构体 或者枚举联系起来。 存储属性会存储常量或者变量作为实例的一部分。反之计算属性会计算(而不是存储值)值。 计算属性可以由类 结构体 和枚举定义。存储属性只能由类和结构体定义。

    存储属性和计算属性通常和特定类型的实例相关联。总之 属性也可以与类型本身相关联。这中属性就是所谓的类型属性。

    另外,你也可以定义属性观察器来检查属性中值的变化,这样你就可以用自定义的行为来响应。属性观察器可以被添加到你自己定义的存储属性中,也可以添加到子类从他的父类那里所继承来的属性中。

    存储属性

    在其最简单的形式下,存储属性是一个作为特定类和结构体实例一部分的常量或者变量。存储属性要么是变量存储属性(var 声明)要么是常量存储属性(let 声明)

    下面的例子定义了一个名为 FixedLengthRange 的结构体,它描述了一个一旦被创建长度就不能改变的整型值域:

    struct FixedLengthRange {
        var firstValue:Int
        let length:Int
    }

    FixedLengthRange的实例有一个名为firstValue的变量存储属性和一个名为length的常量存储属性。在上面的例子中,当新的值域创建时length已经被创建并且不能再修改,因为这是一个常量属性。

    常量结构体实例的存储属性

    如果你创建了一个结构体实例并且把这个实例赋给常量,你不能修改这个实例的属性,即使是声明为变量的属性。

    let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
    
    //rangeOfFourItems.firstValue = 2//不能修改会报错

    延迟存储属性

    延迟存储属性的初始值在其第一次使用时才进行计算。你可以通过在其声明前标注lazy修饰语来表示一个延迟存储属性。

    你必须把延迟存储属性声明为变量(使用 var 关键字),因为它的初始值可能在实例初始化完成之前无法取得。常量属性则必须在初始化完成之前有值,因此不能声明为延迟。

    一个属性的初始值可能依赖于某些外部因素,当这些外部因素的值只有在实例的初始化完成后才能得到时,延迟属性就可以发挥作用了。而当属性的初始值需要执行复杂或代价高昂的配置才能获得,你又想要在需要时才执行,延迟属性就能够派上用场了。

    class dataImporter {
        var fileName = "data.txt"
    }
    class dataManager {
        lazy var importer = dataImporter()
        var data = [String]()
    }
    let manager = dataManager()
    manager.data.append("some data")
    manager.data.append("some more data")

    类 DataManager 有一个名为 data 的存储属性,它被初始化为一个空的新 String 数组。尽管它的其余功能没有展示出来,还是可以知道类 DataManager 的目的是管理并提供访问这个 String 数组的方法。

    DataManager 类的功能之一是从文件导入数据。此功能由 DataImporter 类提供,它假定为需要一定时间来进行初始化。这大概是因为 DataImporter 实例在进行初始化的时候需要打开文件并读取其内容到内存中。

    DataManager 实例并不要从文件导入数据就可以管理其数据的情况是有可能发生的,所以当 DataManager 本身创建的时候没有必要去再创建一个新的 DataImporter 实例。反之,在 DataImporter 第一次被使用的时候再创建它才更有意义。

    因为它被 lazy 修饰符所标记,只有在 importer 属性第一次被访问时才会创建 DataManager 实例,比如当查询它的 fileName 属性时:

    print(manager.importer.fileName)

    存储属性与实例变量

    如果你有 Objective-C 的开发经验,那你应该知道在类实例里有两种方法来存储值和引用。另外,你还可以使用实例变量作为属性中所储存的值的备份存储。

    Swift 把这些概念都统一到了属性声明里。Swift 属性没有与之相对应的实例变量,并且属性的后备存储不能被直接访问。这避免了不同环境中对值的访问的混淆并且将属性的声明简化为一条单一的、限定的语句。所有关于属性的信息——包括它的名字,类型和内存管理特征——都作为类的定义放在了同一个地方。

    计算属性

    除了存储属性,类、结构体和枚举也能够定义计算属性,而它实际并不存储值。相反,他们提供一个读取器和一个可选的设置器来间接得到和设置其他的属性和值。

    struct Point {
        var x = 0.0
        var y = 0.0
    }
    
    struct Size {
        var width = 0.0
        var 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( 10.0, height: 10.0))
    let initialSquareCenter = square.center
    square.center = Point(x: 15.0, y: 15.0)
    square.origin.x
    square.origin.y

    只读计算属性

    一个有读取器但是没有设置器的计算属性就是所谓的只读计算属性。只读计算属性返回一个值,也可以通过点语法访问。但是不能被修改为另外一个值。

    你必须用 var 关键字定义计算属性——包括只读计算属性——为变量属性,因为它们的值不是固定的。 let 关键字只用于常量属性,用于明确那些值一旦作为实例初始化就不能更改。

    struct Cuboid {
        var width = 0.0,height = 0.0,depth = 0.0
        var volume:Double {
            return width * height * depth
        }
    }
    let fourByFiveByTwo = Cuboid( 4.0, height: 5.0, depth: 2.0)
    print("the volume of fourByFiveByTwo is (fourByFiveByTwo.volume)")

    属性观察者

    属性观察者会观察并对属性值的变化做出回应。每当一个属性的值被设置时,属性观察者都会被调用,即使这个值与该属性当前的值相同。

    你可以为你定义的任意存储属性添加属性观察者,除了延迟存储属性。你也可以通过在子类里重写属性来为任何继承属性(无论是存储属性还是计算属性)添加属性观察者。

    willSet 会在该值被存储之前调用

    didSet 会在一个新值被存储后调用

    class LightBuld {
        //类变量 不能通过实例引用
        static let maxCurrent = 30
        var current = 0 {
            didSet (oldCurrent){
                if current > LightBuld.maxCurrent {
                    //current = 修改之前的值
                    current = oldCurrent
                    print("功率过大")
                }else if 0 < current && current <= 30 {
                    print("功率适中")
                }else {
                    print("不符合实际情况")
                }
            }
        }
    }
    let buld = LightBuld()
    buld.current = 30
    class LightBuld {
        //类变量 不能通过实例引用
        static let maxCurrent = 30
        var current = 0 {
            //在赋值之前运行的逻辑 newCurrent 将要被赋值的数据
            willSet (newCurrent){
               print("变化了 (abs(newCurrent - current))")
            }
            
            didSet (oldCurrent){
                if current > LightBuld.maxCurrent {
                    //current = 修改之前的值
                    current = oldCurrent
                    print("功率过大")
                }else if 0 < current && current <= 30 {
                    print("功率适中")
                }else {
                    print("不符合实际情况")
                }
            }
        }
    }
    let buld = LightBuld()
    buld.current = 30
    buld.current = 40

    如果我们不在willSet 和 didSet后面的括号中声明变量名称,可以使用系统默认的 对于willSet来说是 newValue 对于didSet来说是oldVlaue。

    enum Theme {
        case DayModel
        case NightModel
    }
    class UI {
        var fontColor:UIColor!
        var backGroundColor:UIColor!
        var themeModel:Theme {
            didSet {
                self.changeModel(themeModel: themeModel)
            }
        }
        
        init(themeModel:Theme) {
            self.themeModel = themeModel
            self.changeModel(themeModel: themeModel)
        }
        
        func changeModel(themeModel:Theme) {
            switch themeModel {
            case .DayModel:
                fontColor = UIColor.black
                backGroundColor = UIColor.white
            case .NightModel:
                fontColor = UIColor.white
                backGroundColor = UIColor.black
            }
        }
    }
    //注意 didSet 和 willSet不会在初始化阶段 也就是init阶段调用的 包括有默认值的时候 也不会调用
    let ui = UI(themeModel: .DayModel)
    ui.fontColor
    ui.backGroundColor
    
    ui.themeModel = .NightModel
    ui.fontColor
    ui.backGroundColor

    全局和局部变量

    上边描述的计算属性和观察属性的能力同样对全局变量和局部变量有效。全局变量是定义在任何函数、方法、闭包或者类型环境之外的变量。局部变量是定义在函数、方法或者闭包环境之中的变量。

    类型属性

    实例属性是属于特定类型实例的属性。每次你创建这个类型的新实例,它就拥有一堆属性值,与其他实例不同。

    你同样可以定义属于类型本身的属性,不是这个类型的某一个实例的属性。这个属性只有一个拷贝,无论你创建了多少个类对应的实例。这样的属性叫做类型属性。

    存储类型属性可以是变量或者常量。计算类型属性总要被声明为变量属性,与计算实例属性一致。

    类型属性语法

    class Player {
        var name:String
        var score = 0
      //类属性
    static var highestScore = 0 init(name:String) { self.name = name } func play() { let score:Int = Int(arc4random()%100) print("(name) played and got (score) scores") self.score += score print("Total score of (name) is (self.score)") if self.score > Player.highestScore { Player.highestScore = self.score } print("所有玩家的最高分是(Player.highestScore)") } } let player1 = Player(name: "player1") let player2 = Player(name: "player2") player1.play() player2.play() player2.play()

    类方法

    通过在func前添加static 可以声明一个类方法 在类(class)中也可以使用关键字class 达到同样的目的

    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 != row {
                    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:"	")
                }
                print()
            }
        }
        
        static func identityMatrix(n:Int) ->Matrix?{
            if n <= 0 {
                return nil
            }
            var arr2d:[[Int]] = []
            for i in 0..<n {
                var row = [Int](repeatElement(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: 4) {
        e.printMatrix()
    }
  • 相关阅读:
    BZOJ 5059: 前鬼后鬼的守护 可并堆 左偏树 数学
    BZOJ 1975: [Sdoi2010]魔法猪学院 大水题 第k短路 spfa
    BZOJ 4003: [JLOI2015]城池攻占 左偏树 可并堆
    BZOJ 3091: 城市旅行 lct 期望 splay
    2018/3/27 省选模拟赛 140分
    BZOJ 2959: 长跑 lct 双联通分量 并查集 splay
    2018/3/26 省选模拟赛 60分
    快速排序
    插入排序法
    快速排序的两种方法
  • 原文地址:https://www.cnblogs.com/huanying2000/p/6398633.html
Copyright © 2011-2022 走看看