这是示例代码. Swift3中有哪些,过去工作正常 –
open class MyClass { private let value: Int static var defaultValue: Int { return 10 } public init(value: Int = MyClass.defaultValue) { self.value = value } }
为了使代码在Swift4中运行,我必须将defaultValue的访问控制更改为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 } }
当我想知道发生了什么时,我试图删除MyClass的开放访问控制,它允许我删除defaultValue的访问标识符.甚至可以把它私有化.
class MyClass { private let value: Int private static var defaultValue: Int { return 10 } public init(value: Int = MyClass.defaultValue) { self.value = value } }
我理解所有访问标识符,但我无法理解这种行为.特别是第一种情况,xcode强迫我将defaultValue的访问控制权改为public.
请帮忙.
我的原始答案(如下所示)现在已经过时了 – 弹性模型 are to be implemented in Swift 4.2的开头,引入了@inlinable和@usableFromInline属性,对应于旧的@_inlineable和@_versioned属性.此外,更重要的是,公共可访问函数的默认参数可以引用的规则再次发生了变化.要回顾以前的规则:
>在Swift 3中,没有强制执行这样的默认参数表达式可以引用的访问级别(允许你的第一个例子,其中defaultValue是内部的).
>在Swift 4中,这样的默认参数只能引用作为模块接口的一部分公开的声明,包括那些在另一个模块中用户无法直接看到的声明(即@_versioned internal).
但是在Swift 4.2中,随着SE-0193,the rule is now的实现,公共可访问函数的默认参数表达式只能引用可公开访问的声明(甚至不是@inlinable internal或@usableFromInline internal).
我相信这为在模块生成的接口文件中显示默认参数表达式铺平了道路.目前Swift只显示了一个无用的=默认值,但我相信这会改变为实际显示默认参数.这只能通过适当的新访问控制限制实际发生(编辑:这是now happening).
老答案(斯威夫特4)
这种变化是由于已经通过强调属性(@_ inlineable,@ _ version,@ _ fixed_layout)提供的弹性模型的工作,但还没有正式确定(所以你可能不应该自己使用这些属性) .您可以阅读有关full proposed details of the resilience model here以及Swift evolution discussion on it here的信息.
简而言之,可内联函数的实现和声明作为模块接口的一部分公开,因此可以在从另一个模块调用时进行内联.因此,一个可内联的函数也必须首先公开访问(即公共或更高版本).
您遇到的是default argument expressions for publically accessible functions inlineable的更改,这意味着它们必须可以直接在调用模块的二进制文件中进行评估.这减少了使用来自另一个模块的默认参数值调用函数的开销,因为编译器不再需要为每个默认参数执行函数调用.它已经知道了实施.
我不相信Swift 4本身的发布中正式记录了这一变化,但是Swift编译工程师Slava Pestov确认了这一点,who says:
Swift 3.1 added resilience diagnostics for inlineable code, which is not an officially supported feature, but in Swift 4 we switched these checks on for default argument expressions as well.
因此,如果您有一个具有默认参数表达式的公共可访问函数(例如,在您的情况下为MyClass.defaultValue),则该表达式现在只能引用也是该模块接口的一部分的内容.因此,您需要公开访问defaultValue.
不幸的是,目前无法将私有函数的声明作为模块接口的一部分(这将允许您在默认参数表达式中使用它).促进此操作的属性是@_versioned,但由于以下原因,它被禁止(文件)私有given by Slava Pestov:
It would be a trivial change to allow
@_versioned
onprivate
and
fileprivate
declarations, but there are two pitfalls to keep in mind:
Private symbols are mangled with a ‘discriminator’ which is basically a hash of the file name. So now it would be part of the ABI,
which seems fragile — you can’t move the private function to another
source file, or rename the source file.Similarly, right now a
@_versioned
function becoming public is an ABI compatible change. This would no longer work if you could have
private @_versioned
functions, because the symbol name would change if
it becamepublic
.For these reasons we decided against “
private versioned
” as a concept.
I feel likeinternal
is enough here.
您可以使用@_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的声明现在作为模块接口的一部分导出,但仍然无法从另一个模块的代码中直接调用(因为它是内部的).但是,该模块的编译器现在可以在评估默认参数表达式时调用它.但是,如前所述,您可能不应该在此处使用下划线属性;你应该等到弹性模型最终确定.