浅谈 Swift 中的属性(Property)

2018-04-28  本文已影响4人  枫叶1234

前言

Swift 中的属性分为存储属性与计算属性,存储属性即为我们平时常用的属性,可以直接赋值使用,而计算属性不直接存储值,而是根据其他(存储)属性计算得来的值。

在其他面向对象的编程语言中,例如 Java 和 Objective-C 中,get 和 set 方法提供了统一、规范的接口,可以使得外部访问或设置对象的私有属性,而不破坏封装性,也可以很好的控制权限(选择性实现 get 或 set 方法)。而 Swift 中似乎并没有见到类似的 get 和 set 方法,而 Swift 使用了一种名为属性观察器的概念来解决该问题。

本文简单介绍下 Swift 中的这两种属性,以及属性观察器。

延迟存储属性

Demo

class ViewController: UIViewController {

    lazy var goods: NSArray? = {
        var goodsArray: NSMutableArray = []

        if let path = Bundle.main.path(forResource: "Goods", ofType: "plist") {
            if let array = NSArray(contentsOfFile: path) {
                for goodsDict in array {
                    goodsArray.add(Goods(goodsDict as! NSDictionary))
                }
                return goodsArray
            }
        }

        return nil
    }()

    // 这样也是允许的,可以把初始化的代码直接放在构造方法中
    lazy var testLazy = Person()
}

class Person {}

可以在延迟存储属性运算的代码中加入打印语句,即可验证其何时初始化。

Lazy 初始化的「演变」过程

struct Person {
    var name = ""

    init() {
        print(#function)
    }
}

// 直接初始化
let p1 = Person()

// 利用闭包
let getOnePerson = { () -> Person in
    let p = Person()
    return p
}

let p2 = getOnePerson()

// 闭包执行
let p3 = { () -> Person in
    let p = Person()
    return p
}()

// 简化
let p4: Person = {
    let p = Person()
    return p
}()

Lazy 方法

let data = 0...3
let result = data.lazy.map { (i: Int) -> Int in
    print("Handling...")
    return i * 2
}

print("Begin:")
for i in result {
    print(i)
}
// OUTPUT:
// Begin:
// Handling...
// 0
// Handling...
// 2
// Handling...
// 4
// Handling...
// 6

devxoul/Then

Demo

ViewController.swift

import UIKit
import Then

class ViewController: UIViewController {
    lazy var myLabel = UILabel().then {
        $0.text = "My Label"
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        myLabel.frame = CGRect(x: 0.0, y: 0.0,
                               width: 100.0, height: 100.0)
        view.addSubview(myLabel)
    }
}

Source Code

extension Then where Self: AnyObject {

  /// Makes it available to set properties with closures just after initializing.
  public func then(_ block: (Self) -> Void) -> Self {
    block(self)
    return self
  }

}

with()

extension Then where Self: Any {

  /// Makes it available to set properties with closures just after initializing and copying the value types.
  public func with(_ block: (inout Self) -> Void) -> Self {
    var copy = self
    block(&copy)
    return copy
  }

}

do()

extension Then where Self: Any {
  /// Makes it available to execute something with closures.
  public func `do`(_ block: (Self) -> Void) {
    block(self)
  }

}

计算属性

Demo

struct Point {
    var x = 0.0
    var y = 0.0
}

struct Rectangle {
    var width = 0.0
    var height = 0.0
    var origin = Point()

    // 只读计算属性
    var size: Double {
        get {
            return width * height
        }
    }

    // 只读计算属性简写为
//    var size: Double {
//        return width * height
//    }

    var center: Point {
        get {
            return Point(x: origin.x + width / 2,
                         y: origin.y + height / 2)
        }

        set(newCenter) {
            origin.x = newCenter.x - width / 2
            origin.y = newCenter.y - height / 2
        }

        // 便捷 setter 声明
//        set {
//            origin.x = newValue.x - width / 2
//            origin.y = newValue.y - height / 2
//        }
    }

}

var rect = Rectangle()
rect.width = 100
rect.height = 50
print(rect.size)

rect.origin = Point(x: 0, y: 0)
print(rect.center)

rect.center = Point(x: 100, y: 100)
print(rect.origin)

// 5000.0
// Point(x: 50.0, y: 25.0)
// Point(x: 50.0, y: 75.0)

综上,getter 可以根据存储属性推算计算属性的值,setter 可以在被赋值时根据新值倒推存储属性,但它们与我们在其他语言中的 get/set 方法却不一样。

属性观察器

Demo

struct Animal {
    // internal 为默认权限,可不加
    internal private(set) var privateSetProp = 0

    var hungryValue = 0 {
        // 设置前调用
        willSet {
            print("willSet \(hungryValue) newValue: \(newValue)")
        }

        // 设置后调用
        didSet {
            print("didSet \(hungryValue) oldValue: \(oldValue)")
        }

        // 也可以自己命名默认的 newValue/oldValue
        // willSet(new) {}
        // didSet(old) {}
    }
}

var cat = Animal()

// private(set) 即只读
// cat.privateSetProp = 10
print(cat.privateSetProp)

cat.hungryValue += 10
print(cat.hungryValue)

// 0
// willSet 0 newValue: 10
// didSet 10 oldValue: 0
// 10

总结

Swift 的这几个 feature 我未曾在其他语言中见过,对于初学者确实容易造成凌乱。特别是 getter/setter 以及属性观察器中均没有代码提示,容易造成手误,代码似乎也变得臃肿。但是熟悉之后,这些也都能完成之前的功能,甚至更加细分。保持每一部分可控,也使得整个程序更加严谨,更加安全。

上一篇 下一篇

猜你喜欢

热点阅读