余晖落尽暮晚霞,黄昏迟暮远山寻
本站
当前位置:网站首页 > 编程知识 > 正文

Swift 中的属性(swift语法 简书)

xiyangw 2022-12-03 12:00 24 浏览 0 评论

Swift 中的属性

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

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

Swift 中的属性(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 方法不可访问
复制代码

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

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

相关推荐

前后端分离 Vue + NodeJS(Koa) + MongoDB实践

作者:前端藏经阁转发链接:https://www.yuque.com/xwifrr/gr8qaw/vr51p4写在前面闲来无事,试了一下Koa,第一次搞感觉还不错,这个项目比较基础但还是比较完整了,...

MongoDB 集群如何工作?

一、什么是“MongoDB”?“MongoDB”是一个开源文档数据库,也是领先的“NoSQL”数据库,分别用“C++”“编程语言”编写,使用带有“Schema”的各种类似JSON的文档,是也分别被认为...

三部搭建mongo,和mongo UI界面

三步搭建mongo,和mongoUI界面安装首先你需要先有一个docker的环境检查你的到docker版本docker--versionDockerversion18.03.1-ce,b...

Mongodb 高可用落地方案

此落地方案,用于实现高可用。复制集这里部署相关的复制集,用于实现MongoDB的高可用。介绍MongoDB复制集用于提供相关的数据副本,当发生硬件或者服务中断的时候,将会从副本中恢复数据,并进行自动...

一次线上事故,我顿悟了MongoDB的精髓

大家好,我是哪吒,最近项目在使用MongoDB作为图片和文档的存储数据库,为啥不直接存MySQL里,还要搭个MongoDB集群,麻不麻烦?让我们一起,一探究竟,继续学习MongoDB分片的理论与实践,...

IDEA中安装MongoDB插件-再也无要nosql manager for mongodb

大家都知道MongoDB数据库作为典型的非关系型数据库被广泛使用,但基于MongoDB的可视化管理工具-nosqlmanagerformongodb也被用的较多,但此软件收费,所以国内的破解一般...

数据库监控软件Lepus安装部署详解

Lepus安装部署一、软件介绍Lepus是一套开源的数据库监控平台,目前已经支持MySQL、Oracle、SQLServer、MongoDB、Redis等数据库的基本监控和告警(MySQL已经支持复...

YAPI:从0搭建API文档管理工具

背景最近在找一款API文档管理工具,之前有用过Swagger、APIManager、Confluence,现在用的还是Confluence。我个人一直不喜欢用Swagger,感觉“代码即文档”,让代...

Mac安装使用MongoDB

下载MongoDB包:https://www.mongodb.com/download-center解压mongodb包手动解压到/usr/local/mongodb文件夹配置Mac环境变量打开环境...

保证数据安全,不可不知道的MongoDB备份与恢复

大家在项目中如果使用MongoDB作为NOsql数据库进行存储,那一定涉及到数据的备份与恢复,下面给大家介绍下:MongoDB数据备份方法在MongoDB中我们使用mongodump命令来备...

MongoDB数据备份、还原脚本和定时任务脚本

备注:mongodump和mongorestore命令需要在MongoDB的安装目录bin下备份脚本备份格式/usr/local/mongodb/bin/mongodump -h ...

等保2.0测评:mongoDB数据库

一、MongoDB介绍MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB是一个介于关系数据库和非关系数据库之间的产...

MongoDB入门实操《一》

什么是MongoDBMongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB是一个介于关系数据库和非关系数据库之...

Python安装PyMongo的方法详细介绍

欢迎点击右上角关注小编,除了分享技术文章之外还有很多福利,私信学习资料可以领取包括不限于Python实战演练、PDF电子文档、面试集锦、学习资料等。前言本文主要给大家介绍的是关于安装PyMongo的...

第四篇:linux系统中mongodb的配置

建议使用普通用户进行以下操作。1、切换到普通用户odysee。2、准备mongodb安装包,自行去官网下载。3、解压安装包并重命名为mongodb4.04、配置mongodbcdmongod...

取消回复欢迎 发表评论: