Swift基礎(chǔ)12

擴(kuò)展

擴(kuò)展就是向一個(gè)已有的類(lèi)、結(jié)構(gòu)體、枚舉類(lèi)型或者協(xié)議類(lèi)型添加新功能。這包括在沒(méi)有權(quán)限獲取原始源代碼的情況下擴(kuò)展類(lèi)型的能力。擴(kuò)展和Objective-c中的分類(lèi)(category)類(lèi)似。(不過(guò)與Objective-c不同的是,Swift的擴(kuò)展沒(méi)有名字)

note:擴(kuò)展可以對(duì)一個(gè)類(lèi)型添加新的功能,但是不能重寫(xiě)已有的功能。

擴(kuò)展語(yǔ)法

聲明一個(gè)擴(kuò)展使用關(guān)鍵字 extension :

extension someType {
    //加到SomeType的新功能
}

一個(gè)擴(kuò)展可以擴(kuò)展一個(gè)已有類(lèi)型,使其能夠適配一個(gè)或多個(gè)協(xié)議。當(dāng)這種情況發(fā)生時(shí),協(xié)議的名字應(yīng)該玩去按照類(lèi)或結(jié)構(gòu)體的名字的方式進(jìn)行書(shū)寫(xiě):

extension SomeType : SomeProtocol , AnotherProtocol {
    //協(xié)議實(shí)現(xiàn)寫(xiě)在這里
}

note:如果我們定義了一個(gè)擴(kuò)展向一個(gè)已有類(lèi)型添加新功能,那么這個(gè)新功能對(duì)該類(lèi)型的所有已有實(shí)例中都是有用的,即使它們?cè)谀愕倪@個(gè)擴(kuò)展前面定義的。

計(jì)算型屬性

擴(kuò)展可以向已有類(lèi)型添加計(jì)算實(shí)例屬性和計(jì)算型類(lèi)型屬性。

extension Double {
    var km:Double {
        return self * 1000.0
    }
    
    var m:Double {
        return self
    }
    
    var cm:Double {
        
        return self / 100.0
    }
    
    var mm:Double {
        return self / 1000.0
    }
    
}

var  height = 170.0.cm
print("the height is \(height)")

height = 1700.0.mm
print("the height is \(height)")

note:擴(kuò)展可以添加新的計(jì)算屬性,但是不可以添加存儲(chǔ)屬性,也不可以向已有屬性添加屬性觀測(cè)器。

構(gòu)造器

擴(kuò)展可以向已有類(lèi)型添加新的構(gòu)造器。這可以讓我們擴(kuò)展其它類(lèi)型,將我們自己定制類(lèi)型作為構(gòu)造器參數(shù),或者提供該類(lèi)型的原始實(shí)現(xiàn)中沒(méi)有包含的額外初始化選項(xiàng)。

擴(kuò)展能向類(lèi)中添加新的便利構(gòu)造器,但是它們不能向類(lèi)中添加新的指定構(gòu)造器或析構(gòu)器。指定構(gòu)造器和析構(gòu)器必須總是由原始類(lèi)實(shí)現(xiàn)來(lái)提供。

注意:如果我們使用擴(kuò)展向一個(gè)值類(lèi)型添加一個(gè)構(gòu)造器,在該值類(lèi)型已經(jīng)向所有的存儲(chǔ)屬性提供默認(rèn)值,而且沒(méi)有定義任何定制構(gòu)造器時(shí),我們可以在值類(lèi)型的擴(kuò)展構(gòu)造器中調(diào)用默認(rèn)構(gòu)造器和逐一成員構(gòu)造器。


struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
}

extension Rect{
    
    init(center:Point,size:Size){
        let originx = center.x - (size.width / 2.0)
        let originy = center.y - (size.width / 2.0)
        
        self.init(origin:Point(x: originx, y: originy),size: size)
    }
    
}

note:如果我們使用擴(kuò)展提供了一個(gè)新的構(gòu)造器,我們依舊由責(zé)任保證構(gòu)造過(guò)程能夠讓所有實(shí)例完全初始化。

方法

擴(kuò)展可以向已有類(lèi)型添加新的實(shí)例方法和類(lèi)方法。

extension Int {
    
    func loop(task:()->Void){
        
        for _ in  0..<self {
            task()
        }
    }
    
}

3.loop({
    print("xxxxx")
})


2.loop({
    () -> Void in
    
    print("hello world")
})

下標(biāo)

擴(kuò)展可以向一個(gè)已有類(lèi)型添加新下標(biāo)。


extension Int {
    
    subscript(let  index: Int) -> Int {
        
        let str = "\(self)"
        var arr = [String]()
        for item in str.characters {
            arr.append(String(item))
        }
       
        print("hello:\(arr)")
        
        if index > arr.count {
            return 0
        }else{
            let tmp = arr[index]
            
            return Int(tmp)!
            
        }
    }
    
}



1234567[0]
1234567[1]
1234567[2]

嵌套類(lèi)型

擴(kuò)展可以向已有的類(lèi)、結(jié)構(gòu)體和枚舉添加新的嵌套類(lèi)型:

extension Int {
    
    enum Kind {
        case Negative, Zero, Positive
    }
    
    var kind: Kind {
        switch self {
        case 0:
            return .Zero
        case let x where x > 0:
            return .Positive
        default:
            return .Negative
        }
    }
}

20.kind
(-2).kind
0.kind

這個(gè)例子向Int添加了新的嵌套枚舉,這個(gè)名為Kind的枚舉表示特定整數(shù)的類(lèi)型。具體來(lái)說(shuō),就是表示整數(shù)時(shí)正數(shù),零或者負(fù)數(shù)。

這個(gè)例子還向Int添加了一個(gè)新的計(jì)算實(shí)例屬性,即kind,用來(lái)返回合適的Kind枚舉成員。

協(xié)議

協(xié)議定義了一個(gè)藍(lán)圖,規(guī)定了用來(lái)實(shí)現(xiàn)某一特定工作或者功能所必需的方法和屬性。類(lèi),結(jié)構(gòu)體或枚舉類(lèi)型都可以遵循協(xié)議,并提供具體實(shí)現(xiàn)來(lái)完成協(xié)議定義的方法和功能。任意能夠滿(mǎn)足協(xié)議要求的類(lèi)型被稱(chēng)為 遵循 這個(gè)協(xié)議。

除了遵循協(xié)議的類(lèi)型必須實(shí)現(xiàn)那些指定的規(guī)定以外,還可以對(duì)協(xié)議進(jìn)行擴(kuò)展,實(shí)現(xiàn)一些特殊的規(guī)定或者一些附加的功能,使得遵循的類(lèi)型能過(guò)收益。

協(xié)議的語(yǔ)法

協(xié)議的定義方式與類(lèi),結(jié)構(gòu)體,枚舉的定義非常相似。

protocol SomeProtocol {

}

要使類(lèi)遵循某個(gè)協(xié)議,需要在類(lèi)型名稱(chēng)后加上協(xié)議名稱(chēng),中間以冒號(hào) : 分隔,作為類(lèi)型定義的一部分。作為類(lèi)型定義的一部分。遵循多個(gè)協(xié)議時(shí),各協(xié)議之間用逗號(hào) , 分隔。

struct SomeStructure : FirstProtocol , AnotherProtocol {
    //結(jié)構(gòu)體內(nèi)容
}

如果類(lèi)在遵循協(xié)議的同時(shí)擁有父類(lèi),應(yīng)該將父類(lèi)名放在協(xié)議名之前,以逗號(hào)分隔。

class SomeClass : SomeSuperClass , FirstProtocol ,SecondProtocol {
//類(lèi)定義
}

對(duì)屬性的規(guī)定

協(xié)議可以規(guī)定其 遵循者 提供特定名稱(chēng)和類(lèi)型的 實(shí)例屬性 或 類(lèi)屬性,而不用指定是 存儲(chǔ)屬性還是計(jì)算型屬性。此外還必須指明是只讀的還是可讀可寫(xiě)的。

如果協(xié)議規(guī)定屬性是可讀可寫(xiě)的,那么這個(gè)屬性不能是常量或只讀的計(jì)算屬性。如果協(xié)議只要求屬性是只讀的,那個(gè)屬性不僅可以是只讀的,如果代碼需要的話(huà),也可以是可寫(xiě)的。

協(xié)議中的通常用var來(lái)聲明變量屬性,在類(lèi)型聲明后加上{ set get}來(lái)表示屬性是可讀可寫(xiě)的,只讀屬性則用{get}來(lái)表示。

protocol SomeProtocol {
    var mustBeSettable : Int {get set}
    var doesNotNeedToBeSetted: Int {get}
}

在協(xié)議中定義類(lèi)屬性時(shí),總是使用static關(guān)鍵字作為前綴。當(dāng)協(xié)議的遵循者是類(lèi)時(shí),可以使用class或static關(guān)鍵字來(lái)聲明類(lèi)屬性:

protocol AnotherProtocol {
    static var someTypeProperty : Int {get set}
}

eg:

protocol FullyNamed {
    
    var fullName : String { get }
    
}

struct Person : FullyNamed {
    
    var fullName: String
    
}

let joh = Person(fullName: "Jhon")


class Starship : FullyNamed {
    
    var prefix : String?
    var name : String
    
    init(name:String , prefix : String? = nil){
        self.name = name
        self.prefix = prefix
    }
    
    var fullName:String {
        return (prefix != nil ? prefix! + " " : "") + name
    }
    
}

var xxx = Starship(name: "jhon", prefix: "conna")
print(xxx.fullName)

對(duì)方法的規(guī)定

協(xié)議可以搖起其遵循者實(shí)現(xiàn)某些指定的實(shí)例方法或類(lèi)方法。這些方法作為協(xié)議的一部分,像普通的方法一樣放在協(xié)議的定義中,但是不需要大括號(hào)和方法體。可以在協(xié)議中定義具有可變參數(shù)的方法,和普通方法的定義式相同。但是在協(xié)議的方法定義中,不支持參數(shù)默認(rèn)值。

正如對(duì)屬性的規(guī)定中所說(shuō)的,在協(xié)議中定義類(lèi)方法的時(shí)候,總是使用static關(guān)鍵字作為前綴。當(dāng)協(xié)議的遵循者是類(lèi)的時(shí)候,我們可以在類(lèi)的實(shí)現(xiàn)中使用class或者static來(lái)實(shí)現(xiàn)類(lèi)方法。

protocol SomeProtocol {
    static func someTypeMethod()
}

eg:

protocol RandomNumberProtocol {
    func random() -> Int
}

class Test : RandomNumberProtocol {
    
    var x : Int = 0
    
    func random() -> Int {
        
        return   Int(arc4random()) % 10 + 1
        
    }
    
    func testPrint() -> Void {
        x = random()
        print("x number is \(x)")
    }
    
}

let x = Test()
x.testPrint()

對(duì)Mutating方法的規(guī)定

有時(shí)需要在方法中改變它的實(shí)例。例如,值類(lèi)型(結(jié)構(gòu)體,枚舉)的實(shí)例方法中,將mutating關(guān)鍵字作為函數(shù)的前綴,寫(xiě)在func之前,表示可以在該方法中修改它所屬的實(shí)例及其實(shí)例屬性的值。

如果我們?cè)趨f(xié)議中定義了一個(gè)方法旨在改變遵循該協(xié)議的實(shí)例,那么在協(xié)議定義時(shí)需要在方法前加mutating關(guān)鍵字。這使得結(jié)構(gòu)和枚舉遵循協(xié)議并滿(mǎn)足此方法要求。

note:用類(lèi)實(shí)現(xiàn)協(xié)議中的mutating方法時(shí),不用寫(xiě)mutating關(guān)鍵字;用結(jié)構(gòu)體,枚舉實(shí)現(xiàn)協(xié)議中的mutating方法時(shí),必須寫(xiě)mutating關(guān)鍵字。

protocol Toggleable {
    mutating func toggle()
}

enum OnOffSwitch : Toggleable {
    case Off, On
    
    mutating func toggle() {
        switch self {
        case .Off:
            self = .On
        case .On:
            self = .Off
        }
    }
}

var lightSwitch = OnOffSwitch.Off
lightSwitch.toggle()

對(duì)構(gòu)造器的規(guī)定

協(xié)議可以要求它的遵循者實(shí)現(xiàn)指定的構(gòu)造器。我們可以像書(shū)寫(xiě)普通的構(gòu)造器那樣,在協(xié)議的定義里寫(xiě)下構(gòu)造器的聲明,但不需要寫(xiě)花括號(hào)和構(gòu)造器的實(shí)體:

protocol SomeProtocol {
    init(SomeParameter: Int)
}

協(xié)議構(gòu)造器規(guī)定在類(lèi)中的實(shí)現(xiàn),我們可以在遵循該協(xié)議的類(lèi)中實(shí)現(xiàn)構(gòu)造器,并指定其為類(lèi)的指定構(gòu)造器或者便利構(gòu)造器。這兩種情況下,我們都必須給構(gòu)造器實(shí)現(xiàn)標(biāo)上 required 修飾符:

class SomeClass: SomeProtocol {
    required init(someParameter: int){
        //構(gòu)造器實(shí)現(xiàn)
    }
}

使用required修飾符可以保證: 所有的遵循該協(xié)議的子類(lèi),同樣能為構(gòu)造器規(guī)定提供一個(gè)顯式的實(shí)現(xiàn)或繼承實(shí)現(xiàn)。

note:如果類(lèi)已經(jīng)被標(biāo)記為final,那么不需要在協(xié)議構(gòu)造器的實(shí)現(xiàn)中使用required修飾符。因?yàn)閒inal類(lèi)不能有子類(lèi)。

如果一個(gè)子類(lèi)重寫(xiě)了父類(lèi)的指定構(gòu)造器,并且該構(gòu)造器遵循了某個(gè)協(xié)議的規(guī)定,那么該構(gòu)造器的實(shí)現(xiàn)需要被同時(shí)標(biāo)示required和override修飾符:

protocol SomeProtocol {
    init()
}

class SomeSuperClass {
    init(){
        //構(gòu)造器實(shí)現(xiàn)
    }
}

class SomeClass: SomeSuperClass , SomeProtocol{
    
    required override init(){
        //構(gòu)造器實(shí)現(xiàn)
    }
}

可失敗構(gòu)造器的規(guī)定

可以通過(guò)給協(xié)議protocols中添加可失敗構(gòu)造器來(lái)使遵循該協(xié)議的類(lèi)型必須實(shí)現(xiàn)該可失敗構(gòu)造器。

如果在協(xié)議中定義一個(gè)可失敗構(gòu)造器,則在遵循該協(xié)議的類(lèi)型中必須添加同名同參數(shù)的可失敗構(gòu)造器或非可失敗構(gòu)造器。如果在協(xié)議中定義一個(gè)非可失敗構(gòu)造器,則在遵循該協(xié)議的類(lèi)型中必須添加同名同參數(shù)的非可失敗構(gòu)造器或隱式解析類(lèi)型的可失敗構(gòu)造器(init!)。

協(xié)議類(lèi)型

盡管協(xié)議本身并不實(shí)現(xiàn)任何功能,但是協(xié)議可以被當(dāng)作類(lèi)型來(lái)使用。

協(xié)議可以像其他普通類(lèi)型一樣使用,使用場(chǎng)景:

  • 作為函數(shù)、方法或構(gòu)造器中的參數(shù)類(lèi)型或返回值類(lèi)型
  • 作為常量、變量或?qū)傩缘念?lèi)型
  • 作為數(shù)組、字典或其他容器中的元素類(lèi)型

note:協(xié)議是一種類(lèi)型,因此協(xié)議類(lèi)型的名稱(chēng)應(yīng)與其他類(lèi)型(Int,Double,String)的寫(xiě)法相同,使用大寫(xiě)字母開(kāi)頭的駝峰式寫(xiě)法。

protocol NameProtocol {
    var name:String {get set}
}

class Person : NameProtocol {
    
    var name: String
    
    init(name:String){
        self.name = name
    }
}

func myPrint(nameObj:NameProtocol){
    
    print("the name is \(nameObj.name)")
    
}

myPrint(Person(name: "hello"))

委托(代理)模式

委托是一種設(shè)計(jì)模式,它允許 類(lèi) 或 結(jié)構(gòu)體 將一些需要他們負(fù)責(zé)的功能 交由委托 給其他的類(lèi)型的實(shí)例。委托模式實(shí)現(xiàn)很簡(jiǎn)單: 定義協(xié)議來(lái)封裝那些需要被委托的函數(shù)和方法,使其 遵循者 擁有這些被委托的 函數(shù)和方法。 委托模式 可以用來(lái)響應(yīng)特定的動(dòng)作或接收外部數(shù)據(jù)源提供的數(shù)據(jù),而無(wú)需知道外部數(shù)據(jù)源的類(lèi)型信息。

在擴(kuò)展中添加協(xié)議成員

即便無(wú)法修改源代碼,依然可以通過(guò)擴(kuò)展來(lái)擴(kuò)充已存在的類(lèi)型(類(lèi),結(jié)構(gòu)體,枚舉等)。擴(kuò)展可以為已存在的類(lèi)型添加屬性,方法,下標(biāo)腳本,協(xié)議成員等。

note:通過(guò)擴(kuò)展為已存在的類(lèi)型遵循協(xié)議時(shí),該類(lèi)型的所有實(shí)例也會(huì)隨之添加協(xié)議中的方法

eg:

protocol TextRepresentable {
    var textDescription: String{get}
}


extension Test: TextRepresentable {
    var textDescription: String {
        return "hello world"
    }
}




通過(guò)擴(kuò)展補(bǔ)充協(xié)議聲明

當(dāng)一個(gè)類(lèi)型已經(jīng)實(shí)現(xiàn)了協(xié)議中的所有要求,卻沒(méi)有聲明為遵循協(xié)議時(shí),可以通過(guò)擴(kuò)展(空的擴(kuò)展體)來(lái)補(bǔ)充協(xié)議聲明:

struct Test2 {
    var name:String
    var textDescription: String {
        return "hi"
    }
}
extension Test2: TextRepresentable {}

note:即使?jié)M足了協(xié)議的所有要求,類(lèi)型也不會(huì)自動(dòng)轉(zhuǎn)變,因此我們必須為它做成顯式的協(xié)議聲明。



###協(xié)議類(lèi)型集合
協(xié)議類(lèi)型可以在數(shù)組或者字典這樣的集合中使用。

let things :[TextRepresentable] = [game,d12,simonTheHamster]

for thing in things {
print(thing.textDescription)
}


###協(xié)議的繼承
協(xié)議能夠繼承一個(gè)或多個(gè)其他協(xié)議,可以在繼承的協(xié)議基礎(chǔ)上添加新的內(nèi)容要求。協(xié)議的繼承語(yǔ)法與類(lèi)的繼承相似,多個(gè)被繼承的協(xié)議間用逗號(hào)分隔

protocol InheritingProtocol : SomeProtocol , AnotherProtocol {
//協(xié)議定義
}


如下,eg:

protocol NameProtocol {
var name:String {get set}
}

protocol InfoProtocol : NameProtocol {
var sex:String {get set}
var address:String {set get}
}


###類(lèi)專(zhuān)屬協(xié)議
我們可以在協(xié)議的繼承列表中,通過(guò)添加class關(guān)鍵字,限制協(xié)議只能適配到類(lèi)(class)類(lèi)型。(結(jié)構(gòu)體或枚舉不能遵循該協(xié)議)。該class關(guān)鍵字必須時(shí)第一個(gè)出現(xiàn)在協(xié)議的繼承列表中,其后才是其他??協(xié)議。

```
protocol SomeClassOnlyProtocol : class , SomeProtocolo {
    //協(xié)議定義
}

note:當(dāng)協(xié)議想要定義的行為,要求(或假設(shè))它的遵循類(lèi)型必須是引用類(lèi)型而非值類(lèi)型時(shí),應(yīng)該采用類(lèi)專(zhuān)屬協(xié)議。

協(xié)議合成

有時(shí)候需要同時(shí)遵循多個(gè)協(xié)議,我們可以將多個(gè)協(xié)議采用protocol<SomeProtocol,AnotherProtocol> 這樣的格式進(jìn)行組合,稱(chēng)為 協(xié)議合成 。我們可以在<>中羅列任意多個(gè)我們想要遵循的協(xié)議,以逗號(hào)分隔。

protocol Named {
    var name: String {get}
}

protocol  Aged {
    var age: Int {get}
}

struct Person: Named, Aged {
    var name:String
    var age: Int
}

func wishHappyBirthday(person: protocol<Named,Aged>){
    print("Happy birthday \(person.name) - you are \(person.age)")
}

let person = Person(name: "tom", age: 12)
wishHappyBirthday(person)

note:協(xié)議合成 并不會(huì)生成一個(gè)新協(xié)議類(lèi)型,而是將多個(gè)協(xié)議合成為一個(gè)臨時(shí)的協(xié)議,超出范圍后立即失效。

檢驗(yàn)協(xié)議的一致性

我們可以使用 is 和 as 操作符來(lái)檢查是否遵循某一協(xié)議或強(qiáng)制轉(zhuǎn)化為某一類(lèi)型。檢查和轉(zhuǎn)化的語(yǔ)法和之前相同。

  • is 操作符用來(lái)檢查實(shí)例是否遵循了某個(gè)協(xié)議。
  • as?返回一個(gè)可選值,當(dāng)實(shí)例 遵循 協(xié)議時(shí),返回該協(xié)議類(lèi);否則返回nil。
  • as 用以強(qiáng)制向下轉(zhuǎn)型,如果強(qiáng)轉(zhuǎn)失敗,會(huì)引起運(yùn)行時(shí)錯(cuò)誤。
protocol Named {
    var name: String {get}
}

struct Dog: Named {
    var name:String
    var age:Int
}

struct Person: Named {
    var name:String
    var age: Int
    var address:String
}

let dog1 = Dog(name: "dog1", age: 1)
let dog2 = Dog(name: "dog2", age: 2)

let person1 = Person(name: "tom", age: 20, address: "nanjing")
let person2 = Person(name: "Jhon", age: 20, address: "shanghai")
let person3 = Person(name: "sam", age: 13, address: "changcun")

let objs : [Named] = [dog1,person1,dog2,person3,person2]

for item in objs {
    
    if let value = item as? Dog {
        
        print("\(value.name) is dog")
        
    }else if let value = item as? Person {
        
        print("\(value.name) is preson")
        
    }
    
}

對(duì)可選協(xié)議的規(guī)定

協(xié)議可以含有可選成員,其 遵循者 可以選擇是否實(shí)現(xiàn)這些成員。在協(xié)議中使用 optional 關(guān)鍵字作為前綴來(lái)定義可選成員。當(dāng)需要使用可選規(guī)定的方法或者屬性時(shí),他的類(lèi)型自動(dòng)會(huì)變成可選的。

可選協(xié)議在調(diào)用時(shí)使用 可選鏈 ,因?yàn)閰f(xié)議的遵循者可能沒(méi)有實(shí)現(xiàn)可選內(nèi)容。

note:可選協(xié)議只能在含有 @objc 前綴的協(xié)議中生效。這個(gè)前綴表示協(xié)議將暴露給Object-c代碼,即使我們不打算和Objective-C有什么交互,如果我們想要指明協(xié)議包含可選屬性,哪么還是要加上@objc前綴。還需要注意的是,@objc的協(xié)議只能由繼承自O(shè)bjective-c類(lèi)的類(lèi)或者其他的@objc類(lèi)來(lái)遵循。他也不能被結(jié)構(gòu)體和枚舉遵循。

@objc protocol CounterDataSource {
    optional func incrementForCount(count: Int) -> Int
    optional var fixedIncrement: Int {get}
}

class Counter {
    
    var count = 0
    var dataSource: CounterDataSource?
    
    func increment() {
        if let amount = dataSource?.incrementForCount?(count){
            count += amount
        }else if let amount = dataSource?.fixedIncrement{
            count += amount
        }
    }
    
}


 class ThreeSource : CounterDataSource {
    
    @objc let fixedIncrement: Int = 3
    
}


var counter = Counter()

counter.dataSource = ThreeSource()

for _ in 1...4 {
    counter.increment()
    print(counter.count)
}

協(xié)議擴(kuò)展

使用擴(kuò)展協(xié)議的方式可以為遵循者提供方法或?qū)傩缘膶?shí)現(xiàn)。通過(guò)這種方式,可以讓我們無(wú)需在每個(gè)遵循者都實(shí)現(xiàn)一次,無(wú)需使用全局函數(shù),我們可以通過(guò)擴(kuò)展協(xié)議的方式進(jìn)行定義。

例如,可以擴(kuò)展 RandomNumberGenerator協(xié)議,讓其提供randomBool()方法。該方法使用random()方法返回一個(gè)隨機(jī)的Bool值:

extension RandomNumberGEnerator {
    func randomBool() -> Bool {
        return random() > 0.5
    }
}

通過(guò)擴(kuò)展協(xié)議,所有協(xié)議的遵循者,在不用任何修改的情況下,都自動(dòng)得到了這個(gè)擴(kuò)展所增加的方法。

提供默認(rèn)實(shí)現(xiàn)

可以通過(guò)協(xié)議擴(kuò)展的方式為協(xié)議規(guī)定的屬性和方法提供默認(rèn)的實(shí)現(xiàn)。如果協(xié)議的遵循者對(duì)規(guī)定的屬性和方法提供了自己的實(shí)現(xiàn),那么遵循者提供的實(shí)現(xiàn)將被使用。

note:通過(guò)擴(kuò)展協(xié)議提供的協(xié)議實(shí)現(xiàn)和可選協(xié)議規(guī)定有區(qū)別。雖然協(xié)議遵循者無(wú)需自己實(shí)現(xiàn),通過(guò)擴(kuò)展提供的默認(rèn)實(shí)現(xiàn),可以不是用可選鏈調(diào)用。

例如,

@objc protocol CounterDataSource {
    optional func incrementForCount(count: Int) -> Int
    optional var fixedIncrement: Int {get}
}

class Counter {
    
    var count = 0
    var dataSource: CounterDataSource?
    
    func increment() {
        if let amount = dataSource?.incrementForCount?(count){
            count += amount
        }else if let amount = dataSource?.fixedIncrement{
            count += amount
        }else if let  amount = dataSource?.incrementForCount2(count){
            count += amount
        }
    }
    
}


class ThreeSource : CounterDataSource {
    
}

extension CounterDataSource{
    func incrementForCount2(count: Int) -> Int{
        return count + 5
    }
}


var counter = Counter()

counter.dataSource = ThreeSource()

for _ in 1...4 {
    counter.increment()
    print(counter.count)
}

為協(xié)議擴(kuò)展添加限制條件

在擴(kuò)展協(xié)議的時(shí)候,可以指定一些限制,只有滿(mǎn)足這些限制的協(xié)議遵循者,才能獲得協(xié)議擴(kuò)展提供的屬性和方法。這些限制卸載協(xié)議名之后,使用where關(guān)鍵字來(lái)描述限制情況。

extension CollectionType where Generator.Element : CounterDataSource {
    func  sayHello(){
        print("hello")
    }
}

let arr = [ThreeSource(),ThreeSource(),ThreeSource()]
arr.sayHello()

note:如果有多個(gè)協(xié)議擴(kuò)展,而一個(gè)協(xié)議的遵循者又同時(shí)滿(mǎn)足它們的限制,那么將會(huì)使用所滿(mǎn)足限制最多的那個(gè)擴(kuò)展。

泛型

泛型函數(shù)

泛型函數(shù) 可以工作于任何類(lèi)型。

func swapTwoValues<T>(inout a: T,inout _ b:T){


}

類(lèi)型參數(shù)

在上面的swapTwoValues例子中,占位類(lèi)型 T 是一種類(lèi)型參數(shù)的式例。類(lèi)型參數(shù)指定并命名為一個(gè)占位類(lèi)型,并且緊隨在函數(shù)名后面,適用一對(duì)尖括號(hào)括起來(lái),(如<T>)。

一旦一個(gè)類(lèi)型參數(shù)被指定,那么其可以被使用來(lái)定義一個(gè)函數(shù)的參數(shù)類(lèi)型,或作為一個(gè)函數(shù)返回類(lèi)型,或用作函數(shù)體中的注釋類(lèi)型。在這種情況下,被類(lèi)型參數(shù)所代表的占位類(lèi)型不管函數(shù)任何時(shí)候被調(diào)用,都會(huì)被實(shí)際類(lèi)型所替換。

我們可以支持多個(gè)類(lèi)型參數(shù),命名在尖括號(hào)中,用逗號(hào)分開(kāi)。

命名類(lèi)型參數(shù)

在簡(jiǎn)單的情況下,泛型函數(shù)或泛型類(lèi)型需要指定一個(gè)占位類(lèi)型,通常用一單個(gè)字母T來(lái)命名類(lèi)型參數(shù)。不過(guò),我們可以使用任何有效的標(biāo)識(shí)符來(lái)作為類(lèi)型參數(shù)名。

如果你使用多個(gè)參數(shù)定規(guī)更復(fù)雜的泛型函數(shù)或泛型類(lèi)型,那么使用更多的描述類(lèi)型參數(shù)是非常有用的,例如,Swift中字典(Dictionary)類(lèi)型有兩個(gè)類(lèi)型參數(shù),一個(gè)是鍵,另外一個(gè)是值。如果我們自己刺蛾字典,我們或許會(huì)定義這兩個(gè)類(lèi)型參數(shù)為 key 和 Value ,用來(lái)記住它們?cè)谖覀兊姆盒痛a中的作用。

note: 請(qǐng)始終使用大寫(xiě)字母開(kāi)頭的駝峰命名法來(lái)給類(lèi)型參數(shù)命名,以表明它們是類(lèi)型占位符,而非類(lèi)型值。

泛型類(lèi)型

通常在泛型類(lèi)型中,Swift運(yùn)行你定義你自己的泛型類(lèi)型。這些自定義類(lèi)、結(jié)構(gòu)體和枚舉作用于任何類(lèi)型。

struct Stack<T> {
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    mutating func pop() -> T {
        return items.removeLast()
    }
}

擴(kuò)展一個(gè)泛型類(lèi)型

當(dāng)我們擴(kuò)展一個(gè)泛型類(lèi)型的時(shí)候,我們并不需要在擴(kuò)展定義中提供類(lèi)型參數(shù)列表。更加方便的是,原始類(lèi)型定義中的類(lèi)型參數(shù)列表在擴(kuò)展中是可以使用的,并且這些來(lái)自原始類(lèi)型中的參數(shù)名稱(chēng)會(huì)被用作原始定義中類(lèi)型參數(shù)的引用。

extension Stack {
    var topItem: T? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}

類(lèi)型約束

類(lèi)型約束指定了一個(gè)必須繼承自指定類(lèi)的類(lèi)型參數(shù),或者遵循一個(gè)特定的協(xié)議或協(xié)議構(gòu)成。

1.類(lèi)型約束語(yǔ)法

我們可以寫(xiě)一個(gè)在一個(gè)類(lèi)型參數(shù)名后面的類(lèi)型約束,通過(guò)冒號(hào)分割,來(lái)作為類(lèi)型參數(shù)鏈的一部分。這種作用于泛型函數(shù)的類(lèi)型約束的基本語(yǔ)法如下:

func someFunction<T:SomeClass,U:SomeProtocol>(someT:T,someU:U){

}

上面這個(gè)假定函數(shù)有兩個(gè)類(lèi)型參數(shù)。第一個(gè)類(lèi)型參數(shù)T,有一個(gè)需要T必須是SomeClass子類(lèi)的類(lèi)型約束;第二個(gè)類(lèi)型參數(shù)U,有一個(gè)需要U必須遵循SomeProtocol協(xié)議的類(lèi)型約束。

2.類(lèi)型約束實(shí)例

func findIndex<T: Equatable>(array: [T], _ valueToFind: T) -> Int? {
     for (index, value) in array.enumerate() {
         if value == valueToFind {
             return index
          }
    }

    return nil

}

Swift標(biāo)準(zhǔn)庫(kù)中定義了一個(gè)Equatable協(xié)議,該協(xié)議要求任何遵循的類(lèi)型實(shí)現(xiàn)等式符(==)和不等符(!=)對(duì)任何兩個(gè)該類(lèi)型進(jìn)行比較。所有的Swift標(biāo)準(zhǔn)類(lèi)型自動(dòng)支持Equatable協(xié)議。

關(guān)聯(lián)類(lèi)型

當(dāng)定義一個(gè)協(xié)議時(shí),有時(shí)候聲明一個(gè)或多個(gè)關(guān)聯(lián)類(lèi)型作為協(xié)議定義的一部分是非常有用的。一個(gè)關(guān)聯(lián)類(lèi)型作為協(xié)議的一部分,給定義類(lèi)型的一個(gè)占位名(或別名)。作用于關(guān)聯(lián)類(lèi)型上實(shí)際類(lèi)型在協(xié)議被實(shí)現(xiàn)前是不需要指定的。關(guān)聯(lián)類(lèi)型被指定為 typealias 關(guān)鍵字。

1.關(guān)聯(lián)類(lèi)型實(shí)例

protocol Container {
    
    typealias ItemType
    
    mutating func append(item: ItemType)
    var count:Int {get}
    
    subscript(i: Int) -> ItemType {get}
}

struct Stack<T>: Container {
    // original Stack<T> implementation
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    mutating func pop() -> T {
        return items.removeLast()
    }
    // conformance to the Container protocol
    mutating func append(item: T) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> T {
        return items[i]
    }
}

2.擴(kuò)展一個(gè)存在的類(lèi)型為指定關(guān)聯(lián)類(lèi)型

在Swift中Array已經(jīng)提供append(_:)方法,一個(gè)count屬性和通過(guò)下標(biāo)來(lái)查找一個(gè)自己的元素。這三個(gè)功能都達(dá)到Container協(xié)議的要求。也就意味著你可以擴(kuò)展Array去遵循Container協(xié)議,只要通過(guò)簡(jiǎn)單聲明Array適用于該協(xié)議而已。

extension Array: Countainer {}

3.where語(yǔ)句
類(lèi)型約束 能夠確保類(lèi)型符合泛型函數(shù)或類(lèi)的定義約束。

對(duì)關(guān)聯(lián)類(lèi)型定義約束是非常有用的。我們可以在參數(shù)列表中通過(guò)where語(yǔ)句定義參數(shù)的約束。一個(gè)where語(yǔ)句能夠使一個(gè)關(guān)聯(lián)類(lèi)型遵循一個(gè)特定的協(xié)議,以及(或)那個(gè)特定的類(lèi)型參數(shù)和關(guān)聯(lián)類(lèi)型可以是相同的。我們可以寫(xiě)一個(gè)where語(yǔ)句,緊跟在類(lèi)型參數(shù)列表后面,where語(yǔ)句后跟一個(gè)或多個(gè)針對(duì)關(guān)聯(lián)的約束,以及(或)一個(gè)或多個(gè)類(lèi)型和關(guān)聯(lián)類(lèi)型間的等價(jià)關(guān)系。

func allItemsMatch<
    C1: Container, C2: Container
    where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
    (someContainer: C1, anotherContainer: C2) -> Bool {
    
    return false
}

訪(fǎng)問(wèn)控制

訪(fǎng)問(wèn)控制可以限定其他源文件或模塊中代碼對(duì)你代碼的訪(fǎng)問(wèn)級(jí)別。這個(gè)特性可以讓我們隱藏功能實(shí)現(xiàn)一些細(xì)節(jié),并且可以明確的申明我們提供給其他人的接口中哪些部分是他們可以訪(fǎng)問(wèn)和使用的。

我們可以明確地給單個(gè)類(lèi)型(類(lèi)、結(jié)構(gòu)體、枚舉)設(shè)置訪(fǎng)問(wèn)級(jí)別,也可以給這些類(lèi)的屬性、函數(shù)、初始化方法、基本類(lèi)型、下標(biāo)索引等設(shè)置訪(fǎng)問(wèn)級(jí)別。協(xié)議也可以被限定在一定范圍內(nèi)使用,包括協(xié)議里的全局常量、變量和函數(shù)。

在提供了不同訪(fǎng)問(wèn)級(jí)別的同時(shí),Swift還為某些經(jīng)典場(chǎng)景提供了默認(rèn)的訪(fǎng)問(wèn)級(jí)別,這樣就不需要我們?cè)诿慷未a中都申明顯示訪(fǎng)問(wèn)級(jí)別。其實(shí),如果只是開(kāi)發(fā)一個(gè)單目標(biāo)應(yīng)用程序,我們完全不用申明代碼的顯示訪(fǎng)問(wèn)級(jí)別。

note:簡(jiǎn)單起見(jiàn),代碼中可以設(shè)置訪(fǎng)問(wèn)級(jí)別的特性(屬性、基本類(lèi)型、函數(shù)等),在下面的章節(jié)中我們會(huì)以“實(shí)體”代替。

模塊和源文件

Swift中的訪(fǎng)問(wèn)控制模型基于模塊和源文件這兩個(gè)概念。

模塊指的是以單獨(dú)單元構(gòu)建和發(fā)布的Framework或application。在Swift中的一個(gè)模塊可以使用import關(guān)鍵字引入另外一個(gè)模塊。

在Swift中,Xcode的每個(gè)構(gòu)建目標(biāo)(比如 FrameWork或app bundle)都被當(dāng)作模塊處理。如果我們是為了實(shí)現(xiàn)某個(gè)通用的功能,或者是為了封裝一些常用方法二將代碼打包成獨(dú)立的Framework,這個(gè)Framework在Swift中就被稱(chēng)為模塊。當(dāng)它被引入到某個(gè)app工程或者另外一個(gè)framwwork時(shí),它里面的一切(屬性、函數(shù))等仍然屬于這個(gè)獨(dú)立的模塊。

源文件指的是Swift中的Swift File,就是編寫(xiě)Swift源代碼的文件,它通常屬于一個(gè)模塊,盡管一般我們將不同的 類(lèi) 分別定義在不同的源文件中,但是同一個(gè)源文件也可以包含多個(gè)類(lèi)和函數(shù)的定義。

訪(fǎng)問(wèn)級(jí)別

swift為代碼中的實(shí)體提供了三種不同的訪(fǎng)問(wèn)級(jí)別。這些訪(fǎng)問(wèn)級(jí)別不僅與源文件中定義的實(shí)體相關(guān),同時(shí)也與源文件所屬的模塊相關(guān)。

  • public : 可以訪(fǎng)問(wèn)自己模塊中源文件里的任何實(shí)體,別人也可以通過(guò)引入該模塊來(lái)訪(fǎng)問(wèn)源文件中所有實(shí)體。通常情況下,framework 中的某個(gè)接口是可以被任何人使用時(shí),我們也可以將其設(shè)置為public級(jí)別。
  • internal : 可以訪(fǎng)問(wèn)自己模塊中源文件里的任何實(shí)體,但是別人不能訪(fǎng)問(wèn)該模塊中源文件里的實(shí)體。通常情況下,某個(gè)接口或framwork作為內(nèi)部結(jié)構(gòu)使用時(shí),我們可以將其設(shè)置為internal級(jí)別。
  • private :只能在當(dāng)前源代碼文件中使用的實(shí)體,稱(chēng)為私有實(shí)體。使用private級(jí)別,可以用作隱藏某些功能的實(shí)現(xiàn)細(xì)節(jié)。

public為最高訪(fǎng)問(wèn)級(jí)別,private為最低訪(fǎng)問(wèn)級(jí)別。

訪(fǎng)問(wèn)級(jí)別的使用原則

swift中的訪(fǎng)問(wèn)級(jí)別遵循一個(gè)使用原則:訪(fǎng)問(wèn)級(jí)別統(tǒng)一性。比如:

  • 一個(gè)public訪(fǎng)問(wèn)級(jí)別的變量,不能將它的類(lèi)型定義為internal 和private。因?yàn)樽兞靠梢员蝗魏稳嗽L(fǎng)問(wèn),但是定義它的類(lèi)型不可以,所以這樣就會(huì)出現(xiàn)錯(cuò)誤。
  • 函數(shù)的訪(fǎng)問(wèn)級(jí)別不能高于它的參數(shù)、返回類(lèi)型的訪(fǎng)問(wèn)級(jí)別。因?yàn)槿绻瘮?shù)定義為了public而參數(shù)或返回類(lèi)型定位為internal或private,就會(huì)出現(xiàn)函數(shù)可以被別人訪(fǎng)問(wèn),但是它的參數(shù)和返回類(lèi)型確不可以。同樣會(huì)出現(xiàn)錯(cuò)誤。

默認(rèn)訪(fǎng)問(wèn)級(jí)別

如果你不為代碼中的所有實(shí)體定義顯式訪(fǎng)問(wèn)級(jí)別,那么它們默認(rèn)為internal級(jí)別。在大多數(shù)情況下,我們不需要設(shè)置實(shí)體的顯示訪(fǎng)問(wèn)級(jí)別。因?yàn)槲覀円话愣际窃陂_(kāi)發(fā)一個(gè)app bundle。

單目標(biāo)應(yīng)用程序的訪(fǎng)問(wèn)級(jí)別

當(dāng)你編寫(xiě)一個(gè)單目標(biāo)應(yīng)用程序時(shí),該應(yīng)用的所有功能都是為該應(yīng)用服務(wù),不需要提供給其他應(yīng)用或者模塊使用,所以我們不需要明確設(shè)置訪(fǎng)問(wèn)級(jí)別,使用默認(rèn)的訪(fǎng)問(wèn)級(jí)別 internal即可。但是如果你愿意,我們也可以使用private級(jí)別,用于隱藏一些功能的實(shí)現(xiàn)細(xì)節(jié)。

Framework的訪(fǎng)問(wèn)級(jí)別

當(dāng)你開(kāi)發(fā)Framework時(shí),就需要把一些對(duì)外的接口定義為public級(jí)別,以便其他人導(dǎo)入該framework后可以正常的使用其功能。這些你定義為public的接口,就是這個(gè)Framework的API。

note:Framwork 的內(nèi)部實(shí)現(xiàn)細(xì)節(jié)依然可以使用默認(rèn)的internal級(jí)別,或者也可以定義為private級(jí)別。只有當(dāng)你想把它作為API的一部分的時(shí)候,才將其定義為public級(jí)別。

單元測(cè)試目標(biāo)的訪(fǎng)問(wèn)級(jí)別

當(dāng)你的app有單元測(cè)試目標(biāo)時(shí),為樂(lè)方便測(cè)試,測(cè)試模塊需要能訪(fǎng)問(wèn)到你app中的代碼。默認(rèn)情況下只有public級(jí)別的實(shí)體才可以被其他模塊訪(fǎng)問(wèn)。然而,如果在引入一個(gè)生產(chǎn)模塊時(shí)使用@testable 注解,然后用帶測(cè)試的方式編譯這個(gè)生產(chǎn)模塊,單元測(cè)試目標(biāo)就是可以訪(fǎng)問(wèn)所有internal級(jí)別的實(shí)體。

訪(fǎng)問(wèn)控制語(yǔ)法

通過(guò)修飾符 public、internal、private來(lái)聲明實(shí)體的訪(fǎng)問(wèn)級(jí)別:

public class SomePublicClass {}
internal class SomeInternalClass{}
private class SomePrivateClass{}

public var somePublicVariable = 0
internal let someInternalConstant = 0
private func somePrivateFunction(){}

除非有特殊的說(shuō)明,否則實(shí)體都使用默認(rèn)的訪(fǎng)問(wèn)級(jí)別 inernal 。這意味著不再使用修飾符聲明顯示訪(fǎng)問(wèn)級(jí)別的情況下,SomeInternalClass和someInternalConstant仍然擁有隱式的訪(fǎng)問(wèn)級(jí)別 internal。

class SomeInternalClass {} //隱式訪(fǎng)問(wèn)級(jí)別 internal
var someInternalVariable = 0 //隱式訪(fǎng)問(wèn)級(jí)別 internal

自定義類(lèi)型

如果想為一個(gè)自定義類(lèi)型中申明顯示訪(fǎng)問(wèn)級(jí)別,那么要明確一點(diǎn)。那就是你要確保新類(lèi)型的訪(fǎng)問(wèn)級(jí)別和它實(shí)際的作用域相匹配。比如說(shuō),如果你定義了一個(gè)private類(lèi),那么這個(gè)類(lèi)就只能在定義它的源文件中當(dāng)作屬性類(lèi)型、函數(shù)參數(shù)或者返回類(lèi)型使用。

類(lèi)的訪(fǎng)問(wèn)級(jí)別也可以影響到類(lèi)成員(屬性、函數(shù)、初始化方法等)的默認(rèn)訪(fǎng)問(wèn)級(jí)別。如果你將類(lèi)申明為private類(lèi),那么該類(lèi)的所有成員的默認(rèn)訪(fǎng)問(wèn)級(jí)別也會(huì)成為private。如果將類(lèi)申明為public或者internal類(lèi)(或者不明確的申明訪(fǎng)問(wèn)級(jí)別,即使用默認(rèn)internal訪(fǎng)問(wèn)級(jí)別),那么該類(lèi)的所有成員的訪(fǎng)問(wèn)級(jí)別式internal。

note:上面提到,一個(gè)public類(lèi)的所有成員的訪(fǎng)問(wèn)級(jí)別默認(rèn)為internal級(jí)別,而不是public級(jí)別。如果你想將某個(gè)成員申明為public級(jí)別,那么你必須使用修飾符明確的聲明該成員。這樣做的好處是,在你定義公共接口API的時(shí)候,可以明確的選擇那些屬性或方法是需要公開(kāi)的,哪些是內(nèi)部使用的,可以避免將內(nèi)部使用的屬性方法公開(kāi)成公共API的錯(cuò)誤。

//顯式的public類(lèi)
public class SomePublicClass {
    //顯式的public類(lèi)成員
    public var somePublicProperty = 0
    
    //隱式的internal類(lèi)成員
    var someInternalPeroperty = 0
    
    //顯式的private 方法
    private func somePrivateMethod(){
        
    }
}

//隱式的internal類(lèi)
class SomeInternalClass {
    //隱式的internal 成員
    var someInternalProperty = 0
    
    //顯式的private 方法
    private func somePrivateMethod(){
        
    }
}

//顯式的private類(lèi)
private class SomePrivateClass {
    //隱式的private類(lèi)成員
    var somePrivatePropery = 0
    
    //隱式的private方法
    func somePrivateMethod(){
        
    }
}

元組類(lèi)型

元組的訪(fǎng)問(wèn)級(jí)別使用時(shí)所有類(lèi)型的訪(fǎng)問(wèn)級(jí)別使用中最為嚴(yán)謹(jǐn)。比如說(shuō),如果你構(gòu)建一個(gè)包含兩種不同類(lèi)型元素的元組,其中一個(gè)元素類(lèi)型的訪(fǎng)問(wèn)級(jí)別為internal,另外一個(gè)為private級(jí)別,哪么這個(gè)元組的訪(fǎng)問(wèn)級(jí)別為private。也就是說(shuō)元組的訪(fǎng)問(wèn)級(jí)別與元組中訪(fǎng)問(wèn)級(jí)別最低的類(lèi)型一致。

note:元組不同于類(lèi)、結(jié)構(gòu)體、枚舉、函數(shù)那樣有單獨(dú)的定義。元組的訪(fǎng)問(wèn)級(jí)別是在它使用時(shí)自動(dòng)推導(dǎo)出的,而不是明確的申明。

函數(shù)類(lèi)型

函數(shù)的訪(fǎng)問(wèn)級(jí)別需要根據(jù)該函數(shù)的參數(shù)類(lèi)型和返回類(lèi)型的訪(fǎng)問(wèn)級(jí)別得出。如果根據(jù)參數(shù)類(lèi)型和返回類(lèi)型得出的函數(shù)訪(fǎng)問(wèn)級(jí)別不符合默認(rèn)上下文,哪么就需要明確地聲明該函數(shù)的訪(fǎng)問(wèn)級(jí)別。

eg:

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
        // function implementation goes here
}

因?yàn)椋⊿omeInternalClass,SomePrivateClass)這個(gè)元組訪(fǎng)問(wèn)級(jí)別是private的,所以函數(shù)需要顯式的設(shè)置為private的。而不能使用默認(rèn)修飾符internal。

枚舉類(lèi)型

枚舉中成員的訪(fǎng)問(wèn)級(jí)別繼承自該枚舉,你不能為枚舉中的成員單獨(dú)申明不同的訪(fǎng)問(wèn)級(jí)別。

比如下面的例子,枚舉 CompassPoint 被明確的申明為public級(jí)別,那么它的成員 North,South,East,West的訪(fǎng)問(wèn)級(jí)別同樣也是public:

public enum CompassPoint {
    case North
    case South
    case East
    case West

}

枚舉定義中的任何原始值或關(guān)聯(lián)值的類(lèi)型都必須有一個(gè)訪(fǎng)問(wèn)級(jí)別,這個(gè)級(jí)別至少要不低于枚舉的訪(fǎng)問(wèn)級(jí)別。比如說(shuō),你不能在一個(gè)internal訪(fǎng)問(wèn)級(jí)別的枚舉中定義private的原始值類(lèi)型。

嵌套類(lèi)型

如果在private級(jí)別的類(lèi)型中定義嵌套類(lèi)型,那么該嵌套類(lèi)型就自動(dòng)擁有private訪(fǎng)問(wèn)級(jí)別。如果在public或者internal級(jí)別的類(lèi)型中定義嵌套類(lèi)型,那么該嵌套類(lèi)型自動(dòng)擁有internal訪(fǎng)問(wèn)級(jí)別。如果想讓嵌套類(lèi)型擁有public訪(fǎng)問(wèn)級(jí)別,那么需要明確地申明該嵌套類(lèi)型的訪(fǎng)問(wèn)級(jí)別。

子類(lèi)

子類(lèi)的訪(fǎng)問(wèn)級(jí)別不得高于父類(lèi)的訪(fǎng)問(wèn)級(jí)別。比如說(shuō),父類(lèi)的訪(fǎng)問(wèn)級(jí)別是internal,子類(lèi)的訪(fǎng)問(wèn)級(jí)別就不能聲明為public。

此外,在滿(mǎn)足子類(lèi)不高于父類(lèi)訪(fǎng)問(wèn)級(jí)別以及遵循各方位級(jí)別作用域(即模塊或源文件)的前提下,你可以重寫(xiě)任意類(lèi)成員(方法,屬性,初始化方法,下標(biāo)索引等等)。

如果我們無(wú)法直接訪(fǎng)問(wèn)某個(gè)類(lèi)中的屬性或函數(shù)等,那么可以繼承該類(lèi),從而可以更容易的訪(fǎng)問(wèn)到該類(lèi)的類(lèi)成員。

public class A {
    private func someMethod() {}
}
internal class B: A {
       override internal func someMethod() {
}

常量、變量、屬性、下標(biāo)

常量、變量、屬性不能擁有比它們的類(lèi)型更高的訪(fǎng)問(wèn)級(jí)別。比如說(shuō),你定義一個(gè)pbulic級(jí)別的屬性,但是它的類(lèi)型是private的級(jí)別,這是編譯器所不允許的。同樣,下標(biāo)也不能擁有比索引類(lèi)型或返回類(lèi)型更高的訪(fǎng)問(wèn)級(jí)別。

初始化

我們可以給自己定義的初始化方法申明訪(fǎng)問(wèn)級(jí)別,但是要不高于它所屬類(lèi)的訪(fǎng)問(wèn)級(jí)別。

如同函數(shù)或方法參數(shù),初始化方法參數(shù)的訪(fǎng)問(wèn)級(jí)別也不能低于初始化方法的訪(fǎng)問(wèn)級(jí)別。

默認(rèn)初始化方法

swift為結(jié)構(gòu)體、類(lèi)都提供了一個(gè)默認(rèn)的無(wú)參初始化方法,用于給它們的所有屬性提供賦值操作,但不會(huì)給出具體的值。默認(rèn)初始化方法的訪(fǎng)問(wèn)級(jí)別所屬類(lèi)型的訪(fǎng)問(wèn)級(jí)別相同。

note:如果一個(gè)類(lèi)型被申明為public級(jí)別,哪么默認(rèn)的初始化方法的訪(fǎng)問(wèn)級(jí)別為internal。如果你想讓無(wú)參的初始化方法在其他模塊中可以被使用,哪么你必須提供一個(gè)具有public訪(fǎng)問(wèn)級(jí)別的無(wú)參初始化方法。

結(jié)構(gòu)體的默認(rèn)成員初始化方法

如果結(jié)構(gòu)體中的任一存儲(chǔ)屬性的訪(fǎng)問(wèn)級(jí)別為private,那么它的默認(rèn)成員初始化方法訪(fǎng)問(wèn)級(jí)別就是private。盡管如此,結(jié)構(gòu)體的初始化方法的訪(fǎng)問(wèn)級(jí)別依然是internal。

如果你想在其他模塊中使用該結(jié)構(gòu)體的默認(rèn)成員初始化方法,那么你需要提供一個(gè)訪(fǎng)問(wèn)級(jí)別為public的默認(rèn)成員初始化方法。

協(xié)議

如果想為一個(gè)協(xié)議明確的申明訪(fǎng)問(wèn)級(jí)別,哪么需要注意一點(diǎn),就是你要確保該協(xié)議旨在你申明的訪(fǎng)問(wèn)級(jí)別作用域中使用。

協(xié)議中的每一個(gè)必須要實(shí)現(xiàn)的函數(shù)都具有和該協(xié)議相同的訪(fǎng)問(wèn)級(jí)別。這樣才能確保該協(xié)議的使用者可以實(shí)現(xiàn)它所提供的函數(shù)。

note:如果你定義了一個(gè)public訪(fǎng)問(wèn)級(jí)別的協(xié)議,哪么實(shí)現(xiàn)該協(xié)議提供的必要函數(shù)也會(huì)是public的訪(fǎng)問(wèn)級(jí)別。這一點(diǎn)不同于其他類(lèi)型,比如,public訪(fǎng)問(wèn)級(jí)別的其他類(lèi)型,他們的成員的訪(fǎng)問(wèn)級(jí)別為internal。

協(xié)議繼承

如果定義了一個(gè)新的協(xié)議,并且該協(xié)議繼承了一個(gè)已知的協(xié)議,那么新的協(xié)議擁有的訪(fǎng)問(wèn)級(jí)別最高也只和被繼承的協(xié)議的訪(fǎng)問(wèn)級(jí)別相同。比如說(shuō),你不能定義一個(gè)public的協(xié)議而去繼承一個(gè)internal的協(xié)議。

協(xié)議一致性

類(lèi)可以采用比自身訪(fǎng)問(wèn)級(jí)別低的協(xié)議。比如說(shuō),你可以定義一個(gè)public級(jí)別的類(lèi),可以讓它的其他模塊中使用,同時(shí)它也可以采用一個(gè)internal級(jí)別的協(xié)議,并且只能定義了該協(xié)議的模塊中使用。

采用了協(xié)議的類(lèi)的訪(fǎng)問(wèn)級(jí)別取它本身和所采用協(xié)議中最低的訪(fǎng)問(wèn)級(jí)別,也就是說(shuō)如果一個(gè)類(lèi)是public級(jí)別,采用的協(xié)議是internal級(jí)別,那么采用了這個(gè)協(xié)議后,該類(lèi)的訪(fǎng)問(wèn)級(jí)別也是internal。

如果你采用了協(xié)議,那么實(shí)現(xiàn)了協(xié)議所必須的方法后,該方法的訪(fǎng)問(wèn)級(jí)別遵循協(xié)議的訪(fǎng)問(wèn)級(jí)別。比如說(shuō),一個(gè)public級(jí)別的類(lèi),采用了internal級(jí)別的協(xié)議,那么該類(lèi)實(shí)現(xiàn)協(xié)議的方法至少也得食internal。

note:swift和Objective-C一樣,協(xié)議的一致性保證了一個(gè)類(lèi)不可能在同一個(gè)程序中用不同的方法采用同一個(gè)協(xié)議。

擴(kuò)展

你可以在條件允許的情況下對(duì)類(lèi)、結(jié)構(gòu)體、枚舉進(jìn)行擴(kuò)展。擴(kuò)展成員應(yīng)該具有和原始類(lèi)成員一致的訪(fǎng)問(wèn)級(jí)別。比如你擴(kuò)展了一個(gè)公共類(lèi)型,那么你新加的成員應(yīng)該具有和原始成員一樣默認(rèn)的internal的訪(fǎng)問(wèn)級(jí)別。

或者你可以明確申明訪(fǎng)問(wèn)級(jí)別(比如使用 private extesion)給該擴(kuò)展內(nèi)所有成員申明一個(gè)新的默認(rèn)訪(fǎng)問(wèn)級(jí)別。這個(gè)新的訪(fǎng)問(wèn)級(jí)別仍然可以被單獨(dú)成員所申明的訪(fǎng)問(wèn)級(jí)別所覆蓋。

協(xié)議的擴(kuò)展

如果一個(gè)擴(kuò)展采用了某個(gè)協(xié)議,那么你就不能對(duì)該擴(kuò)展使用該訪(fǎng)問(wèn)級(jí)別修飾符來(lái)申明了。該擴(kuò)展中實(shí)現(xiàn)協(xié)議的方法都會(huì)遵循該協(xié)議的訪(fǎng)問(wèn)級(jí)別。

泛型

泛型類(lèi)型或泛型函數(shù)的訪(fǎng)問(wèn)級(jí)別取泛型類(lèi)型、函數(shù)本身、泛型類(lèi)型參數(shù)三者中的最低訪(fǎng)問(wèn)級(jí)別。

類(lèi)型別名

任何你定義的類(lèi)型別名都被當(dāng)作不同的類(lèi)型,以便于進(jìn)行訪(fǎng)問(wèn)控制。一個(gè)類(lèi)型別名的訪(fǎng)問(wèn)級(jí)別不可高于原類(lèi)型的訪(fǎng)問(wèn)級(jí)別。比如說(shuō),一個(gè)private級(jí)別的類(lèi)型別名可以設(shè)定給一個(gè)public、internal、private的類(lèi)型,但是一個(gè)public級(jí)別的類(lèi)型別名只能設(shè)定給一個(gè)public級(jí)別的類(lèi)型,不能設(shè)定給internal或private級(jí)別的類(lèi)型。

note:這條規(guī)定也適用于為滿(mǎn)足協(xié)議一致性而給相關(guān)類(lèi)型命名別名的情況。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容