Swift(學習):訪問控制

訪問控制(Access Control)

  • 在訪問權限控制這塊,Swift提供了5個不同的訪問級別(以下是從高到低排列, 實體指被訪問級別修飾的內容)
  1. open:允許在定義實體的模塊(工程中所有創建的swift文件)、其他模塊(三方庫)中訪問,允許其他模塊進行繼承、重寫(open只能用在類、類成員上)
  2. public:允許在定義實體的模塊、其他模塊中訪問,不允許其他模塊進行繼承、重寫
  3. internal:只允許在定義實體的模塊中訪問,不允許在其他模塊中訪問
  4. fileprivate:只允許在定義實體的源文件中訪問(也就是隻允許在當前swift文件中訪問)
  5. private:只允許在定義實體的封閉聲明中訪問(只允許在本類中訪問,一般是用來修飾屬性)
  6. 絕大部分實體默認都是internal 級別

訪問級別的使用準則

  • 一個實體不可以被更低訪問級別的實體定義,比如 :
  1. 變量\常量類型 ≥ 變量\常量 

上圖變量類型的訪問級別爲 fileprivate 小於 變量的訪問級別internal,所以會報錯

    2.  參數類型、返回值類型 ≥ 函數

    3.  父類 ≥ 子類

    4.  父協議 ≥ 子協議

    5.   原類型 ≥ typealias

上圖原類型的訪問級別爲 fileprivate 小於typealiad的訪問級別internal,所以會報錯

    6.   原始值類型、關聯值類型 ≥ 枚舉類型 

上圖關聯值類型的訪問級別爲 fileprivate 小於enum的訪問級別public,所以會報錯

    7.   定義類型A時用到的其他類型 ≥ 類型A

    8.   ......


元祖類型

  • 元組類型的訪問級別是所有成員類型最低的那個

這裏我們可以看到元祖(Dog,Person)中Dog的訪問級別是internal,Person的訪問級別是fileprivate,Person的訪問級別較低,所以元祖的訪問級別是所有成員類型最低的fileprivate


泛型類型

  • 泛型類型的訪問級別是 類型的訪問級別 以及 所有泛型類型參數的訪問級別 中最低的那個

由上圖可以看出,類型的訪問級別 Person 是 public,泛型類型參數的訪問級別 Dog 是fileprivate,Car是interval,這三個中最小的是fileprivate,所以泛型類型的訪問級別是fileprivate,等於變量p的訪問級別,所以是對的


成員、嵌套類型

  • 類型的訪問級別會影響成員(屬性、方法、初始化器、下標)、嵌套類型的默認訪問級別 
  1. 一般情況下,類型爲private或fileprivate,那麼成員\嵌套類型默認也是private或fileprivate 
  2. 一般情況下,類型爲internal或public,那麼成員\嵌套類型默認是internal


成員的重寫

  • 子類重寫成員的訪問級別必須 ≥ 子類的訪問級別,或者 ≥ 父類被重寫成員的訪問級別 

  • 父類的成員不能被成員作用域外定義的子類重寫

上圖的age成員屬性作用域只在person類中,子類不能使用,應都寫在Person類中,寫成下圖形式


下面的代碼能否重寫?

1.   

當放在同一個方法體時會報錯,但是都放在全局作用域中就不會報錯,因爲在全局作用域中private和fileprivate訪問級別相同都是整個.swift文件可以訪問, 如下兩圖:

2.

  

與1的情況一致,看是否在全局作用域

3.

這段代碼任何情況下,包括全局作用域下也會報錯,因爲Dog類雖然和Person在全局作用域下的訪問級別一致,都可以認爲是fileprivate,但是run和age的訪問級別已經設置爲private,始終低於Person,所以會報錯,如下圖:

 


getter、setter

  • getter、setter默認自動接收它們所屬環境的訪問級別
  • 可以給setter單獨設置一個比getter更低的訪問級別,用以限制寫的權限

由上圖可知,age的setter訪問級別,只能在本類中使用,外部無法使用,所以不可以通過setter 給age賦值


初始化器

  • 如果一個public類想在另一個模塊調用編譯生成的默認無參初始化器,必須顯式提供public的無參初始化器,因爲public類的默認初始化器是internal級別
public class Person {
    //顯式提供public的無參初始化器
    public init() {
        
    }
}
  •  required初始化器 ≥ 它的默認訪問級別
  •  如果結構體有private\fileprivate的存儲實例屬性,那麼它的成員初始化器也是private\fileprivate,否則默認就是internal

由上圖可知,Point除了默認初始化器,其他所有的初始化器都變爲private級別,不能訪問


枚舉類型的case

  • 不能給enum的每個case單獨設置訪問級別

  可以看出給case單獨設置訪問級別,只能通過接收enum的訪問級別

  • 每個case自動接收enum的訪問級別
  • public enum定義的case也是public

協議

協議中定義的(方法等)要求自動接收協議的訪問級別,不能單獨設置訪問級別

public協議定義的(方法等)要求也是public

協議實現的(方法等)訪問級別必須 ≥ 類型的訪問級別,或者 ≥ 協議的訪問級別

  協議定義的方法實現訪問級別小於協議的訪問級別,所以報錯

  協議實現的(方法等)訪問級別 ≥ 協議的訪問級別,正確

  協議實現的(方法等)訪問級別  ≥ 類型的訪問級別,正確

下面代碼能編譯通過麼?

不能,因爲Person類型裏的run()方法默認的訪問級別爲internal,internal小於類型和協議的訪問級別public,所以錯誤,會報錯


擴展

  • 如果有顯式設置擴展的訪問級別,擴展添加的成員自動接收擴展的訪問級別
class Person {}
fileprivate extension Person {
    //這裏的run()方法的訪問界別默認接受擴展的訪問級別,爲fileprivate
    func run() {}
}
  • 如果沒有顯式設置擴展的訪問級別,擴展添加的成員的默認訪問級別,跟直接在類型中定義的成員一樣
class Person {}
extension Person {
    //沒有顯示給擴展設置訪問級別,run()方法的訪問級別和Person類型一致
    func run() {}
}
  •  可以單獨給擴展添加的成員設置訪問級別
class Person {}
 fileprivate extension Person {
    //單獨給run()方法設置private訪問級別
    private func run() {}
}
  • 不能給用於遵守協議的擴展顯式設置擴展的訪問級別

  • 在同一文件中的擴展,可以寫成類似多個部分的類型聲明 
  • 在原本的聲明中聲明一個私有成員,可以在同一文件的擴展中訪問它 
  • 在擴展中聲明一個私有成員,可以在同一文件的其他擴展中、原本聲明中訪問它
public class Person {
    private func run0() { }
    private func eat0() {
        run1()
    }
}

extension Person {
    private func run1() { }
    private func eat1() {
        run0()
    }
}

extension Person {
    private func eat2() {
        run1()
    }
}

上面代碼裏extension中的代碼可以看作是寫在Person類的原聲明中,所以即使是私有的,原有類和extension也可以互相訪問


將方法賦值給var\let

  • 方法也可以像函數那樣,賦值給var、let

當 run() 是實例方法,如下:

struct Person {
    var age: Int
    func run(_ v: Int) {
        print("func run", age, v)
    }
}

// fn1類型是(Person) -> ((Int) -> ()),接收一個Person實例,返回一個函數
var fn1 = Person.run
//fn2類型(Int) -> (),接收一個Int參數,無返回值
var fn2 = fn1(Person(age: 10))
fn2(20)  //func run 10 20

Person(age: 10).run(20)  //func run 10 20

由上圖可以看到,將方法賦值給var、let時,需要先傳給var、let一個初始化的實例,再傳入方法需要的參數,實際的結果與Person(age: 10).run(20) 這種方式相同

當 run() 是類方法,如下:

struct Person {
    var age: Int
    static func run(_ v: Int) {
        print("func run", v)
    }
}

//當run是類方法,fn1的類型是(Int) -> ()
var fn1 = Person.run
fn1(20) //func run 20

當實例方法和類方法同名的時候,在給var, let賦值會優先類方法,想要優先實例方法,必須指明給var, let賦值的類型爲實例方法類型,如下:

struct Person {
    var age: Int
    func run(_ v: Int) {
        print("func run", age, v)
    }
    static func run(_ v: Int) {
        print("func run", v)
    }
}


//fn類型是(int) -> (),也就是默認是類方法的run()
var fn = Person.run

//fn1類型是(Person) -> (Int) -> (),想要run是實例方法,必須指明方法類型
var fn1: (Person) -> (Int) -> () = Person.run

do設置代碼塊

  • Swift中不能直接用大括號{ }設置代碼塊:

Swift中可以用do設置代碼塊:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章