Swift系列九 - 属性
任何一门语言都有属性的概念。Swift中的属性是怎么的呢?
一、属性
Swift中跟实例相关的属性可以分为2大类:存储属性和计算属性。
1.1. 存储属性(Stored Property)
特点:
- 类似于成员变量的概念;
- 存储在实例的内存中;
- 结构体、类可以定义存储属性;
- 枚举不可以定义存储属性。
示例代码:
struct Circle {
var radius: Double
}
class classCircle {
var radius: Double
}
关于存储属性,Swift有个明确的规定:
在创建类或结构体的实例时,必须为所有的存储属性设置一个合适的初始值。
- 可以在初始化器里为存储属性设置一个初始值;
- 可以分配一个默认的属性值作为属性定义的一部分。
1.2. 计算属性(Computed Property)
特点:
- 本质就是方法(函数);
- 不占用实例的内存;
- 枚举、结构体、类都可以定义计算属性。
示例代码:
struct Circle {
// 存储属性
var radius: Double
// 计算属性
var diameter: Double {
set {
print("set")
radius = newValue / 2
}
get {
print("get")
return radius * 2
}
}
}
var c = Circle(radius: 10)
c.radius = 11
print("--1--")
c.diameter = 40
print("--2--")
print(c.diameter)
/*
输出:
set
get
20.0
*/
输出分析: 上面代码如果执行c.diameter = 40
,radius
的值就会变为20。因为这样会执行diameter
的set
方法(40作为参数),上面的案例看到变量newValue
,但是代码中没有定义这个变量,其实newValue
是set
方法提供的形参,只不过省略没有写而已,完整的set
方法代码应该是set(newValue) {...}
,newValue
是默认值,可以按照自己的规范修改(建议使用默认的形参命名)。c.diameter
调用的是diameter
的get
方法。
内存分析:
上面示例代码中结构体Circle
占用多少内存呢?
print(MemoryLayout<circle>.stride)
// 输出:8
结果显示占用8个字节。因为计算属性的本质是方法。
补充说明:
set
传入的新值默认叫做newValue
,也可以自定义。struct Circle {
var radius: Double
var diameter: Double {
set(newDiameter) {
radius = newDiameter / 2
}
get {
return radius * 2
}
}
}
只读计算属性:只有
get
,没有set
。
如果是只读属性,
get
可以省略不写:struct Circle {
var radius: Double
var diameter: Double { radius * 2 }
}
定义计算属性只能用
var
,不能用let
。
有
set
就必须有get
。
扩展: 枚举
rawValue
的本质就是只读的计算属性。
1.3. 属性观察器(Property Observer)
通过名字就可以联想到OC
中的KVO
,是的,两者确实有相似之处。在Swift
中可以为非lazy
的 var
存储属性 设置属性观察器。
示例代码:
struct Circle {
var radius: Double {
willSet {
print("willSet", newValue)
}
didSet {
print("didSet", oldValue, radius)
}
}
init() {
self.radius = 2.0
print("Circle Init")
}
}
var c = Circle()
// 输出:Circle Init
c.radius = 3.0
/*
输出:
willSet 3.0
didSet 2.0 3.0
*/
分析:
willSet
会传递新值,默认叫做newValue
;didSet
会传递旧值,默认叫做oldValue
;- 在初始化器中设置属性值不会触发
willSet
和didSet
。同样在属性定义时设置初始值也不会触发。
二、延迟存储属性(Lazy Stored Property)
使用lazy
可以定义一个延迟存储属性,在第一次用到属性的时候才会进行初始化。
特点:
lazy
属性必须是var
,不能是let
(let
必须在实例的初始化方法完成之前就拥有值);- 如果多条线程同时第一次访问
lazy
属性,无法保证属性只被初始化1次(非线程安全)。
示例代码:
class Car {
init() {
print("Car init")
}
func run() {
print("car run")
}
}
class Person {
lazy var car = Car()
init() {
print("Person init")
}
func goOut() {
print("Person goOut")
car.run()
}
}
var p = Person()
// 输出:Person init
p.goOut()
/*
输出:
Person goOut
Car init
car run
*/
分析: 如果Person
中的存储属性car
没有lazy
修饰,在创建Person
对象p
的时候就会调用存储属性car
的初始化方法。添加lazy
修饰后,只会在第一次使用car
属性(对象)时进行初始化。
注意点: 当结构体包含一个延迟存储属性时,只有var
才能访问延迟存储属性。因为延迟属性初始化时需要改变结构体的内存,而结构体如果使用let
修饰后就不能修改所在内存。
三、类型属性(Type Property)
严格来说,属性可以分为:
实例属性(Instance Property):只能通过实例去访问
- 存储实例属性(Stored Instance Property):存储在实例的内存中,每个实例都有1份;
- 计算实例属性(Computed Instance Property)
类型属性(Type Property):只能通过类型去访问
- 存储类型属性(Stored Type Property):整个程序运行过程中,就只有1份内存(类似于全局变量)
- 计算实例属性(Computed Type Property)
可以通过static
定义类型属性。如果是类,也可以用关键字class
。
示例代码:
struct Shape {
var width: Int
static var count: Int = 30
}
var s = Shape(width: 10)
s.width = 20
print("before count:\(Shape.count)") // 输出:before count:30
Shape.count = 40
print("after count:\(Shape.count)") // 输出:after count:40
3.1. 类型属性细节
不同于存储实例属性,存储类型属性必须进行初始化,否则报错(因为类型没有像实例那样的
init
初始化器来初始化存储属性):
存储类型属性默认就是
lazy
,会在第一次使用的时候才初始化,就算被多个线程同时访问,保证只会初始化一次(线程安全)。存储类型属性可以是
let
。枚举类型也可以定义类型属性(存储类型属性,计算类型属性)。
3.2. 单例模式
使用类型属性可以创建单例模式。
示例代码:
class FileManager {
public static let shared = FileHandle()
private init() {}
}
var f1 = FileManager.shared;
把初始化器设为private
,这样就无法让外界使用init
创建实例。把类型属性设为public
,在其他文件中也可以访问,存储类型属性再用let
修饰,这样就能保证实例只能指向一块固定内存。
3.2. 类型存储属性的本质
第一步:示例代码
第二步:查看全局变量内存地址
分析:
num1
内存地址:0x1000013f1 + 0x5df7 = 0x1000071E8
;
num2
内存地址:0x1000013fc + 0x5df4 = 0x1000071F0
;
num3
内存地址:0x100001407 + 0x5df1 = 0x1000071F8
。
结论:
num1
,num2
,num3
三个变量的内存地址是连续的。
第三步:查看类型存储属性地址
分析:
num1
内存地址:0x100001013 + 0x631d = 0x100007330
;
Car.count
内存地址:0x100007338
;
num3
内存地址:0x10000105c + 0x62e4 = 0x100007340
。
结论:
num1
,Car.count
,num3
三个变量的内存地址是连续的。
从内寸角度看,类型存储属性写在外面和里面没有什么区别,写在类里面只是代表该属性有一定访问权限。
类型存储属性默认是lazy
,所以在第一次访问的时候做了很多操作。而且只被初始化一次。
通过汇编查看类型存储属性初始化:
发现,类型属性初始化最终调用的是GCD
中的dispatch_once
,这样就保证了属性只被初始化一次。
Swift系列九 - 属性的更多相关文章
- struts2官方 中文教程 系列九:Debugging Struts
介绍 在Struts 2 web应用程序的开发过程中,您可能希望查看由Struts 2框架管理的信息.本教程将介绍两种工具,您可以使用它们来查看.一个工具是Struts 2的配置插件,另一个是调试拦截 ...
- Bing Maps进阶系列九:使用MapCruncher进行地图切片并集成进Bing Maps
Bing Maps进阶系列九:使用MapCruncher进行地图切片并集成进Bing Maps 在Bing Maps开发中,由于各种应用功能的不同,更多的时候用户可能需要将自己的一部分图片数据作为地图 ...
- XAML实例教程系列 - 依赖属性和附加属性(四)
XAML实例教程系列 - 依赖属性和附加属性 2012-06-07 13:11 by jv9, 1479 阅读, 5 评论, 收藏, 编辑 微软发布Visual Studio 2012 RC和Wind ...
- 爬虫系列(九) xpath的基本使用
一.xpath 简介 究竟什么是 xpath 呢?简单来说,xpath 就是一种在 XML 文档中查找信息的语言 而 XML 文档就是由一系列节点构成的树,例如,下面是一份简单的 XML 文档: &l ...
- Keil MDK STM32系列(九) 基于HAL和FatFs的FAT格式SD卡TF卡读写
Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...
- 窥探Swift系列博客说明及其Swift版本间更新
Swift到目前为止仍在更新,每次更新都会推陈出新,一些Swift旧版本中的东西在新Swift中并不适用,而且新版本的Swift会添加新的功能.到目前为止,Swift为2.1版本.去年翻译的Swift ...
- 李洪强iOS开发Swift篇—09_属性
李洪强iOS开发Swift篇—09_属性 一.类的定义 Swift与Objective-C定义类的区别 Objective-C:一般需要2个文件,1个.h声明文件和1个.m实现文件 Swift:只需要 ...
- java基础解析系列(九)---String不可变性分析
java基础解析系列(九)---String不可变性分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---In ...
- java多线程系列(九)---ArrayBlockingQueue源码分析
java多线程系列(九)---ArrayBlockingQueue源码分析 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 j ...
随机推荐
- joda-time的简单使用及mysql时间函数的使用(今天,本周,本月)
近期在做一些首页的统计数据复习了下mysql的时间函数,以及后续修改成 传入时间查询时使用的joda-time 软件简介 JodaTime 提供了一组Java类包用于处理包括ISO8601标准在内的d ...
- (十四)docker exec 详解
1. 作用 在运行的容器中执行命令 2. 语法 docker exec [OPTIONS] CONTAINER COMMAND [ARG...] OPTIONS说明: -d :分离模式: 在后台运行 ...
- (二十)VMware Harbor - API
可以用swagger在线解析 http://editor.swagger.io/将swagger.yaml中的内容拷贝到里面即可. 官方文档说明链接如下:https://github.com/vmwa ...
- 2021 DevOpsDays 东京站完美收官 | CODING 专家受邀分享最新技术资讯
DevOpsDays 是一个全球知名的系列技术会议品牌,内容涵盖了软件开发.自动化.测试.安全.组织文化以及 IT 运营的社区会议等.DevOpsDays 由 DevOps 之父 Patrick De ...
- Day02_13_Javadoc_生成帮助文档
JavaDoc 命令:javadoc -encoding UTF-8 -charset UTF-8 Doc.java 执行该命令后,会在java目录生成index.html打开就可以看到生成的文档了 ...
- C#搞个跨平台的桌面NES游戏模拟器
支持Windows,Mac,Linux NES模拟器内核源码来自 https://github.com/colinvella/EmuNes 他这边的源码功能很完善了的,支持视频录制,手柄,金 ...
- 另类的曲线方式定时Start up/Shut down VM 的解决方案
一,引言 最近看到一位小兄弟在为了做 Azure 云虚拟机的自动关机开启 在群里求助,最后也不知道结果咋样了. 至于他提到的利用 Automation Account 我是没有接触过,并且也没有看资料 ...
- MYSQL中TIMESTAMP类型的默认值理解
MYSQL中TIMESTAMP类型可以设定默认值,就像其他类型一样. 1.自动UPDATE 和INSERT 到当前的时间:表:----------- Table Create Table ...
- 7 IDEA连接数据库
IDEA连接数据库 连接成功后,选择数据库 查看数据库/表的内容就双击数据库 修改数据库--要点击DB才能保存 出现问题 错误描述 Server returns invalid timezone. G ...
- Pytest自动化测试-简易入门教程(03)
今天分享内容的重点,和大家来讲一下我们的测试框架--Pytest 讲到这个框架的话呢,可能有伙伴就会问老师,我在学习自动化测试过程中,我们要去学一些什么东西? 第一个肯定要学会的是一门编程语言,比如说 ...