1.關于命名空間
Objective-C 沒有命名空間的,在應用開發時,所有的代碼和引用的靜態庫最終都會被編譯到同一個域和二進制中。這樣的后果是一旦我們有重復的類名的話,就會導致編譯時的沖突和失敗。為了避免這種事情的發生,Objective-C 的類型一般都會加上兩到三個字母的前綴,比如 Apple 保留的 NS和 UI 前綴,各個系統框架的前綴 SK (StoreKit), CG (CoreGraphic) 等。Objective-C 社區的大部分開發者也遵守了這個約定,一般都會將自己名字縮寫作為前綴,把類庫命名為 AFNetworking 或者 MBProgressHUD 這樣。這種做法可以解決部分問題,至少我們在直接引用不同人的庫時沖突的概率大大降低了,但是前綴并不意味著不會沖突,有時候我們確實還是會遇到即使使用前綴也仍然相同的情況。另外一種情況是可能你想使用的兩個不同的庫,分別在它們里面引用了另一個相同的很流行的第三方庫,而又沒有更改名字。在你分別使用這兩個庫中的一個時是沒有問題的,但是一旦你將這兩個庫同時加到你的項目中的話,這個大家共用的第三方庫就會和自己發生沖突了。
在 Swift中,由于可以使用命名空間了,即使是名字相同的類型,只要是來自不同的命名空間的話,都是可以和平共處的。swift中的命名空間的使用不是一個項目,而是需要跨項目,在一個項目中,都是一個命名空間,在同一個命名空間下,所有全局變量或者函數共享,不需要import,從swift開始,官方更多的建議大家使用pod來管理第三方框架(不然拿進來一個框架,整個項目哪哪都能使)
2.構造函數 以及 override 重寫
/**
1.給自己的屬性分配空間并且設置初始值
2.調用父類的構造函數,給父類的屬性分配空間設置初始值
ONObject 沒有屬性,只有一個成員變量 ?"isa"
3.調用父類的構造函數,給父類的屬性分配空間設置初始值
上面兩步的操作與OC中是相反的,OC中是先調用父類的,然后在為屬性賦值
4.如果重載了構造函數,并且沒有實現父類的init方法,系統不再提供init()方法,
*/
class Person: NSObject {
// 'Person' cannot be constructed because it has no accessible initializers
// person類 沒有初始化構造器,構造函數,可以有多個,默認是 init
var name: String
// 構造函數不需要寫 fun
// override 重寫
// 1. 父類存在相同的方法
// 2. 子類重新編寫父類方法中的實現
override init() {
// 非 option 屬性,都必須在構造函數中設置初始值,從而保證對象在被實例化的時候,屬性都被正確初始化,
// 在調用父類的構造函數之前,必須保證本類的屬性都已經初始化完成
name = "呵呵呵";
print("Person - init")
super.init()
}
// 重載 函數名相同,但是參數和個數不同
// 重載可以給自己的屬性外部設置初始值
// OC是沒有重載的, initWithXXX
init(name: String) {
// 使用參數的name 設置給屬性
self.name = name
super.init()
}
}
3.遍歷構造器
指定構造器是類中最主要的構造器。一個指定構造器將初始化類中提供的所有屬性,并根據父類鏈往上調用父類的構造器來實現父類的初始化。
便利構造器是類中比較次要的、輔助型的構造器。你可以定義便利構造器來調用同一個類中的指定構造器,并為其參數提供默認值。你也可以定義便利構造器來創建一個特殊用途或特定輸入值的實例?!?/p>
類的構造器代理規則
規則 1
指定構造器必須調用其直接父類的的指定構造器。
指定構造器規則 2
便利構造器必須調用同類中定義的其它構造器。規則 3
便利構造器必須最終導致一個指定構造器被調用。規則4
convenience 的初始化方法是不能被子類重寫
一個更方便記憶的方法是:
指定構造器必須總是向上代理
便利構造器必須總是橫向代理
4.可失敗的構造器
如果一個類、結構體或枚舉類型的對象,在構造過程中有可能失敗,則為其定義一個可失敗構造器。這里所指的“失敗”是指,如給構造器傳入無效的參數值,或缺少某種所需的外部資源,又或是不滿足某種必要的條件等。
為了妥善處理這種構造過程中可能會失敗的情況。你可以在一個類,結構體或是枚舉類型的定義中,添加一個或多個可失敗構造器。其語法為在init關關鍵字后面添加問號(init?)。
注意
可失敗構造器的參數名和參數類型,不能與其它非可失敗構造器的參數名,及其參數類型相同。
可失敗構造器會創建一個類型為自身類型的可選類型的對象。你通過return nil語句來表明可失敗構造器在何種情況下應該“失敗”。
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
注意
嚴格來說,構造器都不支持返回值。因為構造器本身的作用,只是為了確保對象能被正確構造。因此你只是用return nil表明可失敗構造器構造失敗,而不要用關鍵字return來表明構造成功。
let someCreature = Animal(species: "Giraffe")
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
let someCreature2 = Animal(species: "")
// someCreature 的類型是 Animal? 而不是 Animal
print(someCreature ?? ()) // 輸出結果()
guard let giraffe = someCreature else {
print("類型不匹配") // 輸出結果 類型不匹配
return
}
- 枚舉類型的可失敗構造器
enum TemperatureUnit {
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
5.必要構造器
在類的構造器前添加required修飾符表明所有該類的子類都必須實現該構造器:
class SomeClass {
required init() {
// 構造器的實現代碼
}
}
在子類重寫父類的必要構造器時,必須在子類的構造器前也添加required修飾符,表明該構造器要求也應用于繼承鏈后面的子類。在重寫父類中必要的指定構造器時,不需要
添加override
修飾符:
class SomeSubclass: SomeClass {
required init() {
// 構造器的實現代碼
}
}
注意
如果子類繼承的構造器能滿足必要構造器的要求,則無須在子類中顯式提供必要構造器的實現。
6.通過閉包或者函數設置屬性的默認值
如果某個存儲型屬性的默認值需要一些定制或設置,你可以使用閉包或全局函數為其提供定制的默認值。每當某個屬性所在類型的新實例被創建時,對應的閉包或函數會被調用,而它們的返回值會當做默認值賦值給這個屬性。
這種類型的閉包或函數通常會創建一個跟屬性類型相同的臨時變量,然后修改它的值以滿足預期的初始狀態,最后返回這個臨時變量,作為屬性的默認值。
class SomeClass {
let someProperty: SomeType = {
// 在這個閉包中給 someProperty 創建一個默認值
// someValue 必須和 SomeType 類型相同
return someValue
}()
}
注意閉包結尾的大括號后面接了一對空的小括號。這用來告訴 Swift 立即執行此閉包。如果你忽略了這對括號,相當于將閉包本身作為值賦值給了屬性,而不是將閉包的返回值賦值給屬性?!?/p>
注意
如果你使用閉包來初始化屬性,請記住在閉包執行時,實例的其它部分都還沒有初始化。這意味著你不能在閉包里訪問其它屬性,即使這些屬性有默認值。同樣,你也不能使用隱式的self屬性,或者調用任何實例方法。
7.使用KVC屬性賦值,以及后期使用字典轉模型框架,我們的模型應該如何定義?
/**
1.定義模型屬性的時候,如果是對象,通常定義成可選的
- 在需要的時候創建,避免寫構造函數 ,可以簡化代碼
2.如果是基本數據類型,不能設置成可選的,必須要設置初始值,否者KVC會崩潰
3.使用KVC設置數值的時候,數值不能是private的
4.使用KVC之前,應該調用super.init 保證對象實例化完成
*/
class Person: NSObject {
var name: String?
// 這種寫法會報錯
// 使用KVC提示無法找到 age這個KEY
// 原因:Int是一個基本數據類型的結構體,OC中沒有,OC中只有基本數據類型
// var age: Int?
var age: Int = 0
// this class is not key value coding-compliant for the key title.'
// 因為這個屬性是私有的,使用KVC設置值的時候,同樣無法設置,會崩潰
// private var title: String?
var title: String?
init(dict: [String: Any]) {
super.init()
// Use of 'self' in method call 'setValuesForKeys' before super.init initializes self
// 使用self的方法 setValuesForKeys 之前,應該調用 super.init方法
// KVC的方法是OC的方法。在運行時給對象發送消息
// 要求對象已經實例化完成
setValuesForKeys(dict)
}
override func setValue(_ value: Any?, forUndefinedKey key: String) {
// 注意這里不能調用父類的方法,不能將父類的代碼完全覆蓋,這樣才不會崩潰
// super.setValue(value, forUndefinedKey: key)
}
}
// 我們沒有定義XXX這個key的屬性,所以會崩潰,如果解決這個問題,在對象中,重寫
// override func setValue(_ value: Any?, forUndefinedKey key: String) 這個方法
let stu2 = Student(dict: ["name": "洋蔥", "age": 11, "title": "Boss", "no": "110", "XXX": "yyy"])
print("\(stu2.name ?? "") \(stu2.age) \(stu2.no ?? "")")
4.使用runtime獲取對象的屬性,這個和OC中的使用姿勢基本一致
class Student: Person {
var no: String?
var bithday: String?
var address: String?
var height: Int = 180
class func getpropertiesList() ->[String] {
var count: UInt32 = 0
var arrM: [String] = []
// 獲取類的屬性列表,返回屬性列表的數組,可選項
let list = class_copyPropertyList(self, &count)
print("屬性個數:\(count)")
// 遍歷數組
for i in 0..<Int(count) {
//根據下標獲取屬性
let pty = list?[i]
//獲取屬性的名稱<C語言字符串>
//轉換過程:Int8 -> Byte -> Char -> C語言字符串
let cName = property_getName(pty!)
//轉換成String的字符串
let name = String(utf8String: cName!)
arrM.append(name!)
print("-----\(name!)")
}
free(list) //釋放list
return arrM
}
// 使用guard let來實現
class func propertyList() -> [String] {
var count: UInt32 = 0
var arrM: [String] = []
// 獲取類的屬性列表,返回屬性列表的數組,可選項
let list = class_copyPropertyList(self, &count)
print("屬性個數:\(count)")
for i in 0..<Int(count) {
// 使用guard語法,一次判斷每一項是否有值,只要有一項為nil,就不再執行后續的代碼
guard let pty = list?[i],
let cName = property_getName(pty),
let name = String(utf8String: cName)
else {
//繼續遍歷下一個
continue
}
print(name)
arrM.append(name)
}
free(list)
return arrM
}
}