1.元類型
元類型是指所有類型的類型冈止,包括類狂票、結(jié)構(gòu)體、枚舉和協(xié)議熙暴。
類闺属、結(jié)構(gòu)體或枚舉類型的元類型是相應(yīng)的類型名緊跟.Type慌盯。
協(xié)議類型的元類型——并不是運行時適配該協(xié)議的具體類型——是該協(xié)議名字緊跟.Protocol。
比如掂器,類SomeClass的元類型就是SomeClass.Type亚皂,協(xié)議SomeProtocol的元類型就是SomeProtocal.Protocol。
你可以使用后綴self表達式來獲取類型国瓮。比如孕讳,SomeClass.self返回SomeClass本身,而不是SomeClass的一個實例巍膘。同樣厂财,SomeProtocol.self返回SomeProtocol本身,而不是運行時適配SomeProtocol的某個類型的實例峡懈。還可以對類型的實例使用type(of: someInstance)表達式來獲取該實例在運行階段的類型
protocol TestProtocol {
func test()
}
class SomeBaseClass {
class func printClassName() {
print("SomeBaseClass")
}
}
class SomeSubClass: SomeBaseClass {
var title = "標(biāo)題"
override class func printClassName() {
print("SomeSubClass")
}
}
let someInstance: SomeBaseClass = SomeSubClass()
print(type(of: someInstance))
print(type(of: SomeSubClass.self))
print(type(of: TestProtocol.self))
SomeSubClass
SomeSubClass.Type
TestProtocol.Protocol
2. .self和self
T.self:T是實例對象璃饱,當(dāng)前T.self返回的就是實例對象本身;T是類肪康,當(dāng)前T.self返回的就是類型本身
let p = SomeSubClass()
print("\(p.self) === \(type(of: p.self))")
print("\(SomeSubClass.self)--------\(type(of: SomeSubClass.self))")
TestDemo1.ViewController.SomeSubClass === SomeSubClass
SomeSubClass--------SomeSubClass.Type
在實例方法中self是實例對象的本身荚恶;在類方法中self是類型本身。
class SomeSubClass: SomeBaseClass {
var title = "標(biāo)題"
class func test(){
print("類方法:\(self)")
}
func test(){
print("對象方法:\(self)")
}
}
對象方法:TestDemo1.ViewController.SomeSubClass
類方法:SomeSubClass
3.Self
Self類型不是特定類型磷支,而是為了方便引用當(dāng)前類型谒撼,而無需重復(fù)或知道該類型的名稱。在協(xié)議聲明或者協(xié)議成員聲明中雾狈,Self類型是指最終符合協(xié)議的類型廓潜。
func get() -> Self {
return self
}
static let age = 18
func test1(){
print("Self指代類型本身:\(Self.age)")
}
4.AnyObject、Any和AnyClass
AnyObject可以代表任意 class 類型(用來替代OC中的 id)
public typealias AnyObject
由定義就可以看出它就是一個接口善榛,所有的 class 都隱式地實現(xiàn)了這個接口辩蛋。所以 AnyObject 只適用于 class 類型。但是 swift 中的基本類型都是 struct 類型移盆,并不能用 AnyObject 來表示悼院。所以官方又提出了一個更特殊的 Any 類型,它除了 class 以外還可以表示其他類型咒循,可以說是任意類型(包括 struct据途,enum,func等)叙甸。
例如:
let swiftArr = ["a", "b", "c"]
let swiftStr = "hello world"
var array = [AnyObject]()
array.append(swiftArr as AnyObject)
array.append(swiftStr as AnyObject)
這里我們顯示的將 swift 中的 String 和 Array 轉(zhuǎn)成了 AnyObject颖医。實際上 array 里面的元素已經(jīng)變成了 NSString 和 NSArray 了。
當(dāng)然我們還有另外的方式解決此問題蚁署,用 Any便脊。
let swiftArr1 = ["a", "b", "c"]
let swiftStr1 = "hello world"
var array1 = [Any]()
array1.append(swiftArr1)
array1.append(swiftStr1)
可以看到結(jié)果全部是 swift 中的原生類型
值得注意的是 Any 類型使用的時候需要使用 as 關(guān)鍵字做類型轉(zhuǎn)換, 例如:
let string = mixed.first as? String {
print("The first element of mixed is \(string)")
}
AnyClass是AnyObject.Type的別名而已
public typealias AnyClass = AnyObject.Type
表示任意類的元類型蚂四,任意類的類型都隱式遵守這個協(xié)議光戈,一般我們使用不多
5.可選類型(Optional)
您可以通過追加將數(shù)據(jù)類型簡單地表示為 Optional哪痰。 方法是類型附加! 或 ?。 如果可選變量中包含一個值久妆,則將其值返回為 Optional <Value>晌杰,否則返回nil。
var someValue:Int?
var someAnotherValue:Int!
print(someValue)
print(someAnotherValue)
注意:訪問null的未包裝可選對象時發(fā)生致命錯誤崩潰
var someValue:Int!
var unwrappedValue:Int = someValue
當(dāng)您運行該程序時筷弦,您將得到致命錯誤的崩潰:解開Optional值時意外發(fā)現(xiàn)nil肋演,因為代碼unwrappedValue:Int = someValue試圖將Optional someValue中的值分配給變量unwrappedValue。
但是烂琴,somevalue 是一個包含 nil 值的可選類型爹殊。試圖將 nil 值分配給變量 unwrappedValue (這不是一個Optional)將導(dǎo)致崩潰。
可以用if判斷nil+iflet+guard三種方式處理上面崩潰問題
6.類型別名(Typealias)
類型別名不會創(chuàng)建新類型奸绷。它們只是為現(xiàn)有類型提供一個新名稱梗夸。
typealias name = existing type
在Swift中,大多數(shù)類型都可以使用typealias号醉。它們可以是:
1.內(nèi)置類型(例如:String, Int)
2.用戶定義的類型(例如:類反症,結(jié)構(gòu),枚舉)
3.復(fù)雜類型(例如:閉包)
func someMethod(oncomp:(Int)->(String)){
}
typealias CompletionHandler = (Int)->(String)
func someMethod(oncomp:CompletionHandler){
}
7.關(guān)聯(lián)類(associatedtype)
associatedtype定義關(guān)聯(lián)類型畔派,相當(dāng)于類型的占位符铅碍,讓實現(xiàn)協(xié)議的類型來指定具體的類型
protocol Food {
}
protocol Animal {
associatedtype F: Food
func eat(_ food: F)
}
struct Meat: Food {
}
struct Tiger: Animal {
func eat(_ food: Meat) {
print("eat \(food)")
}
}
/*
具有關(guān)聯(lián)類型的協(xié)議類型,只能當(dāng)做泛型約束使用
錯誤代碼:
func isTiger(animal: Animal) -> Bool {
}
*/
// 具有關(guān)聯(lián)類型的協(xié)議類型线椰,只能當(dāng)做泛型約束使用
func isTiger<A: Animal>(animal: A) -> Bool {
if animal is Tiger {
return true
} else {
return false
}
}
8.類型修飾詞
1.available
可用來標(biāo)識計算屬性胞谈、函數(shù)、類憨愉、協(xié)議呜魄、結(jié)構(gòu)體、枚舉等類型的生命周期莱衩。(依賴于特定的平臺版本 或 Swift 版本)爵嗅。它的后面一般跟至少兩個參數(shù),參數(shù)之間以逗號隔開笨蚁。
if #available(iOS 11.0, *) {
scrollView.contentInsetAdjustmentBehavior = .never
} else {
automaticallyAdjustsScrollViewInsets = false
}
還有一種用法是放在函數(shù)睹晒、結(jié)構(gòu)體、枚舉括细、類或者協(xié)議的前面伪很,表示當(dāng)前類型僅適用于某一平臺
@available(iOS 12.0, *)
func adjustDarkMode() {
/* code */
}
@available(iOS 12.0, *)
struct DarkModeConfig {
/* code */
}
@available(iOS 12.0, *)
protocol DarkModeTheme {
/* code */
}
2.@discardableResult
帶返回的函數(shù)如果沒有處理返回值會被編譯器警告。但有時我們就是不需要返回值的奋单,這個時候我們可以讓編譯器忽略警告锉试,就是在方法名前用@discardableResult聲明一下±辣簦可以參考Alamofire中request的寫法:
@discardableResult
public func request(
_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
{
return SessionManager.default.request(
url,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers
)
}
3.@inlinable
這個關(guān)鍵詞是可內(nèi)聯(lián)的聲明呆盖,它來源于C語言中的inline拖云。C中一般用于函數(shù)前,做內(nèi)聯(lián)函數(shù)应又,它的目的是防止當(dāng)某一函數(shù)多次調(diào)用造成函數(shù)棧溢出的情況宙项。因為聲明為內(nèi)聯(lián)函數(shù),會在編譯時將該段函數(shù)調(diào)用用具體實現(xiàn)代替株扛,這么做可以省去函數(shù)調(diào)用的時間尤筐。
內(nèi)聯(lián)函數(shù)常出現(xiàn)在系統(tǒng)庫中,OC中的runtim中就有大量的inline使用
需要注意內(nèi)聯(lián)聲明不能用于標(biāo)記為private或者fileprivate的地方,這很好理解洞就,對私有方法的內(nèi)聯(lián)是沒有意義的盆繁。內(nèi)聯(lián)的好處是運行時更快。因為是編譯時做替換旬蟋,這增加了編譯的開銷改基,會相應(yīng)的延長編譯時間。
4.@warn_unqualified_access
通過命名我們可以推斷出其大概含義:對“不合規(guī)”的訪問進行警告咖为。這是為了解決對于相同名稱的函數(shù)秕狰,不同訪問對象可能產(chǎn)生歧義的問題。
比如說躁染,Swift 標(biāo)準(zhǔn)庫中Array和Sequence均實現(xiàn)了min()方法鸣哀,而系統(tǒng)庫中也定義了min(::),對于可能存在的二義性問題吞彤,我們可以借助于@warn_unqualified_access我衬。
extension Array where Self.Element : Comparable {
@warn_unqualified_access
@inlinable public func min() -> Element?
}
extension Sequence where Self.Element : Comparable {
@warn_unqualified_access
@inlinable public func min() -> Self.Element?
}
extension Array where Element: Comparable {
func minValue() -> Element? {
return min()
}
}
我們會收到編譯器的警告:Use of 'min' treated as a reference to instance method in protocol 'Sequence', Use 'self.' to silence this warning。它告訴我們編譯器推斷我們當(dāng)前使用的是Sequence中的min()饰恕,這與我們的想法是違背的挠羔。因為有這個@warn_unqualified_access限定,我們能及時的發(fā)現(xiàn)問題埋嵌,并解決問題:self.min()破加。
5.@objc
把這個特性用到任何可以在 Objective-C 中表示的聲明上——例如,非內(nèi)嵌類雹嗦,協(xié)議范舀,非泛型枚舉(原始值類型只能是整數(shù)),類和協(xié)議的屬性了罪、方法(包括 setter 和 getter )锭环,初始化器,反初始化器泊藕,下標(biāo)辅辩。 objc 特性告訴編譯器,這個聲明在 Objective-C 代碼中是可用的。
@objc還有一個用處是當(dāng)你想在OC的代碼中暴露一個不同的名字時玫锋,可以用這個特性蛾茉,它可以用于類,函數(shù)景醇,枚舉,枚舉成員吝岭,協(xié)議三痰,getter,setter等窜管。
當(dāng)在OC代碼中訪問enabled的getter方法時散劫,是通過isEnabled
class ExampleClass: NSObject {
@objc var enabled: Bool {
@objc(isEnabled) get {
// Return the appropriate value
}
}
}
6.@objcMembers
因為Swift中定義的方法默認(rèn)是不能被OC調(diào)用的,除非我們手動添加@objc標(biāo)識幕帆。但如果一個類的方法屬性較多获搏,這樣會很麻煩,于是有了這樣一個標(biāo)識符@objcMembers失乾,它可以讓整個類的屬性方法都隱式添加@objc常熙,不光如此對于類的子類、擴展碱茁、子類的擴展都也隱式的添加@objc裸卫,當(dāng)然對于OC不支持的類型,仍然無法被OC調(diào)用
7.@frozen 和@unknown default
@frozen即凍結(jié)纽竣,保證之后該值類型不會再變墓贿。其實我們常用的類型像Int、Float蜓氨、Array聋袋、Dictionary、Set等都已被“凍結(jié)”穴吹。需要說明的是凍結(jié)僅針對struct和enum這種值類型幽勒,因為他們在編譯器就確定好了內(nèi)存布局。
對于沒有標(biāo)記為frozen的枚舉例如AVPlayerItem.Status港令,則認(rèn)為該枚舉值在之后的系統(tǒng)版本中可能變化代嗤。對于可能變化的枚舉,我們在列出所有case的時候還需要加上對@unknown default的判斷缠借,這一步會有編譯器檢查干毅。
switch currentItem.status {
case .readyToPlay:
/* code */
case .failed:
/* code */
case .unknown:
/* code */
@unknown default:
fatalError("not supported")
}
- lazy
lazy是懶加載的關(guān)鍵詞,當(dāng)我們僅需要在使用時進行初始化操作就可以選用該關(guān)鍵詞泼返。
lazy var dayLabel: UILabel = {
let label = UILabel()
label.text = self.todayText()
return label
}()
使用lazy你可能會發(fā)現(xiàn)它只能通過var初始而不能通過let硝逢,這是由lazy 的具體實現(xiàn)細(xì)節(jié)決定的:它在沒有值的情況下以某種方式被初始化,然后在被訪問時改變自己的值,這就要求該屬性是可變的渠鸽。
9.unowned weak
weak相當(dāng)于oc里面的weak叫乌,弱引用,不會增加循環(huán)計數(shù)徽缚。主體對象釋放時被weak修飾的屬性也會被釋放憨奸,所以weak修飾對象就是optional。
·unowned相當(dāng)于oc里面的unsafe_unretained凿试,它不會增加引用計數(shù)排宰,即使它的引用對象釋放了,它仍然會保持對被已經(jīng)釋放了的對象的一個 "無效的" 引用那婉,它不能是 Optional 值板甘,也不會被指向nil。如果此時為無效引用详炬,再去嘗試訪問它就會crash盐类。
lazy var someClosure: () -> Void = { [weak self] in
// 被weak修飾后self為optional,這里是判斷self非空的操作
guard let self = self else { retrun }
self.doSomethings()
}
10.some
some是Swift5.1新增的特性呛谜。它的用法就是修飾在一個 protocol 前面在跳,默認(rèn)場景下 protocol 是沒有具體類型信息的,但是用some 修飾后隐岛,編譯器會讓 protocol 的實例類型對外透明硬毕。
可以通過一個例子理解這段話的含義,當(dāng)我們嘗試定義一個遵循Equatable協(xié)議的value時:
// Protocol 'Equatable' can only be used as a generic constraint because it has Self or associated type requirements
var value: Equatable {
return 1
}
var value: Int {
return 1
}
編譯器提示我們Equatable只能被用來做泛型的約束礼仗,它不是一個具體的類型吐咳,這里我們需要使用一個遵循Equatable的具體類型(Int)進行定義。但有時我們并不想指定具體的類型元践,這時就可以在協(xié)議名前加上some韭脊,讓編譯器自己去推斷value的類型:
var value: some Equatable {
return 1
}
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
9.屬性修飾詞和權(quán)限控制詞
swift提供了5個不同的訪問級別,權(quán)限最高的是open单旁,其次依次是public沪羔、internal、fileprivate象浑,最低是private蔫饰。默認(rèn)使用的是internal。
open & public
使用open和public定義的實體愉豺,在它們定義的module的任意文件中皆可使用篓吁。其他module如果import了此實體,則其他module的源文件也可使用蚪拦。一般在framework中指定的公開接口里杖剪,使用open和public級別冻押。
open 和 public 的區(qū)別
public或更低權(quán)限的類,只能在其定義的module中子類化
public或更低權(quán)限的類的成員盛嘿,只能在其定義的module中被重寫或子類化
open權(quán)限的類洛巢,可在其定義的module,或者import了其的module中子類化
open權(quán)限的類的成員次兆,可在其定義的module稿茉,或者import了其的module中重寫或子類化
internal
intenal修飾的實體在其定義的module中皆可使用,但是在其他module中無法使用芥炭。一般定義APP或者framework內(nèi)部結(jié)構(gòu)的時候漓库,會使用internal級別
fileprivate
fileprivate定義的實體,只能在其源文件中使用蚤认,當(dāng)其只在整個源文件中使用的時候米苹,使用fileprivate級別
private
private定義的實體糕伐,只能在定義的范圍內(nèi)砰琢,和其在同一文件的extension中使用,當(dāng)其只在當(dāng)前聲明范圍內(nèi)使用的時候良瞧,使用private級別
使用原則
子類:子類的訪問級別不可以高于父類陪汽,但子類重寫的方法的訪問級別可以高于父類
枚舉:枚舉的每個值都和它們所屬枚舉有相同的權(quán)限,且不能單獨為某個值定義權(quán)限
協(xié)議:協(xié)議可以指定訪問級別褥蚯,并且對于協(xié)議的每一項來說挚冤,都和協(xié)議的訪問級別相同,不能單獨為協(xié)議的某個方法指定不同的訪問級別