一. 屬性
Swift中跟實(shí)例相關(guān)的屬性可以分為兩大類:
存儲(chǔ)屬性(Stored Property):
- 類似于成員變量這個(gè)概念
- 存儲(chǔ)在實(shí)例的內(nèi)存中
- 結(jié)構(gòu)體、類可以定義存儲(chǔ)屬性
- 枚舉不可以定義存儲(chǔ)屬性
計(jì)算屬性(Computed Property):
- 本質(zhì)就是方法(函數(shù))
- 不占用實(shí)例的內(nèi)存
- 枚舉蝙茶、結(jié)構(gòu)體艺骂、類都可以定義計(jì)算屬性
struct Circle{
//存儲(chǔ)屬性
var radius: Double
//計(jì)算屬性
var diameter: Double{
set {
radius = newValue/2
}get{
radius * 2
}
}
}
存儲(chǔ)屬性
關(guān)于存儲(chǔ)屬性,Swift有個(gè)明確的規(guī)定
在創(chuàng)建類或結(jié)構(gòu)體的實(shí)例時(shí)隆夯,必須為所有的存儲(chǔ)屬性設(shè)置一個(gè)合適的初始值
- 可以在初始化器里為存儲(chǔ)屬性設(shè)置一個(gè)初始值
- 可以分配一個(gè)默認(rèn)的屬性值作為屬性定義的一部分
計(jì)算屬性
- set傳入的新值默認(rèn)叫做newValue钳恕,也可以自定義
struct Circle{
var radius: Double
var diameter: Double{
set (newDiamater){
radius = newDiamater/2
}get{
radius * 2
}
}
}
- 定義計(jì)算屬性只能用var,不能用let
??let代表常量:值是一成不變的
??計(jì)算屬性的值是可能發(fā)生變化的(即使是只讀計(jì)算屬性)
- 只讀計(jì)算屬性:只有g(shù)et蹄衷,沒有set
struct Circle{
var radius: Double
var diameter: Double{
get{
radius * 2
}
}
}
//只有g(shù)et的話可以省略get關(guān)鍵字
struct Circle{
var radius: Double
var diameter: Double{
radius * 2
}
}
枚舉rawValue的原理
枚舉原始值rawValue的本質(zhì)是:只讀計(jì)算屬性
enum TestEnum : Int {
case test1 = 1,test2,test3
//這里對(duì)rawValue進(jìn)行了重寫
var rawValue: Int{
switch self {
case .test1:
return 10
case .test2:
return 11
case .test3:
return 12
}
}
}
print(TestEnum.test3.rawValue)//12
延遲存儲(chǔ)屬性(Lazy Stored Property)
使用lazy可以定義一個(gè)延遲存儲(chǔ)屬性忧额,在第一次用到屬性的時(shí)候才會(huì)進(jìn)行初始化
- lazy屬性必須是var,不能是let愧口,因?yàn)閘et必須在實(shí)例的初始化方法完成之前就擁有值
- 如果多條線程同時(shí)第一次訪問lazy屬性睦番,無法保證屬性只被初始化一次
class Car{
init() {
print("Car init")
}
func run() {
print("Car is running")
}
}
class Person{
lazy var car = Car()
init() {
print("Person init")
}
func goOut() {
car.run()
}
}
let p = Person()
p.goOut()
//打印結(jié)果
//Person init
//Car init
//Car is running
當(dāng)結(jié)構(gòu)體包含一個(gè)延遲存儲(chǔ)屬性時(shí),只有var才能訪問延遲存儲(chǔ)屬性耍属。因?yàn)檠舆t屬性初始化時(shí)需要改變結(jié)構(gòu)體的內(nèi)存
struct Point{
var x = 0
var y = 0
lazy var z = 0
}
//let的p無法訪問到延遲存儲(chǔ)屬性
//let p = Point()
//print(p.z)
var p = Point()
print(p.z)//0
屬性觀察器
可以為非lazy的var存儲(chǔ)屬性設(shè)置屬性觀察器
struct Circle{
var radius: Double{
willSet{
print("willSet",newValue)
}
didSet{
print("didSet",oldValue)
}
}
init() {
radius = 1.0
print("Circle init")
}
}
var circle = Circle()
circle.radius = 2.5
print(circle.radius)
//打印結(jié)果
//Circle init
//willSet 2.5
//didSet 1.0
//2.5
- willSet會(huì)傳遞新值抡砂,默認(rèn)叫newValue大咱,也可以自定義
- didSet會(huì)傳遞舊值,默認(rèn)叫oldValue注益,也可以自定義
- 在初始化器中設(shè)置屬性值不會(huì)觸發(fā)willSet和didSet
- 在屬性定義時(shí)設(shè)置初始值也不會(huì)觸發(fā)willSet和didSet
全局變量碴巾、局部變量
屬性觀察期、計(jì)算屬性的功能丑搔,同樣可以應(yīng)用在全局變量厦瓢、局部變量上
//全局計(jì)算屬性
var num: Int{
get{
10
}
set{
print("setNum",newValue)
}
}
num = 22//setNum 22
print(num)//10 由于計(jì)算屬性不具有存儲(chǔ)功能,之前設(shè)置的值沒有保存啤月,所以還是直接拿的get返回的值
func test(){
var age = 10{
willSet{
print("willSet",newValue)
} didSet{
print("didSet",oldValue)
}
}
age = 11
}
test()
//willSet 11
//didSet 10
inout的再次研究
struct Shape{
var width: Int
var side: Int{
willSet{
print("willSetSide",newValue)
}didSet{
print("didSetSide",oldValue,side)
}
}
var girth: Int{
set {
width = newValue/side
print("setGirth",newValue)
}
get{
print("getGirth")
return width*side
}
}
func show() {
print("width=\(width),side=\(side),girth=\(girth)")
}
}
func test(_ num: inout Int){
num = 20
}
var s = Shape(width: 10, side: 4)
test(&s.width)
s.show()
print("-----------")
test(&s.side)
s.show()
print("-----------")
test(&s.girth)
s.show()
//打印結(jié)果
//getGirth
//width=20,side=4,girth=80
//-----------
//willSetSide 20
//didSetSide 4 20
//getGirth
//width=20,side=20,girth=400
//-----------
//getGirth
//setGirth 20
//getGirth
//width=1,side=20,girth=20
inout的本質(zhì)總結(jié):
- 如果實(shí)參有物理內(nèi)存地址煮仇,且沒有設(shè)置屬性觀察值,直接將實(shí)參的內(nèi)存地址傳入函數(shù)(實(shí)參進(jìn)行引用傳遞)
- 如果實(shí)參是計(jì)算屬性或者設(shè)置了屬性觀察值谎仲,采用了Copy In Copy Out的做法浙垫,即:
??1.調(diào)用該函數(shù)時(shí),先復(fù)制實(shí)參的值郑诺,產(chǎn)生副本(get)
??2.將副本的內(nèi)存地址傳入函數(shù)(副本進(jìn)行引用傳遞)夹姥,在函數(shù)內(nèi)部可以修改副本的值
??3.函數(shù)返回后,再將副本的值覆蓋實(shí)參的值(set)
總結(jié):inout的本質(zhì)就是引用傳遞(地址傳遞)
類型屬性(Type Property)
嚴(yán)格來說辙诞,屬性可以分為:
實(shí)例屬性(Instance Property):只能通過實(shí)例去訪問
??存儲(chǔ)實(shí)例屬性(Stored Instance Property):存儲(chǔ)在實(shí)例的內(nèi)存中辙售,每個(gè)實(shí)例都有1份
??計(jì)算實(shí)例屬性(Computed Instance Property)類型屬性(Type Property):只能通過類型去訪問
??存儲(chǔ)類型屬性(Stored Type Property):整個(gè)程序運(yùn)行過程中,就只有1份內(nèi)存(類似于全局變量)
??計(jì)算類型屬性(Computed Type Property)
可以通過static定義類型屬性飞涂,如果是類旦部,也可以用關(guān)鍵字class
struct Car{
static var count: Int = 0
init() {
Car.count += 1
}
}
let c1 = Car()
let c2 = Car()
let c3 = Car()
print(Car.count)//3
類型屬性細(xì)節(jié):
- 不同于存儲(chǔ)實(shí)例屬性,必須給存儲(chǔ)類型屬性設(shè)置初始值较店,因?yàn)轭愋蜎]有像實(shí)例那樣的init初始化器來初始化存儲(chǔ)屬性
- 存儲(chǔ)類型屬性默認(rèn)就是lazy士八,會(huì)在第一次使用的時(shí)候才初始化,就算被多個(gè)線程同時(shí)訪問梁呈,保證只會(huì)初始化一次婚度;存儲(chǔ)類型屬性可以是let
- 枚舉類型也可以定義類型屬性(存儲(chǔ)類型屬性、計(jì)算類型屬性)
單例模式
public class FileManager{
public static let shared = FileManager()
private init(){}
}
public class FileManager{
public static let shared = {
FileManager()
}()
private init(){}
}
二.方法
方法(Method)
- 枚舉捧杉、結(jié)構(gòu)體、類都可以定義實(shí)例方法秘血、類型方法
- 實(shí)例方法(Instance Method):通過實(shí)例對(duì)象調(diào)用
- 類型方法(Type Method):通過類型調(diào)用味抖,用static或者class關(guān)鍵字定義
struct Car{
static var count: Int = 0
init() {
Car.count += 1
}
//大括號(hào)中的count等價(jià)于self.count,Car.self.count,Car.count
static func getCount() -> Int { count }
}
let c1 = Car()
let c2 = Car()
let c3 = Car()
print(Car.getCount())//3
self:
- 在實(shí)例方法中代表實(shí)例對(duì)象
- 在類型方法中代表類型
mutating
結(jié)構(gòu)體和枚舉是值類型,默認(rèn)情況下灰粮,值類型的屬性不能被自身的實(shí)例方法修改
在func關(guān)鍵字前面加mutating可以允許這種修改行為
struct Point{
var x = 0.0
var y = 0.0
mutating func moveBy(deltaX: Double,deletaY: Double){
x += deltaX
y += deletaY
}
}
enum StateSwitch{
case low,middle,high
mutating func next(){
switch self {
case .low:
self = .middle
case .middle:
self = .high
case .high:
self = .low
}
}
}
@discardableResult
在func前面加個(gè)@discardableResult仔涩,可以消除:函數(shù)調(diào)用后返回值未被使用的警告
struct Point{
var x = 0.0
var y = 0.0
@discardableResult mutating func moveBy(deltaX: Double,deletaY: Double) -> Point{
x += deltaX
y += deletaY
return self
}
}
var p = Point()
p.moveBy(deltaX: 0.4, deletaY: 0.7)
@discardableResult func get() -> Int{
return 10
}
get()
三. 下標(biāo)
下標(biāo)(subscript)
使用subscript可以給任意類型(枚舉、結(jié)構(gòu)體粘舟、類)增加下標(biāo)功能熔脂,有些地方也翻譯為:下標(biāo)腳本
subscript的語法類似于實(shí)例方法佩研、計(jì)算屬性,本質(zhì)就是方法(函數(shù))
class Point{
var x = 0.0
var y = 0.0
subscript(index: Int) -> Double{
set {
if index == 0 {
x = newValue
} else if index == 1 {
y = newValue
}
}
get{
if index == 0 {
return x
} else if index == 1 {
return y
}
return 0
}
}
}
var p = Point()
p[0] = 1.1
p[1] = 2.2
print("p.x=\(p.x),p.y=\(p.y),p[0]=\(p[0]),p[1]=\(p[1])")
//打印結(jié)果
//p.x=1.1,p.y=2.2,p[0]=1.1,p[1]=2.2
- subscript中定義的返回值類型決定了
??get方法的返回值類型
??set方法中newValue的類型
subscript可以接受多個(gè)參數(shù)霞揉,并且類型任意
subscript可以沒有set方法旬薯,但必須有g(shù)et方法,如果只有g(shù)et方法适秩,可以省略get
class Point{
var x = 0.0
var y = 0.0
subscript(index: Int) -> Double{
if index == 0 {
return x
} else if index == 1 {
return y
}
return 0
}
}
- 可以設(shè)置參數(shù)標(biāo)簽
class Point{
var x = 0.0
var y = 0.0
subscript(index i: Int) -> Double{
if i == 0 {
return x
} else if i == 1 {
return y
}
return 0
}
}
var p = Point()
p.y = 2.2
print(p[index: 1])//2.2
- 下標(biāo)可以是類型方法
class Sum{
static subscript(v1: Int,v2: Int) -> Int{
v1 + v2
}
}
print(Sum[10,20])//30
結(jié)構(gòu)體绊序、類作為返回值
class Point{
var x = 0.0
var y = 0.0
}
class PointManager {
var point = Point()
subscript(index: Int) -> Point{
point
}
}
var pm = PointManager()
pm[0].x = 11
pm[0].y = 22
print(pm[0])//__lldb_expr_26.Point
print(pm.point)//__lldb_expr_26.Point
struct Point{
var x = 0.0
var y = 0.0
}
class PointManager {
var point = Point()
subscript(index: Int) -> Point{
set { point = newValue }
get { point }
}
}
var pm = PointManager()
pm[0].x = 11
pm[0].y = 22
print(pm[0])//Point(x: 11.0, y: 22.0)
print(pm.point)//Point(x: 11.0, y: 22.0)
接收多個(gè)參數(shù)的下標(biāo)
class Grid{
var data = [
[0,1,2],
[3,4,5],
[6,7,8]
]
subscript(row: Int, column: Int) -> Int{
set{
guard row >= 0 && row < 3 && column >= 0 && column < 3 else {
return
}
data[row][column] = newValue
} get {
guard row >= 0 && row < 3 && column >= 0 && column < 3 else {
return 0
}
return data[row][column]
}
}
}
var grid = Grid()
grid[0,1] = 77
grid[1,2] = 88
grid[2,0] = 99
print(grid.data)//[[0, 77, 2], [3, 4, 88], [99, 7, 8]]