Swift 中的属性

作者:Thomas Hanning,原文链接,原文日期:2018-03-15 译者:Sunnyyoung;校对:小铁匠Linusnumbbbbb;定稿:Forelax

Swift 中有两种类型的属性:存储属性与计算属性。存储属性将值(常量或者变量)保存为实例或类型的一部分,而计算属性没有存储值。

提示:这篇文章已经更新至 Swift 4。

存储属性

让我们从存储属性开始看起。想象一下你有一个名为 Circle 的类:

class Circle {

    var radius: Double = 0

}

let circle = Circle()
circle.radius = 10

print("radius: \(circle.radius)") //radius: 10.0
复制代码

Circle 拥有名为 radius 的实例变量,默认值为 0。在 Swift 中,每个实例变量都为一个属性。因此你可以添加所谓的属性观察者。在 Swift 中有两种类型的属性观察者:一种在赋值之前调用,另一种在赋值之后调用。

在赋值后调用的属性观察者采用 didSet 关键字标记。在我们的示例中,你可以使用它来监测新设置的值:

class Circle {
    
    var radius: Double = 0 {
        didSet {
            if radius < 0 {
                radius = oldValue
            }
        }
    }
    
}
 
let circle = Circle()
 
circle.radius = -10
print("radius: \(circle.radius)") //radius: 0.0
 
circle.radius = 10
print("radius: \(circle.radius)") //radius: 10.0
复制代码

在属性观察者中你可以通过变量 oldValue 来访问属性的旧值。

你还可以使用 willSet 属性观察者,它在赋值之前会被调用:

class Circle {
    
    var radius: Double = 0 {
        willSet {
            print("About to assign the new value \(newValue)")
        }
        didSet {
            if radius < 0 {
                radius = oldValue
            }
        }
    }
    
}
 
let circle = Circle()
 
circle.radius = 10 //设置新值 10.0
复制代码

willSet 中,你可以通过变量 newValue 来访问属性的新值。

计算属性

与存储属性不同的是,计算属性并不会存储属性的值。因此在每次调用计算属性时,都要计算该值。在 Circle 类中,你可以将属性 area 定义为计算属性:

class Circle {
    
    var radius: Double = 0 {
        didSet {
            if radius < 0 {
                radius = oldValue
            }
        }
    }
    
    var area: Double {
        get {
            return radius * radius * Double.pi
        }
    }

}

let circle = Circle()
circle.radius = 5

print("area: \(circle.area)") //area: 78.5398163397448
复制代码

计算属性总是需要一个 getter。如果缺少 setter,则该属性被称为只读属性。下面这个例子很好地说明了 setter 的作用:

import Foundation

class Circle {
    
    var radius: Double = 0 {
        didSet {
            if radius < 0 {
                radius = oldValue
            }
        }
    }
    
    var area: Double {
        get {
            return radius * radius * Double.pi
        }
        set(newArea) {
            radius = sqrt(newArea / Double.pi)
        }
    }
    
}

let circle = Circle()

circle.area = 25

print("radius: \(circle.radius)") //radius: 2.82094791773878
复制代码

至此,每次对 area 设置了新的值之后,radius 都会被重新计算。

存储属性的初始化

每个存储属性在它的对象实例化之后都必须有值。属性初始化有两种方法:

  • init 方法中初始化值
  • 给属性设置默认的值

下面的例子同时使用了这两种方法:

class Circle {
    
    var radius: Double
    var identifier: Int = 0
    
    init(radius: Double) {
        self.radius = radius
    }
    
}

var circle = Circle(radius: 5)
复制代码

如果存储属性在对象实例化之后没有值,代码无法通过编译。

懒加载属性

如果具有默认值的存储属性使用了关键字 lazy 标记,则其默认值不会立即初始化,而是在第一次访问该属性时初始化。

因此,如果该属性从未被访问,它将永远不会被初始化。你可以将这种特性应用于一些特别耗费 CPU 或内存的初始化上。

class TestClass {
    
    lazy var testString: String = "TestString"
    
}
 
let testClass = TestClass()
print(testClass.testString) //TestString
复制代码

该属性在被访问之前不会进行初始化。在这个例子中并不容易看出来。但由于初始化也可以在 block 里面实现,我们可以使它更明显一些:

class TestClass {
    
    lazy var testString: String = {
        print("about to initialize the property")
        return "TestString"
    }()
    
}

let testClass = TestClass()
print("before first call")
print(testClass.testString)
print(testClass.testString)
复制代码

这个例子的输出:

before first call
about to initialize the property
TestString
TestString
复制代码

这意味着该 block 仅被调用一次 - 第一次访问该属性的时候。由于存储属性是可变的,因此可以更改初始值。

类型属性

类型属性是类的一部分,但不是实例的一部分,类型属性也被称为静态属性。存储属性和计算属性都可以是类型属性。类型属性的关键字是 static

class TestClass {
    
    static var testString: String = "TestString"
    
}
 
print("\(TestClass.testString)") //TestString
复制代码

如你所见,它们使用类名而不是实例对象来访问它们。此外,由于类型属性没有初始化方法,它总是需要一个默认值。

拥有私有 Setter 的公共属性

正如我在 另一篇文章 中介绍的那样,这是一种常见的情况,你不想提供一个公共的 setter,而是提供一个私有的 setter。这是封装的基本原则。这样只有类本身可以操作该属性,但仍可从类外部访问读取它。

来看下面的例子:

public class Circle {
    
    public private(set) var area: Double = 0
    public private(set) var diameter: Double = 0
    
    public var radius: Double {
        didSet {
            calculateFigures()
        }
    }
    
    public init(radius:Double) {
        self.radius = radius
        calculateFigures()
    }
    
    private func calculateFigures() {
        area = Double.pi * radius * radius
        diameter = 2 * Double.pi * radius
    }
}

let circle = Circle(radius: 5)

print("area: \(circle.area)") //area: 78.5398163397448
print("diameter: \(circle.diameter)") //diameter: 31.4159265358979

circle.area = 10 //编译错误:无法对 'area' 属性进行赋值,因为 setter 方法不可访问
复制代码

这里的属性 areadiameter 可以从类的外部访问,但只能在类内部赋值。为此你必须使用 public private(set) 的组合。根据本人的经验,这个特性在 iOS 开发中很少使用,但它对写出更少 bug 的代码很有帮助。

本文由 SwiftGG 翻译组翻译,已经获得作者翻译授权,最新文章请访问 swift.gg

Swift属性分为常量属性(常量存储属性)、变量属性(变量存储属性)和计算属性(计算型属性)。 1. 常量属性:用let关键字定义的属性,只能在初始化时被赋值一次,并且在之后的运行过程不允许被修改。常量属性通常用于存储不变的值,例如表示圆周率的常量属性。 2. 变量属性:用var关键字定义的属性,可以在初始化后被赋值多次,并且在之后的运行过程也可以被修改。变量属性通常用于存储可变的值,例如表示用户年龄的变量属性。 3. 计算属性:没有直接存储值,而是提供了一个getter和一个可选的setter来间接获取和设置其他属性或变量的值。计算属性可以用于实现一些复杂的逻辑,例如根据用户输入的值计算出其他相关的属性值。 例如,下面是一个包含常量属性、变量属性和计算属性的简单Swift类: ``` class Circle { let pi = 3.14 // 常量属性 var radius: Double // 变量属性 var area: Double { // 计算属性 return pi * radius * radius } init(radius: Double) { self.radius = radius } } ``` 其,Circle类包含了一个常量属性pi表示圆周率,一个变量属性radius表示圆的半径,以及一个计算属性area表示圆的面积,它的值由pi和radius计算而来。在初始化时,radius属性被赋予了一个初始值,之后也可以通过修改radius属性的值来计算不同半径的圆的面积。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值