Swift入門筆記(二)
列舉
使用enum來建立一個列舉 像類和其他所有命名型別一樣,列舉可以包含方法
enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescription() -> String {
switch self {
case .Ace: return "ace"
case .Jack: return "jack"
case .Queen: return "queen"
case .King: return "king"
default:
return String(self.rawValue)
}
}
}
print(Rank.Six.rawValue)
print(Rank.Jack.simpleDescription())
上例中列舉原始值的型別是Int 只需要設定第一個原始值,剩下的原始值會按照順序賦值 也可以使用字串或者浮點數作為列舉的原始值 使用rawValue屬性來訪問一個列舉成員的原始值
列舉的成員值是實際值,並不是原始值的另一種表達方法 為防原始值沒有意義,可以不設定原始值
結構體
使用struct來建立一個結構體 Swift 中類和結構體有很多共同點
定義屬性用於儲存值 定義方法用於提供功能 定義下標用於通過下標語法訪問值 定義初始化器用於生成初始化值 通過擴充套件以增加預設實現的功能 符合協議以對某類提供標準功能
與結構體相比,類還有如下的附加功能:
繼承:允許一個類繼承另一個類的特徵 型別轉換允許在執行時檢查和解釋一個類例項的型別 取消初始化器允許一個類例項釋放任何其所被分配的資源 引用計數允許對一個類的多次引用
它們之間最大的一個區別就是結構體是傳值,類是傳引用
使用struct來建立一個結構體 所有結構體都有一個自動生成的成員逐一初始化器,用於初始化新結構體例項中成員的屬性。 新例項中各個屬性的初始值可以通過屬性的名稱傳遞到成員逐一初始化器之中
值型別和引用型別
Swift中,型別分為兩類
第一種是值型別,該型別的每個例項持有資料的副本,並且該副本對於每個例項來說是獨一無二的一份,比如結構體(struct)、列舉(enum)、元組(tuple)都是值型別
第二種是引用型別,該型別的例項共享資料唯一的一份副本(在native層面說的話,就是該型別的每個例項都指向記憶體中的同一個地址),類(class)就是引用型別。
它們有什麼不同?
值型別最基本的特點就是複製 這影響到它的賦值、初始化、傳參等操作
struct S {
var data: Int = -1
}
var a = S()
var b = a
a.data = 42
print("\(a.data), \(b.data)")
引用型別的複製行為其實是隱式的建立了一個共享的例項,作為原始型別的引用 所有變數都會引用唯一的那個共享例項的資料,當改變變數中任何一個的資料,都會同樣作用於原始型別的資料
class C { var data: Int = -1 }
var x = C()
var y = x
x.data = 42
print("\(x.data), \(y.data)")
執行結果
值型別用途
- 選擇值型別的一個很重要的原因是可以比較容易的理解和掌控程式碼
- 如果使用值型別,那麼都是和唯一的資料值、型別的副本打交道
- 對資料的修改只作用於資料所屬的型別例項,所以可以不用擔心因為在某個地方對資料的修改而影響到其他地方的資料
- 在多執行緒環境中非常有用,在多執行緒下,不同的執行緒有可能會在不知情的情況下改變資料,發生這種Bug後,除錯起來非常困難
使用值型別的情形:
使用==運算子比較例項資料的時候。 想單獨複製一份例項資料的時候。 當在多執行緒環境下操作資料的時候。
使用引用型別(class)的情形:
當使用Cocoa框架時,很多API都是NSObject的子類,必須要使用引用型別 當使用===運算子判斷兩個物件是否引用同一個物件例項的時候。 當上下文需要建立一個共享的、可變的物件時。
類
使用class和類名來建立一個類 類中屬性的宣告和常量、變數宣告一樣,唯一的區別就是它們的上下文是類 方法和函式宣告也一樣 函式:通過名字來進行直接呼叫,獨立的功能模組 方法:通過名字來進行呼叫,但它跟一個類物件相關聯,可以操作類內部的資料
建立類Shape
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
建立一個類的例項
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
要點
- 系統要求儲存屬性必須初始化
- 可選值可以不初始化,預設將設定為nil
- 如果非可選型別儲存屬性不設定預設值,則必須在初始化方法中對其進行初始化如果非可選型別儲存屬性不設定預設值,則必須在初始化方法中對其進行初始化
- 類必須自己寫初始化方法,用來初始化沒有預設值的非可選儲存屬性類必須自己寫初始化方法,用來初始化沒有預設值的非可選儲存屬性
- 結構體系統預設會新增初始化方法,也可以自定義結構體系統預設會新增初始化方法,也可以自定義
- 子類如果沒有自己的初始化方法,系統預設使用父類的初始化方法,一旦有了自己的初始化方法,或者重寫了父類的初始化方法,則父類的所有初始化不能被子類呼叫子類如果沒有自己的初始化方法,系統預設使用父類的初始化方法,一旦有了自己的初始化方法,或者重寫了父類的初始化方法,則父類的所有初始化不能被子類呼叫
- 可以給子類新增和父類相同的初始化方法,但需要加上override 修飾可以給子類新增和父類相同的初始化方法,但需要加上override 修飾
- 重寫父類的convenience修飾的便利初始化方法,不需要加override重寫父類的convenience修飾的便利初始化方法,不需要加override
類的初始化 使用 init 來建立一個構造器 子類的定義方法是在它們的類名後面加上父類的名字,用冒號分割 建立類的時候並不需要一個標準的根類,所以可以忽略父類。 子類如果要重寫父類的方法,需要用override標記 屬性可以有 getter 和 setter,這稱之為計算屬性 計算屬性 (閉包) 不直接儲存值,而是提供一個 getter 來獲取值,一個可選的 setter 來間接設定其他屬性或變數的值 即:計算屬性可以根據所設定內容,進行一些修改或計算 構造器 init 執行了三步:
- 設定子類宣告的屬性值
- 呼叫父類的構造器呼叫父類的構造器
- 改變父類定義的屬性值,其他的工作比如呼叫方法、getters和setters也可以在這個階段完成改變父類定義的屬性值,其他的工作比如呼叫方法、getters和setters也可以在這個階段完成
這3個步驟順序不能調換,必須按照這個流程來進行初始化
類的初始化器有兩種, 分別是: 1、Designated Initializer(指定初始化器) 指定初始化器是類的最主要的初始化器 它會將類中所有的屬性賦值初始化,並且一路往上呼叫類的父類的指定初始化器去初始化它們各自引入的屬性 類可以有多個指定初始化器,也可只有一個,但必須至少有一個
2、Convenience Initializer(便利初始化器)
1)程式在實際使用過程中,物件和物件的例項變數的值可能不同,可以在 init 方法呼叫的時候傳入一個需要的資料,內部再轉換 2)將物件用一些預設值進行初始化來讓它們適合某種使用場景
便利構造器是一種快速建立物件的方式 本質上是把初始化方法做了一次封裝,方便外界使用 便利初始化器不能被子類重寫或從子類中以super的方式被呼叫 一個類可以沒有便利初始化器
初始化器呼叫規則:
- 子類的指定初始化器必須要呼叫父類的指定初始化器
- 便利初始化器必須呼叫同一類中定義的其他指定初始化器便利初始化器必須呼叫同一類中定義的其他指定初始化器
- 便利初始化器必須最終以呼叫一個指定初始化器結束便利初始化器必須最終以呼叫一個指定初始化器結束
required 關鍵字 required修飾符只能用於修飾類初始化方法。 當子類含有異於父類的初始化方法時(初始化方法引數型別和數量異於父類),子類必須要實現父類的required初始化方法,並且使用required修飾符而不是override 當子類沒有初始化方法時,可以不用實現父類的required方法 最大的好處是可以保證依賴於某個指定初始化方法的便利初始化方法一直可以被使用
可失敗構造器 init初始化方法可以通過在init關鍵字後面加上?或!將其變為可失敗初始化方法 表示某物件的初始化過程可能失敗,並返回 nil 可失敗構造器/初始化方法解決了以前在Swift中只能通過工廠方法捕獲構造或初始化失敗情況的問題 使用可失敗構造器可極大程度的統一Swift中的構造物件語法,消除了構造器與工廠方法之間混亂、重複的冗餘語法,使Swift更加簡潔。 隨著可失敗構造器這一特性的加入,Swift將對大多數Cocoa中帶NSError引數的工廠初始化方法進行調整,從而加強Swift中構造物件語法的統一性,給開發者帶來更好的開發體驗
屬性觀察者 如果不需要計算屬性,但是需要在設定一個新值之前或者之後執行程式碼,可以使用屬性觀察者: willSet didSet 屬性觀察者類似於觸發器,用來監視屬性的除初始化之外的屬性值變化,當屬性值發生改變時可以對此作出響應。 特點:
- 不僅可以在屬性值改變後觸發didSet,也可以在屬性值改變前觸發willSet。
- 給屬性新增觀察者必須要宣告清楚屬性型別,否則編譯器報錯。
- willSet可以帶一個newName的引數,沒有的話,該引數預設命名為newValue。
- didSet可以帶一個oldName的引數,表示舊的屬性,不帶的話預設命名為oldValue。
- 屬性初始化時,willSet和didSet不會呼叫。只有在初始化上下文之外,當設定屬性值時才會呼叫。
- 即使是設定的值和原來值相同,willSet和didSet也會被呼叫
型別檢查和轉換 ==is == 操作符用於判斷一個例項是否是某個類的型別 ==as == 操作符用於轉換一個例項到某個類的型別 返回型別是Bool
is用來做型別檢查 is也可以用來檢查某個類是否遵循了某個協議
as 用來做型別轉換 如果不確定型別轉換能否成功,可以在as後面加問號“?”
//基類,人類
class Human{
}
//男人類
class Man: Human{
}
//女人類
class Woman: Human{
}
let man = Man()
let woman = Woman()
var arr = [man,woman]
for people in arr {
if people is Man {
print("這是個男人")
}else if people is Woman {
print("這是個女人")
}
}
let man = Man()
let woman = Woman()
var arr = [man,woman]
for people in arr {
if let m = people as? Man {
print("這是個男人")
}else if let w = people as? Woman {
print("這是個女人")
}
}
不確定型別 Any 和 AnyObject 型別 AnyObject可以代表任何class型別的例項。 Any可以表示任何型別,包括AnObject和其他資料型別,除了方法型別(function types)。 注意: 只有當明確的需要它的行為和功能時才使用Any和AnyObject 在程式碼裡使用明確的型別比較好