【问题标题】:Access control in swift 4swift 4中的访问控制
【发布时间】:2018-08-09 17:14:15
【问题描述】:

在从Swift3 升级到Swift4 时,我遇到了一些与access control 相关的问题。

这里是示例代码。 Swift3 中的那个,过去工作得很好 -

open class MyClass {
    private let value: Int
    static var defaultValue: Int { return 10 }
    public init(value: Int = MyClass.defaultValue) {
        self.value = value
    }
}

要使代码在Swift4 中运行,我必须将access controldefaultValue 更改为public。 这里是Swift4,编译版

open class MyClass {
    private let value: Int
    static public var defaultValue: Int { return 10 }
    public init(value: Int = MyClass.defaultValue) {
        self.value = value
    }
}

当我想知道发生了什么时,我尝试删除 MyClassopen 访问控制,它允许我删除 defaultValueaccess 标识符。甚至可以发到private

class MyClass {
    private let value: Int
    private static var defaultValue: Int { return 10 }
    public init(value: Int = MyClass.defaultValue) {
        self.value = value
    }
}

我了解所有访问标识符,但我无法理解这种行为。尤其是第一种情况,xcode 强迫我将defaultValueaccess control 更改为public

请帮忙。

【问题讨论】:

    标签: swift access-control swift4


    【解决方案1】:

    我的原始答案(如下所示)现在大多已过时 - 弹性模型 are to be implemented in Swift 4.2 的开端引入了 @inlinable@usableFromInline 属性,对应于旧的 @_inlineable@_versioned 属性.

    此外,更重要的是,公共可访问函数的默认参数可以引用的规则再次发生了变化。回顾之前的规则:

    • 在 Swift 3 中,没有强制执行此类默认参数表达式可以引用的访问级别(允许您的第一个示例,其中 defaultValueinternal)。

    • 在 Swift 4 中,这样的默认参数只能引用作为模块接口的一部分公开的声明,包括其他模块中的用户无法直接看到的声明(即@_versioned internal)。

    但是在 Swift 4.2 中,通过实现 SE-0193the rule is now,可公开访问的函数的默认参数表达式只能引用可公开访问的声明(甚至 @inlinable internal@usableFromInline internal)。

    相信这为在模块生成的接口文件中显示默认参数表达式铺平了道路。目前 Swift 只显示了一个无用的 = default,但我相信这将改变为实际显示默认参数。这只有在新的访问控制限制到位的情况下才能真正发生(编辑:这是now happening)。


    旧答案 (Swift 4)

    此更改是由于已通过下划线属性(@_inlineable@_versioned@_fixed_layout)提供弹性模型,但尚未正式确定(因此您可能不应该这样做)自己使用这些属性)。您可以阅读有关full proposed details of the resilience model here 以及Swift evolution discussion on it here 的信息。

    简而言之,inlineable 函数的 实现 以及声明都作为模块接口的一部分公开,因此在调用时可以内联另一个模块。因此,可内联函数也必须以可公开访问的开头(即public 或更高版本)。

    您遇到的是导致default argument expressions for publically accessible functions inlineable 的更改,这意味着它们必须可用于直接在调用模块的二进制文件中进行评估。这减少了使用来自另一个模块的默认参数值调用函数的开销,因为编译器不再需要为每个默认参数执行函数调用;它已经知道实现了。

    我不相信这个变化在 Swift 4 本身的发布中被正式记录,但是 Swift 编译器工程师 Slava Pestov 证实了这一点,who says

    Swift 3.1 为可内联代码添加了弹性诊断,这不是官方支持的功能,但在 Swift 4 中,我们也为默认参数表达式打开了这些检查。

    因此,如果您有一个带有默认参数表达式的可公开访问的函数(例如 MyClass.defaultValue 在您的情况下),则该表达式现在只能引用也是该模块接口一部分的事物。因此,您需要将defaultValue 设为可公开访问。

    不幸的是,目前无法将private 函数的声明作为模块接口的一部分(这将允许您在默认参数表达式中使用它)。可以促进这一点的属性是@_versioned,但由于以下原因given by Slava Pestov(file)private 禁止使用它:

    private 上允许@_versionedfileprivate 声明,但要记住两个陷阱:

    • 私有符号被一个“鉴别器”破坏,它基本上是文件名的哈希值。所以现在它将成为 ABI 的一部分, 这看起来很脆弱——你不能将私有函数移动到另一个 源文件,或重命名源文件。

    • 同样,现在公开的@_versioned 函数是与 ABI 兼容的更改。如果您可以,这将不再有效 private @_versioned 函数,因为符号名称会改变 它变成了public

    出于这些原因,我们决定不使用“private versioned”作为概念。 我觉得internal 就够了。

    可以通过@_versioned var defaultValue 实现此目的:

    open class MyClass {
    
        private let value: Int
    
        @_versioned static var defaultValue: Int {
            return 10
        }
    
        public init(value: Int = MyClass.defaultValue) {
            self.value = value
        }
    }
    

    MyClass.defaultValue 的声明现在作为模块接口的一部分导出,但仍然不能从另一个模块的代码中直接调用(因为它是 internal)。但是,该模块的 编译器 现在可以在评估默认参数表达式时调用它。但是,如前所述,您可能不应该在这里使用下划线属性;您应该等到弹性模型最终确定。

    【讨论】:

    • 令人着迷,船长!
    猜你喜欢
    • 1970-01-01
    • 2013-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-25
    • 2019-01-16
    • 2016-03-28
    • 2013-07-05
    相关资源
    最近更新 更多