swift之属性与单例模式
一、属性
1.1-swift中的属性
/*
存储属性
类似于成员属性的概念
存储在实例的内存中
结构体、类可以定义存储属性
枚举不可定义存储属性
计算属性
本质就是方法(函数)
不占用实例的内存
*/
struct Test {
var value:Double
var calculate:Double{
set {
value = newValue+1
}
get{
value-1 //单一计算忽略return
}
}
}
var test = Test(value:10)
print(test.value)
print(test.calculate)
test.calculate=20
print(test.value)
print(test.calculate)
10.0
9.0
21.0
20.0
1.2-存储属性
/*
存储属性:
在创建类或者结构体的实例时,必须为所有的存储属性设置一个合适的初始值
可以在构造函数(初始化器)里为存储属性谁在一个初始值
可以分配一个默认的属性值做为属性定义的一部分
*/
struct Test {
var value:Double = 1
}
var test = Test()
print(test.value)
1.0
1.3-计算属性
/*
计算属性:
set传入的新值默认叫做newValue,也可以自定义
只读计算属性:只有get没有set(有set不能没有get)
定义计算属性只能用var不能用let(let代表常量:值是一成不变的)
计算属性的值是可以发生变化的(即使是只读计算属性)
*/
struct Test {
var value:Double
var calculate:Double{
set (testNew){
value = testNew+1
}
get{
value-1
}
}
}
var test = Test(value:10)
print(test.value)
print(test.calculate)
10.0
9.0
//只读计算属性
struct Test {
var value:Double
var calculate:Double{
get{
value-1
}
}
}
1.4-枚举rawValue原理
//枚举原始值rawValue本质是只读计算属性
enum Test:Int {
case up=1,down=2,left,right
var rawValue:Int{
switch self {
case .up:
return 11
case .down:
return 22
case .left:
return 33
case .right:
return 44
}
}
}
print(Test.right.rawValue)
44
1.5-延迟存储属性
/*
延迟存储属性:
使用lazy可以定义一个延迟存储属性,在第一次调用到属性的时候才会进行初始化
lazy属性必须是var(let必须在实例的初始化方法完成之前就拥有值)
如果多条线程同时第一次访问lazy属性时,无法保证属性只被初始化一次
当结构体包含一个延迟存储属性时,只有var才能访问延迟存储属性
因为延迟存储属性初始化时需要改变结构体的内存
*/
class Car{
init(){
print("car init.")
}
func run(){
print("car is running.")
}
}
class Person{
lazy var car = Car()
init() {
print("person init.")
}
func go(){
car.run()
}
}
let p = Person()
print("-----")
p.go()
person init.
-----
car init.
car is running.
//如果没有lazy则输出:
car init.
person init.
-----
car is running.
//经典例子,调用资源的时候需要用到才调用
class PhotoView{
lazy var image:Image={
let url="http://www.test.com/1.jpg"
let data = Data(url:url)
return Image(data:data)
}
}
1.6-属性观察器
/*
可以为非lazy的var存储属性设置属性观察器,
willSet在set即将赋值之前调用,didSet在赋值之后调用
set/get和willSet/didSet不能同时使用
在构造函数(初始化器)中设置属性值不会触发willSet和didSet
willSet新值newValue,didSet旧值oldValue
在属性定义时设置初始值也不会触发
*/
1.7-全局变量局部变量
/*
属性观察器、计算属性的功能,同样可以应用在全局变量、局部变量中
*/
1.8-inout详解
inout(输入参数、指针传递、引用传递)
struct Test {
var a:Int
var b:Int{
set {
a = newValue/2
print("set b value ",newValue)
}
get {
print("get b")
return a * 2
}
}
func show(){
print("a value:\(a),b value:\(b)")
}
}
func test(_ num:inout Int) -> Void{
num = 20
}
var t=Test(a:30)
test(&t.a)
t.show()
0x100000b1f <+111>: leaq -0x30(%rbp), %rsi //将Test.a的地址池引用传递给test函数
0x100000b23 <+115>: movl $0x20, %edx
test(&t.b)
t.show()
0x100001563 <+83>: callq 0x100001620 ; test.Test.b.getter : Swift.Int at main.swift:63
0x100001570 <+96>: callq 0x100001bf0 ; test.test(inout Swift.Int) -> () at main.swift:74
0x100001580 <+112>: callq 0x100001770 ; test.Test.b.setter : Swift.Int at main.swift:59
//如果是计算属性,会先调用test()之前先调用Test.b.get()方法放到临时的局部变量,并传到test(),将值修改后放回局部变量里,再根据新值调用Test.b.set()方法。
/*
总结:
1、如果实参有物理内存地址,且没有设置属性观察器,直接将实参的内存地址传入函数(实参进行引用传递)
2、如果实参是计算属性或者设置了属性观察器,采取copy in copy out的做法,调用该函数时,先复制实参的值,产生副本get,将副本的内存地址传入函数进行引用传递,在函数内部可以修改副本的值,函数返回后再将副本的值覆盖实参的值set。
本质是引用传递(地址传递)
*/
1.9-类型属性
/*
属性可以分为:
实例属性:只能通过实例去访问
存储实例属性,存储在实例的内存中,每个实例都有一份
计算实例属性
类型属性:只能通过类型去访问
存储类型属性,整个程序运行过程中,就只有一份(类似全局变量)
计算类型属性
可以通过static定义类型属性
如果是类,也可以用关键字class
*/
class Test{
static var count:Int = 0
init(){
Test.count += 1
}
}
let t1 = Test()
let t2 = Test()
let t3 = Test()
print(Test.count)
3
/*
不同于存储实例属性,必须给存储类型属性设置初始值,因为类型没有像实例那样init初始化器来初始化存储属性
存储类型属性默认就是lazy,会在第一次使用的时候才初始化
就算被多个线程同时访问,保证只初始化一次
存储类型属性可以是let
枚举类型也可以定义类型属性(存储类型属性、计算类型属性)
*/
二、单例模式
public class FileManager{
public static let shared = FileManager()
private init(){}
}
public class FileManager{
public static let shared = {
//...
//...
return FileManager()
}()
private init(){}
}