任何一门语言都有属性的概念。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 = 11print("--1--")c.diameter = 40print("--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 Initc.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 initp.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 = 20print("before count:\(Shape.count)") // 输出:before count:30Shape.count = 40print("after count:\(Shape.count)") // 输出:after count:403.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,这样就保证了属性只被初始化一次。
原文转载:http://www.shaoqun.com/a/762814.html
丰趣海淘:https://www.ikjzd.com/w/1716
汇通达:https://www.ikjzd.com/w/1758
任何一门语言都有属性的概念。Swift中的属性是怎么的呢?一、属性Swift中跟实例相关的属性可以分为2大类:存储属性和计算属性。1.1.存储属性(StoredProperty)特点:类似于成员变量的概念;存储在实例的内存中;结构体、类可以定义存储属性;枚举不可以定义存储属性。示例代码:structCircle{varradius:Double}classclassCircle{varradius
bsci 认证:https://www.ikjzd.com/w/2339
巴克莱银行:https://www.ikjzd.com/w/2775
ishare:https://www.ikjzd.com/w/2308
Shopee避坑指南丨90%虾皮新手卖家常犯的错误有哪些?:https://www.ikjzd.com/articles/127237
我当女友面跟寡妇上司上演激情戏:http://lady.shaoqun.com/m/a/270606.html
亚马逊推出FBA库存新功能!卖家有望申请仓储扩容!:https://www.ikjzd.com/articles/132136
没有评论:
发表评论