[英]Access control in swift 4
從Swift3
升級到Swift4
時,我遇到了一些與access control
有關的問題。
這是示例代碼。 Swift3
中有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
access control
更改為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
open
訪問控制,它允許我刪除defaultValue
access
標識符。 甚至可以把它private
。
class MyClass {
private let value: Int
private static var defaultValue: Int { return 10 }
public init(value: Int = MyClass.defaultValue) {
self.value = value
}
}
我理解所有訪問標識符,但我無法理解這種行為。 特別是第一種情況, xcode
強迫我將defaultValue
access control
權改為public
。
請幫忙。
我的原始答案(如下所示)現在已經過時了 - 彈性模型的開頭將在Swift 4.2中實現,引入了@inlinable
和@usableFromInline
屬性,對應於舊的@_inlineable
和@_versioned
屬性。
此外,更重要的是,公共可訪問函數的默認參數可以引用的規則再次發生了變化。 要回顧以前的規則:
在Swift 3中,沒有強制執行這樣的默認參數表達式可以引用的訪問級別(允許你的第一個例子,其中defaultValue
是internal
)。
在Swift 4中,這樣的默認參數只能引用作為模塊接口的一部分公開的聲明, 包括那些在另一個模塊中用戶無法直接看到的聲明(即@_versioned internal
)。
但是在Swift 4.2中,隨着SE-0193的實現, 規則現在是公共可訪問函數的默認參數表達式只能引用可公開訪問的聲明(甚至不是@inlinable internal
或@usableFromInline internal
)。
我相信這為在模塊生成的接口文件中顯示默認參數表達式鋪平了道路。 目前Swift只顯示了一個無用的= default
,但我相信這會改變為實際顯示默認參數。 這只能通過這種新的訪問控制限制實際發生( 編輯 : 現在正在發生 )。
這種變化是由於已經通過強調屬性( @_inlineable
, @_versioned
, @_fixed_layout
)提供的彈性模型的工作,但還沒有正式確定(所以你可能不應該自己使用這些屬性) 。 您可以在此處閱讀有關彈性模型的完整建議細節 ,以及此處的Swift演變討論 。
簡而言之,可內聯函數的實現和聲明作為模塊接口的一部分公開,因此可以在從另一個模塊調用時進行內聯。 因此,必須首先公開訪問內聯函數(即public
或更高版本)。
您遇到的是一種更改,它使公共可訪問函數的默認參數表達式可內聯 ,這意味着它們必須可以直接在調用模塊的二進制文件中進行評估。 這減少了使用來自另一個模塊的默認參數值調用函數的開銷,因為編譯器不再需要為每個默認參數執行函數調用。 它已經知道了實施。
我不相信Swift 4本身的發布正式記錄了這一變化,但Swift編譯工程師Slava Pestov證實了這一點, 他說 :
Swift 3.1為內聯代碼添加了彈性診斷,這不是官方支持的功能,但在Swift 4中我們也將這些檢查切換為默認參數表達式。
因此,如果您有一個具有默認參數表達式的公共可訪問函數(例如,在您的情況下為MyClass.defaultValue
),則該表達式現在只能引用也是該模塊接口的一部分的內容。 因此,您需要公開訪問defaultValue
。
不幸的是,目前無法將private
函數的聲明作為模塊接口的一部分(這將允許您在默認參數表達式中使用它)。 促進此操作的屬性是@_versioned
,但由於Slava Pestov給出以下原因,它被禁止(file)private
:
在
private
和fileprivate
聲明中允許@_versioned
是一個微不足道的變化,但要記住兩個陷阱:
私有符號使用'discriminator'進行損壞,該'discriminator'基本上是文件名的散列。 所以現在它將成為ABI的一部分,它似乎很脆弱 - 你不能將私有函數移動到另一個源文件,或重命名源文件。
同樣,現在
@_versioned
函數變為公共是ABI兼容的更改。 如果您可以擁有private @_versioned
函數,這將不再有效,因為如果它變為public
符號名稱將會更改。出於這些原因,我們決定反對“
private versioned
”作為一個概念。 我覺得internal
就足夠了。
您可以使用@_versioned var defaultValue
實現此@_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
)。 但是,該模塊的編譯器現在可以在評估默認參數表達式時調用它。 但是,如前所述,您可能不應該在此處使用下划線屬性; 你應該等到彈性模型最終確定。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.