訪問控制
訪問控制
在訪問權(quán)限控制這塊,Swift提供了5個(gè)不同的訪問級別(以下從高到低排列)
- open:允許在定義實(shí)體的模塊、其他模塊中訪問,允許其他模塊進(jìn)行繼承重寫(open只能用在類、類成員上)
- public:允許在定義實(shí)體的模塊、其他模塊中訪問,不允許其他模塊進(jìn)行繼承重寫
- internal:只允許在定義實(shí)體的模塊中訪問,不允許在其他模塊中訪問
- fileprivate:只允許在定義實(shí)體的源文件中訪問
- private:只允許在定義實(shí)體的封閉聲明中訪問
絕大部分實(shí)體默認(rèn)都是 internal 級別
訪問級別的使用準(zhǔn)則
一個(gè)實(shí)體不可以被更低訪問級別的實(shí)體定義,比如:
- 變量、常量類型 >= 變量、常量
- 參數(shù)類型、返回值類型 >= 函數(shù)
- 父類 >= 子類
- 父協(xié)議 >= 子協(xié)議
- 原類型 >= typealias
- 原始值類型、關(guān)聯(lián)值類型 >= 枚舉類型
- 定義類型A時(shí)用到的其他類型 >= 類型A
- ...
元組類型
- 元組類型的訪問級別是所有成員類型最低的那個(gè)
internal struct Dog{ }
fileprivate class Car { }
fileprivate var data1:(Dog,Car)
private var data2:(Dog,Car)
//internal var data3:(Dog,Car)//Car的訪問級別低于data3 報(bào)錯(cuò)
//public var data3:(Dog,Car)//Dog Car的訪問級別均低于data3 報(bào)錯(cuò)
泛型類型
- 泛型類型的訪問級別是類型的訪問級別以及所有泛型類型參數(shù)的訪問級別中最低的那個(gè)
internal struct Dog{ }
fileprivate class Car { }
public class Person<T1,T2>{}
fileprivate var p1 = Person<Car,Dog>()
private var p2 = Person<Car,Dog>()
//internal var p3 = Person<Car,Dog>()//p3的訪問級別高于Car 報(bào)錯(cuò)
成員嵌套類型
- 類型的訪問級別會影響成員(屬性、方法、初始化器、下標(biāo))、嵌套類型的默認(rèn)訪問級別
- 一般情況下,類型為private或fileprivate,那么成員、嵌套類型默認(rèn)也是private或fileprivate
- 一般情況下,類型為internal或public,那么成員、嵌套類型默認(rèn)是internal
public class PublicClass{
public var p1 = 0//public
var p2 = 0//internal
fileprivate func f1(){}//fileprivate
private func f2(){}//private
}
class InternalClass {//internal
var p = 0//internal
fileprivate func f1(){}//fileprivate
private func f2(){}//private
}
fileprivate class fileprivateClass{
func f1(){}//fileprivate
private func f2(){}//private
}
private class privateClass{
func f1(){}//private
}
成員的重寫
- 子類重寫成員的訪問級別必須 >= 子類的訪問級別,或者 >= 父類被重寫的成員的訪問級別
- 父類的成員不能被成員作用域外定義的子類重寫
class Person {
internal func run(){}
}
fileprivate class Student : Person{
fileprivate override func run() {//至少>=子類的訪問級別或者>=重寫方法的訪問級別
}
}
//如果不是定義在全局則會報(bào)錯(cuò)
private class Person{//因?yàn)閜rivate在全局定義 作用域即為該文件 其實(shí)定義的是fileprivate
}
fileprivate class Student :Person{ }
private struct Dog{////因?yàn)閜rivate在全局定義 作用域即為該文件 其實(shí)定義的是fileprivate
var age = 0 //屬性為fileprivate
func run() {//方法為fileprivate
}
}
private struct Woman{
var dog:Dog = Dog()
mutating func walk() {
dog.run()//所以此處可以訪問
dog.age = 1
}
}

直接在全局作用域下定義的private等價(jià)于fileprivate
getter、setter
- getter、setter默認(rèn)自動接收它們所屬環(huán)境的訪問級別
- 可以給setter單獨(dú)設(shè)置一個(gè)比getter更低的訪問級別,用以限制寫的權(quán)限
fileprivate(set) public var num = 10//訪問權(quán)限為public 寫權(quán)限為fileprivate
class Person {
private(set) var age = 0//存儲屬性
fileprivate(set) public var weight: Int{//計(jì)算屬性
set{}
get{ 10 }
}
internal(set) public subscript(index: Int) -> Int{//下標(biāo)
set{}
get{ index }
}
}
var p = Person()
//p.age = 10 報(bào)錯(cuò)
print(p.age)//輸出:0
初始化器
- 如果一個(gè)public類想在另一個(gè)模塊調(diào)用編譯生成 的默認(rèn)無參初始化器,必須顯示提供public的無參初始化器, 因?yàn)?strong>public類的默認(rèn)初始化器時(shí)internal級別
- required初始化器 >= 它的默認(rèn)訪問級別
- 如果結(jié)構(gòu)體有private\fileprivate的存儲實(shí)例屬性,那么它的成員初始化器也是private\fileprivate,否則就是internal
枚舉類型的case
- 不能給enum的每個(gè)case單獨(dú)設(shè)置訪問級別
- 每個(gè)case自動接收enum的訪問級別
協(xié)議
- 協(xié)議中定義的要求自動接收協(xié)議的訪問級別,不能單獨(dú)設(shè)置訪問級別
- 協(xié)議實(shí)現(xiàn)的訪問級別必須 >= 類型的訪問級別,或者 >= 協(xié)議的訪問級別
fileprivate protocol Runnable{
func run()
}
internal class Person : Runnable{
//訪問權(quán)限至少要大于 Person 的訪問級別 或協(xié)議的訪問級別
fileprivate func run() {//private修飾則報(bào)錯(cuò)
}
}
擴(kuò)展
- 如果有顯示設(shè)置擴(kuò)展的訪問級別,擴(kuò)展添加的成員自動接收擴(kuò)展的訪問級別
- 如果沒有顯示設(shè)置擴(kuò)展的訪問級別,擴(kuò)展添加的成員的默認(rèn)訪問級別,跟直接在類型中定義的成員一樣
- 可以單獨(dú)給擴(kuò)展添加的成員設(shè)置訪問級別
- 不能給用于遵守協(xié)議的擴(kuò)展顯示設(shè)置擴(kuò)展的訪問級別(如下例)
fileprivate protocol Runnable{
func run()
}
class Person{
}
internal extension Person : Runnable{//報(bào)錯(cuò):不能修飾任何訪問控制關(guān)鍵字
func run() {
}
}
- 在同一文件中的擴(kuò)展,可以寫成類似多個(gè)部分的類型聲明
- 在原本的聲明中聲明一個(gè)私有成員,可以再同一文件的擴(kuò)展中訪問它
- 在擴(kuò)展中聲明一個(gè)私有成員,可以在同一文件的其他擴(kuò)展中、原本聲明中訪問它
//可以看做擴(kuò)展中的成員都是在類中定義
public class Person{
private func run0(){}
private func eat0(){
run1()//可以相互調(diào)用
eat2()
}
}
extension Person{
private func run1(){}
private func eat1(){
run0()
eat2()
}
}
extension Person{
private func eat2(){
run1()
eat0()
}
}
將方法賦值類var/let
- 方法也可以像函數(shù)那樣,賦值給一個(gè)let或者var
//實(shí)例方法
struct Person {
var age: Int
func run(_ v: Int) {
print("func run",age,v)
}
}
var fn1 = Person.run//fn1類型: (Person) -> ((Int) -> ())
var fn2 = fn1(Person(age: 10))//fn2類型:(Int) -> () 先傳Person實(shí)例 返回函數(shù) 再傳10
fn2(10)
//類型方法
struct Person {
var age: Int
static func run(_ v: Int) {
print("func run",v)
}
}
var fn1 = Person.run//fn1類型: (Int) -> ()
fn1(10)
struct Person {
var age: Int
func run(_ v: Int) {
print("Obj func run",v)
}
static func run(_ v: Int) {
print("Class func run",v)
}
}
//如果類型方法與實(shí)例方法重名 則聲明類型進(jìn)行區(qū)分
var fn1:(Person) -> ((Int) -> ()) = Person.run
var fn2 = fn1(Person(age: 10))
fn2(20)
var fn3:(Int) -> () = Person.run
fn3(30)