[Swift基础语法入门(强推)]Swift3 中的 Five Access Control Level

Swift 中包含五大 Access Control Levelopenpublicinternalfileprivateprivate(highest access level -> lowest access level,换句话说 least restrictive -> most restrictive 下文有详细解释)

  • open 和 public 定义的 entity 允许被所有作用域(包括当前模块内文件或是其他模块文件)访问;
  • internal 作用范围仅限在 entity 所定义的模块内部,其他模块文件无法访问。ps:默认 Access Control Level 为 Internal;
  • fileprivate 作用范围为当前文件,因此一个文件内定义多个类,某个类标记为 fileprivate 之后,当前模块内的其他文件无法访问这个类,而当前文件内定义的其他类可以访问;
  • private 只允许当前作用域访问。

open 只能应用于类和类成员,与 public 的不同之处在于:

  • public 以及其他 more restrictive 访问级别只能在定义的模块内被继承;
  • public 以及其他 more restrictive 访问级别只能在定义的模块内被重写;
  • open 则既可以在定义的模块或是其他模块内被继承或重写。

重要指导思想

一个优先级较高(约束力很弱,谁都能访问)的实体无法在一个优先级较低(约束力条件很强,不是谁都能访问)的实体内被定义。原文:No entity can be defined in terms of another entity that has a lower (more restrictive) access level.

  • 标记为 public 的变量 x 定义在一个 internalfileprivateprivate 类型 A 内部是不恰当的,分析如下:类型 A 既然被标记为 internal,只允许被当前模块访问,那么即使内部定义一个 public 标记的属性,也无法其他模块访问到,因此毫无意义;
  • 函数被标记的访问优先级不能高于它的传参和返回值。分析如下:首先优先级高意味着约束力较弱,容易被访问,那么定义一个 public 函数 funcA 在模块 A 中,传参和返回值类型使用默认internal(只允许对当前模块开放)。那么问题来了:模块 B 可以访问到函数funcA ,传入模块 B 中的参数我首先要实例化才能传入,实例化操作又必须允许我访问那个参数类型。但由于标记了 internal,所以其他模块中显然不允许;返回值类型同样是这个道理。

自定义类型中的 Access Control Level

如果自定义的类型被标记为 fileprivateprivate ,那么该类型中的成员默认为 fileprivateprivate ;如果自定义的类型被标记为 publicinternal,那么类型成员默认为internalNote:并不是 Public,当然你可以明确标记为 public)。

Note:例子中类标记的 Access Control Level 优先级一定 >= 类内部成员的优先级! 再次强调优先级越高约束力越弱,优先级越低约束力越高。优先级从高到低排列:open public internal(默认) fileprivate private

public class SomePublicClass {                  // 明确为 public class
    public var somePublicProperty = 0            // 明确为 public class member
    var someInternalProperty = 0                 // => 隐性为 internal class member
    fileprivate func someFilePrivateMethod() {}  // 明确为 file-private class member
    private func somePrivateMethod() {}          // 明确为 private class member
}
 
class SomeInternalClass {                       // => 隐性为 internal class
    var someInternalProperty = 0                 // => 隐性为 internal class member
    fileprivate func someFilePrivateMethod() {}  // 明确为 file-private class member
    private func somePrivateMethod() {}          // 明确为 private class member
}
 
fileprivate class SomeFilePrivateClass {        // 明确为 file-private class
    func someFilePrivateMethod() {}              // => 隐性为 file-private class member
    private func somePrivateMethod() {}          // 明确为 private class member
}
 
private class SomePrivateClass {                // 明确为 private class
    func somePrivateMethod() {}                  // => 隐性为 private class member
}

Tuple 类型

元组类型的访问级别是取它包含的所有成员类型的最低优先级(The most restrictive,约束力最强)。比如,组合一个 private 类型和一个 internal 类型成为一个元组,那么当前元组类型访问优先级为 private

Tuple 类型无法被单独明确定义(若你持有怀疑,不妨一试),这与类、结构体、枚举、函数略有不同。它会根据要组合的多个类型访问级别来自动限制元组的 Access Control Level。

函数

函数通常有传参返回值,和 Tuple 类型类似,函数自身的访问级别是取传参和返回值类型两者中的最低优先级。如果函数根据上下文(传参和返回值)得到的访问级别和默认不匹配(不一定是internal,若有疑惑,请见上面例子),那么就需要你明确标记访问级别。

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

someFunction 并未明确 Access Control Level,这里上下文得到默认访问级别为 internal。但是注意返回值 (SomeInternalClass, SomePrivateClass) 元组,根据前一章得到的知识,我们得到元组的访问级别为 private,在根据本章一开始说的,函数的访问级别不难得出: private。和默认的 internal相悖,因此我们需要明确标记访问级别了。

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

枚举类型

枚举中的各个 case 访问级别和枚举的 Access Control Level 保持一致。

Raw Values and Associated Values

The types used for any raw values or associated values in an enumeration definition must have an access level at least as high as the enumeration’s access level. You cannot use a private type as the raw-value type of an enumeration with an internal access level, for example.

内嵌类型

  • 内嵌到一个 private 类型,默认访问级别为 private
  • 内嵌到一个 fileprivate 类型,默认访问级别为 fileprivate
  • 内嵌到一个 publicinternal 类型,默认访问级别为 internal,当然如果你想对其他模块开放,明确标记为 public即可

继承

subclass 的 Access Control Level 优先级不能高于 superclass 的优先级,比如你继承了一个标记为internal的父类,那么子类无法被标记为 public

override 重写一个父类的方法或属性,可以提高访问级别,前提是: 当前上下文中,重写属性必须可以访问!官方提供的例子有误。见例:

public class A {
    private func someMethod() {} //=> 私有 官方提供例子标记为 private 我认为改成 fileprivate 
}
 
internal class B: A {
    override internal func someMethod() {} // => 变成模块可见 当然也不能是public 因为外部限制为internal了
}

原文描述如下:In addition, you can override any class member (method, property, initializer, or subscript) that is visible in a certain access context.

这里明确说了需要重写的类成员必须在当前访问上下文中可见。但是官方提供的例子是 private ,对 B 根本不可见,所以我认为可能是笔误,应该写成 fileprivate

重写方法中允许调用父类方法,前提是当前访问上下文必须对其可见:

public class A {
    fileprivate func someMethod() {}
}

internal class B: A {
    override internal func someMethod() {
        super.someMethod()
    }
}

常量,变量,属性和下标

常量,变量或属性优先级不能设置的比声明类型的优先级高,举例:

internal var privateInstance = SomePrivateClass()

设置了 internal 说明这个 privateInstance 允许向当前模块的其他源文件开放,读没有问题,但是写操作是无法实现的,你想实例化一个新的SomePrivateClass类,但是由于私有限制,你无法在其他文件使用这个类。

Getters & Setters

Getter 和 Setter 方法自动默认和声明属性的访问优先级一致,但是允许 Setter 方法的Access Control Level 较 Getter 方法低一些,即访问限制变大,可能原来外部能访问到的,现在只能内部访问了。一般我们会用 fileprivate(set), private(set),和 internal(set) 来明确属性的setter 访问等级较默认低一些。举例

struct TrackedString {
    private(set) var numberOfEdits = 0 
    var value: String = "" {
        didSet {
            numberOfEdits += 1
        }
    }
}

numberOfEdits 变量的默认访问级别为 internal ,现在设置为 private(set)后,只有在 TrackedString 结构体内部能够 Setter 写,外部只能Getter 读。

类型别名

自定义的类型别名的 Access Control Level 允许小于等于(<=) 它绑定绑定的访问优先级。例如:

// 源文件main.swift 中定义如下:
fileprivate typealias myType = Int64 

private typealias mymyType = myType // ✅ 
typealias mymyType = myType // ❌

分析如下:类型别名 myType 访问域仅限当前的 main.swift文件,那么再为它设置一个别名 mymyType 不管如何最多只能在当前文件下被使用,所以访问级别应该 <= fileprivate ,要么是 fileprivate 或是 private。 而标记了❌的代码行错误原因是默认是 internal 访问级别,显然和myType 初衷相悖。

更多

更多请见官方文档:The Swift Programming Language - Access Control
卓同学 Swift3 必看系列中的:Swift 3必看:新的访问控制fileprivate和open

欢迎关注我的微博:@pmst

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • 访问控制能够限制你的代码从其他文件和模块中访问,这个特性能够让你隐藏你具体的实现,并且也可以让代码能够访问和使用。...
    China_ly阅读 342评论 0 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 【Swift 3.1】24 -访问权限 (Access Control) 自从苹果2014年发布Swift,到现在...
    Lebron_James阅读 2,174评论 5 1
  • (根据王佩丰Excel学习视频整理) 一、分类汇总工具 1.分类汇总 在数据选项卡中,对需要进行分类汇总的数据先排...
    罗恬Sophie阅读 1,001评论 0 0
  • 今天是个特殊的日子,要给小仙女一个惊喜,就约了个小仙女出去玩。 值得一提的是,今天小仙女发型完全出自本粗糙发型设计...
    CDMatt阅读 504评论 0 0