標簽(空格分隔): IOS-Swift
[toc]
簡要說明
名稱 | 功能 |
---|---|
set | 計算屬性的賦值方法,設置屬性的時候調用 |
get | 計算屬性的讀取方法,取屬性值得時候調用 |
willSet(newValue) | 監視屬性的方法,在屬性值將要改變的時候調用,參數附帶即將改變的值 |
didSet(oldValue) | 監視屬性的方法,在屬性值改變后調用,參數附帶改變之前的值 |
? 和 ! | 屬性的可空標識符. ?表示可空, !表示強制拆包 |
set和get
在OC中屬性是由set和get方法來實現的,也就是說在打點調用屬性的時候實際上是對屬性的set和get方法進行調用,通過setget方法來實現對類中一個全局變量的讀寫,即時沒有寫set和get方法這兩個方法也是隱式存在的.下面是OC中屬性的實現原型
//普通方式聲明屬性
@property(nonatomic,copy)NSString * str;
//------------------------------------
xx.h
//內部原理
{
NSString * str;
}
- (void)setObject:(NSString *)anStr;
- (NSString *)getStr;
xx.M
{
NSString * _object;
} - (void)setObject:(NSString *)anObject{
_object = anObject;
} - (NSString *)getObject{
return _object;
}
看的出來,OC中得屬性實際上是對類內部的一個全局變量的操作.我們可以通過重寫set和get方法來實現屬性變化的監聽,屬性的隱式修改等等.
而在swift中set和get方法并不是必須得.申明屬性是直接生成一個存儲屬性,并不存在隱式的set和get方法,能夠使用set和get方法的只有計算屬性.計算屬性本身并非一個值,只是一個能夠通過打點調用set和get方法的假屬性,計算屬性并不能儲存任何數據且必須實現get方法
class Object: NSObject {
//聲明一個計算屬性
var age:Int{
get{
print("get");
}
set(newValue){
print("(newValue)")
}
}
}
//下面對這個屬性進行使用
let man = People();
man.age = 10;
print("(man.age)")
輸出結果:
set:10 //設置age的值 觸發set方法并將值傳遞給set方法的參數newValue
get //輸出age的值觸發了get方法
0 //輸出age的值為0
可以看出 首先調用set方法 傳遞設置的值,newValue就是傳遞進來的值,設置完成后接著輸出man.age屬性,觸發了get方法的調用,最后輸出age的值,輸出結果為0,也就是說并沒有將設置的值保存起來.再來觀察一下OC的屬性原理,是不是和這個有點類似,如果在swift中創建一個私有的全局變量來儲存set的值,讀取的時候再返回這樣就不就和OC中一樣了嗎?
private var newAge = 0;
var age:Int{
get{
print("get")
return newAge
}
set(newValue){
newAge = newValue;
print("set:(newValue)")
}
}
輸出結果:
set:10
get
10
好了 這樣就還原了OC中的屬性格式,在這兩個方法中我們可以對屬性的值進行處理,由于swift中默認屬性沒有set和get方法 所以我們無法重寫父類屬性的set和get方法
.
willSet和didSet
這兩個方法屬于屬性觀察者.分別在屬性值將要被設置和被設置以后調用.屬于基本屬性的方法,可以重寫父類屬性的這兩個方法實現原有OC中重寫set方法的功能.區別是初始化屬性值的時候不會調用這兩個方法.原理更加類似于通知,所以被成為屬性觀察者.
//創建一個觀察者屬性
var name:String = "0"{
willSet(new){
print("will:\(new)");
}
didSet(old){
print("did:\(old)");
}
}
//調用
var man = People()
man.name = "sad";
print("\(man.name)");
//輸出結果
輸出結果::
will:sad //將要設置值得時候調用,傳遞將要設置的值
did:0 //設置完畢,傳遞一個設置之前的值
sad //最終的值
測試結果顯示,在第一次屬性初始化為0的時候,觀察者方法并沒有調用,只有外部使用改變name值的時候才調用了觀察者方法.
?和!
可空屬性
: 關鍵點,每次使用 !
強制解包前都應該判斷下是否為空
在swift中所有的屬性變量默認都不能為空,如果不可避免的可能造成為空屬性.則需要聲明為可空屬性.
var name:string = "王五花" //非空屬性,此屬性不可接受空值
var name:string? //可空屬性,此屬性可以接受空值 我們主要介紹這種
swift中空屬性和OC中的空屬性不同,雖然可以直接使用name == nil
或name != nil
來判斷,但是不可直接使用里面的值進行賦值操作.這里就要說一下這個箱子概念了,當一個屬性為可空的時候,這個屬性被放到一個箱子中,這個箱子只有空和非空兩種狀態,由于屬性是在箱子中的所以我們無法直接使用這個屬性值,需要對箱子進行拆解操作man.name = name!
如果不拆箱,這個屬性的類型是箱子的類型.
let man:People = People()
//初始化沒有對name進行賦值
if man.name == nil {
print("空")
}
//對屬性進行賦值
man.name = "wang";
print("沒有進行拆包的值:(man.name)")//沒有使用"!"號進行拆包操作
if man.name != nil {
print("拆包以后的值:(man.name!)")//使用了"!"拆包
}
//使用屬性進行賦值
let str:String = man.name!;
//也可以不拆包使用Optional接受,Optional是一個枚舉變量
let op:Optional = man.name;
print("(op)")
print("str會在接受值的時候強制拆箱操作:(str)")
輸出結果:
空
沒有進行拆包的值:Optional("wang")
拆包以后的值:wang
Optional("wang")
str會在接受值的時候強制拆箱操作:wang
也就是說,當我們聲明一個可空屬性的時候var name:string?
,這個屬性被一個叫做Optional
的箱子裝了起來,這個箱子是允許為空的,使用 man.name == nil
語句來判斷這個箱子里面有沒有值,如果沒有返回空,如果有則不為空.使用man.name = "wang";
來向箱子中存放一個值,這個值必須和聲明時的類型相同,當我們需要使用箱子的值的時候如果直接使用那么得到的是Optional值,并非是箱子中的值,所以要想使用箱子中的值需要使用!
將箱子打開man.name!
使用之前最好判斷下
if let語法
: 使用if let語法可以快速使用可空屬性
普通用法
var str:String?
if str != nil{
var s = str!
pring(s)
}
使用if let語句
var str:String?
if let s = str{//這句的代碼的作用就是當str不為nil時將str解包并賦值給s之后運行{}中的代碼 否則跳過
pring(s)
}
guard語句
: 一個讓人糾結作用的語法糖,很多人都覺得他很簡潔
var str:String?
//這條語句表示在str不為nil的時候 將str的值賦值給s 并跳過{}中的語句 如果str為nil則執行{}中的語句
guard let s = str else{
return
}
還有一種狀態,就是在可空屬性下面還有屬性的時候
class People: NSObject {
var name:Name? //People類的屬性name是個可空的類型
}
class Name: NSObject {
var English:String?//Name類中English也是可空的
var Chinese:String = ""http://Chinese屬性是不可為空的 它有一個默認值.雖然看起來和沒有一樣
}
這時使用name屬性就要格外當心了.雖然系統會提示.
override func viewDidLoad() {
super.viewDidLoad()
let man:People = People()
man.name?.Chinese = "王"http://name屬性此時沒有值,在這里的?
表示嘗試調用name屬性 如果不為空則調用name后面的屬性或方法
print("(man.name?.Chinese)");//輸出看看上面的賦值操作會不會成功 結果為:nil 因為name并沒有值,則不會執行?
后面的語句 輸出的nil是name的值
man.name = Name()//對name進行初始化
man.name?.Chinese = "王五花"http://再次進行嘗試賦值
print("(man.name?.English)")//打印English屬性,此時English沒有值 對name進行嘗試解包,如果name不為空則使用后面的屬性 結果為:nil 此時打開name輸出的是English的值
print("(man.name!.Chinese)")//打印有值得Chinese屬性,這里使用'!'強制解包,因為我們知道anme已經初始化了.絕對不為空 結果為:王五花 此時強制打開name輸出Chinese的值
man.name?.English = "WangWuhua" //嘗試對English進行賦值 這里也可以用'!'強制解包
print("(man.name?.English)") //嘗試調用name不解包English 結果為:Optional("WangWuhua") 這是我們輸出的是English的值,因為沒有解包操作所以還是Optional類型
print("(man.name?.English!)") //嘗試調用name 強制解包English 結果為:Optional("WangWuhua")此時我們強制解包English 滿以為會輸出WangWuhua,但結果并不是我們想象的那樣,所以推斷'?'并不具備解包能力,只是用來推斷name屬性是否為空,由此決定是否調用name下得屬性
print("(man.name!.English)") //強制解包name 不解包English 結果為:Optional("WangWuhua") 這個比較好理解了.沒有對English解包操作
print("(man.name!.English!)") //強制解包name 強制解包English 結果為:WangWuhua 終于得到想要的結果了.必須要對所有屬性進行強制解包才可以拿到最終值,就好像是一套套娃.我們必須將每個娃娃都打開才能看到最里面的東西
}
總結
- set和get方法在Swift中沒有默認創建,在基本屬性中并不存在,所以無法重寫父類的非計算屬性的set和get方法.主要作用于對屬性值在寫入和讀取過程中進行計算操作
- willSet和didSet類似于OC中得KVO,用于監視屬性值的變化
- 可空屬性,使用
var 屬性名:類名?
這種方式可以聲明一個可空屬性, 這里?
表示可空.這樣系統將屬性放到一個箱子中,通過對箱子是否為空的判斷來確定是否有值,如果想要使用箱子中得值必須使用!
來強制打開箱子.如果屬性下面還有屬性需要使用對象.屬性?.屬性A
來對屬性進行一次是否為空得判斷,如果為空則不調用屬性A,如果確定有值可以使用對象.屬性!.屬性A
來得到屬性A的值 如果屬性A也是可空類型 則需要對象.屬性!.屬性A!
將每層的箱子都打開才能得到屬性A的值