Swift有访问修饰符吗?

在Objective-C实例中数据可以是publicprotectedprivate。例如:

@interface Foo : NSObject
{
@public
int x;
@protected:
int y;
@private:
int z;
}
-(int) apple;
-(int) pear;
-(int) banana;
@end

我没有发现任何提到的访问修饰符在Swift参考。是否有可能限制Swift中数据的可见性?

104191 次浏览

对于Swift 1-3:

不,不可能。这里根本没有任何私有/受保护的方法和变量。

一切都是公开的。

<强>更新 从Swift 4开始,可以在这个线程

中看到其他答案

语言的语法没有关键字'public', 'private'或'protected'。这意味着一切都是公开的。当然,可以有一些替代方法来指定没有这些关键字的访问修饰符,但我在语言参考中找不到它。

据我所知,没有关键词“公共”,“私人”或“受保护”。这意味着一切都是公开的。

然而,Apple可能希望人们使用“协议”(其他地方称为接口)和工厂设计模式来隐藏实现类型的细节。

无论如何,这通常是一个很好的设计模式;因为它允许你改变你的实现类层次结构,同时保持逻辑类型系统不变。

截至Swift 3.0.1,有4个级别的访问,从最高(最不限制)到最低(最限制)描述如下。


1. openpublic

允许在定义模块(目标)之外使用一个实体。在指定框架的公共接口时,通常使用openpublic访问。

然而,open访问只适用于类和类成员,它与public访问的区别如下:

  • public类和类成员只能在定义模块(目标)中被子类化和重写。
  • open类和类成员可以在定义模块(目标)内外被子类化和重写。

// First.framework – A.swift


open class A {}

// First.framework – B.swift


public class B: A {} // ok

// Second.framework – C.swift


import First


internal class C: A {} // ok

// Second.framework – D.swift


import First


internal class D: B {} // error: B cannot be subclassed

2. internal

启用在定义模块(目标)中使用的实体。通常在定义应用程序或框架的内部结构时使用internal访问。

// First.framework – A.swift


internal struct A {}

// First.framework – B.swift


A() // ok

// Second.framework – C.swift


import First


A() // error: A is unavailable

3.fileprivate

将实体的使用限制到其定义的源文件。当在整个文件中使用特定功能块的实现细节时,通常使用fileprivate访问来隐藏这些细节。

// First.framework – A.swift


internal struct A {


fileprivate static let x: Int


}


A.x // ok

// First.framework – B.swift


A.x // error: x is not available

4. private

将实体的使用限制在其封闭声明范围内。通常使用private访问来隐藏特定功能块的实现细节,当这些细节仅在单个声明中使用时。

// First.framework – A.swift


internal struct A {


private static let x: Int


internal static func doSomethingWithX() {
x // ok
}


}


A.x // error: x is unavailable

使用协议、闭包和嵌套/内部类的组合,现在可以使用模块模式来隐藏Swift中的信息。它不是很干净,也不是很好读,但它确实有用。

例子:

protocol HuhThing {
var huh: Int { get set }
}


func HuhMaker() -> HuhThing {
class InnerHuh: HuhThing {
var innerVal: Int = 0
var huh: Int {
get {
return mysteriousMath(innerVal)
}


set {
innerVal = newValue / 2
}
}


func mysteriousMath(number: Int) -> Int {
return number * 3 + 2
}
}


return InnerHuh()
}


HuhMaker()
var h = HuhMaker()


h.huh      // 2
h.huh = 32
h.huh      // 50
h.huh = 39
h.huh      // 59

innerVal和mysterousmath隐藏在这里,不被外部使用,试图挖掘对象的方法应该会导致错误。

我只是阅读了Swift文档的一部分,所以如果这里有缺陷,请指出来,我很想知道。

你可以使用的一个选项是将实例创建包装到一个函数中,并在构造函数中提供适当的getter和setter:

class Counter {
let inc: () -> Int
let dec: () -> Int


init(start: Int) {
var n = start


inc = { ++n }
dec = { --n }
}
}




let c = Counter(start: 10)


c.inc()  // 11
c.inc()  // 12
c.dec()  // 11

当人们谈论在Swift或ObjC(或ruby或java或…)中创建“私有方法”时,这些方法不是真的私有的。他们周围没有实际的访问控制。任何语言只要能够提供一点内省功能,就可以让开发人员从类之外获得这些值,如果他们真的想要的话。

因此,我们在这里真正讨论的是一种定义面向公共接口的方法,该接口仅礼物我们想要的功能,并“隐藏”我们认为“私有”的其余部分。

Swift声明接口的机制是protocol,它可以用于此目的。

protocol MyClass {
var publicProperty:Int {get set}
func publicMethod(foo:String)->String
}


class MyClassImplementation : MyClass {
var publicProperty:Int = 5
var privateProperty:Int = 8


func publicMethod(foo:String)->String{
return privateMethod(foo)
}


func privateMethod(foo:String)->String{
return "Hello \(foo)"
}
}

请记住,协议是第一类类型,可以在类型可以使用的任何地方使用。,当以这种方式使用时,它们只公开自己的接口,而不是实现类型的接口。

因此,只要你在参数类型中使用MyClass而不是MyClassImplementation,等等,它应该都能工作:

func breakingAndEntering(foo:MyClass)->String{
return foo.privateMethod()
//ERROR: 'MyClass' does not have a member named 'privateMethod'
}

在一些直接赋值的情况下,你必须显式地使用类型,而不是依赖Swift来推断它,但这似乎并不是一个问题:

var myClass:MyClass = MyClassImplementation()

以这种方式使用协议是语义上的,相当简洁,而且在我看来很像我们在ObjC中为此目的使用的类扩展。

从Xcode 6 beta 4开始,Swift就有了访问修饰符。发布说明如下:

Swift访问控制分为三种访问级别:

  • 私人实体只能从定义它们的源文件中访问。
  • 内部实体可以在目标中定义它们的任何地方访问。
  • 公共实体可以从目标中的任何地方访问,也可以从导入当前目标模块的任何其他上下文访问。

隐式默认值是internal,所以在应用程序目标中,你可以关闭访问修饰符,除非你想要更严格的限制。在框架目标中(例如,如果你嵌入一个框架来在应用程序和共享或Today视图扩展之间共享代码),使用public指定你想要向框架的客户端公开的API。

现在在测试版4中,他们为Swift添加了访问修饰符。

Xcode 6 beta 4 realese notes:

Swift访问控制分为三种访问级别:

  • private实体只能从定义它们的源文件中访问。
  • internal实体可以在目标中定义它们的任何地方访问。
  • public实体可以从目标中的任何地方和任何其他上下文访问 导入当前目标的模块
默认情况下,源文件中的大多数实体都具有内部访问权限。这允许应用程序开发人员 在很大程度上忽略访问控制,而允许框架开发人员完全控制 框架的API。< / p >

希望为那些想要类似保护方法的人节省一些时间:

与其他答案一样,swift现在提供了“private”修饰符——它是按文件定义的,而不是像Java或c#那样按类定义的。这意味着如果你想要受保护的方法,你可以用swift私有方法如果它们在同一个文件中来实现

  1. 创建一个基类来保存“受保护的”方法(实际上是私有的)
  2. 子类化这个类以使用相同的方法
  3. 在其他文件中,您不能访问基类方法,即使您创建了子类

文件1:

class BaseClass {
private func protectedMethod() {


}
}


class SubClass : BaseClass {
func publicMethod() {
self.protectedMethod()  //this is ok as they are in same file
}
}

文件2:

func test() {
var a = BaseClass()
a.protectedMethod() //ERROR




var b = SubClass()
b.protectedMethod() //ERROR
}


class SubClass2 : BaseClass {
func publicMethod() {
self.protectedMethod() //ERROR
}

在Beta 6中,文档指出有三种不同的访问修饰符:

  • 公共
  • 内部
  • 私人

这三点适用于类、协议、函数和属性。

public var somePublicVariable = 0
internal let someInternalConstant = 0
private func somePrivateFunction() {}

更多信息,请查看访问控制 .

访问控制机制如在Xcode 6中引入:

Swift为代码中的实体提供了三种不同的访问级别。这些访问级别相对于定义实体的源文件,也相对于源文件所属的模块。

  • 公共访问使实体可以在来自它们定义模块的任何源文件中使用,也可以在来自导入定义模块的另一个模块的源文件中使用。在指定框架的公共接口时,通常使用公共访问。
  • 内部访问允许实体在定义模块的任何源文件中使用,但不能在该模块之外的任何源文件中使用。在定义应用程序或框架的内部结构时,通常使用内部访问。
  • 私人访问将实体的使用限制在它自己定义的源文件中。使用私有访问来隐藏特定功能块的实现细节。

公共访问是最高(限制最少)的访问级别,私有访问是最低(或限制最多)的访问级别。

默认访问它内部,因此不需要指定。还要注意,私人说明符在类级别上执行工作,但在源文件级别上。这意味着要使类的某些部分真正私有,您需要将其分离到自己的文件中。这也介绍了一些关于单元测试的有趣案例……

我提出的另一个观点(在上面的链接中有评论)是,你不能“升级”访问级别。如果你子类化了某个东西,你可以对它进行更多的限制,但反过来就不行。

最后一点也会影响函数、元组和其他东西,例如,如果一个函数使用了私人类,那么函数内部公共是无效的,因为它们可能无法访问私人类。这将导致编译器警告,并且您需要将该函数重新声明为私人函数。

Swift 3.0提供了5种不同的访问控制:

  1. 开放
  2. 公共
  3. 内部
  4. fileprivate
  5. 私人
开放访问和公共访问使实体可以在任何源文件中从它们的定义模块中使用,也可以在一个 导入定义模块的另一个模块中的源文件。你 当指定public时,通常使用open或public访问 到框架的接口。< / p >

内部访问允许实体在其定义模块的任何源文件中使用,但不能在该模块之外的任何源文件中使用 模块。定义应用程序的属性或属性时,通常使用内部访问

.框架的内部结构

File-private访问限制实体使用其自己定义的源文件。使用文件私有访问来隐藏 具体功能块的实现细节 详细信息在整个文件中使用

私人访问将实体的使用限制为封闭声明。的实现细节使用私有访问来隐藏 仅使用这些细节时的特定功能部分

< em >开放访问是最高(限制最小)的访问级别,而私人访问是最低(限制最大)的访问级别

默认访问级别

如果您自己没有明确指定访问级别,那么代码中的所有实体(除了少数特定的例外)都有一个默认的内部访问级别。因此,在许多情况下,您不需要在代码中指定显式的访问级别。

关于该主题的发布说明:

声明为public的类不能再在 它们的定义模块和声明为公共的方法不能再是 在定义模块之外被重写。允许一个类被 外部子类化的方法或外部覆盖的方法 它们是开放的,这是超越公共的一个新的访问级别。进口 Objective-C类和方法现在都被导入为开放的 比公开。使用@可测试导入导入模块的单元测试 仍然允许子类化公共类或内部类吗 如覆盖公共或内部方法。(se - 0117) < / p >

更多信息&细节: Swift编程语言(访问控制) < / p >

Swift 3和4也为变量和方法的访问级别带来了很多变化。Swift 3和4现在有4个不同的访问级别,其中/公众开放访问是最高(限制最少)的访问级别,而私人访问是最低(限制最多)的访问级别:

  • 私人函数和成员只能从实体本身(struct, class,…)及其扩展的范围内访问(在Swift 3中,扩展也受到限制)
  • fileprivate函数和成员只能从声明它们的源文件中访问。
  • 内部函数和成员(如果没有显式添加访问级别关键字,这是默认值)可以在目标中定义它们的任何地方访问。这就是为什么TestTarget不能自动访问所有的源,它们必须在xCode的文件检查器中被标记为可访问。
  • 公开的或公共的函数和成员可以从目标中的任何地方访问,也可以从导入当前目标模块的任何其他上下文访问。

有趣:

与其将每个单独的方法或成员标记为“private”,你可以在类/结构的扩展中覆盖一些方法(例如典型的helper函数),并将整个扩展标记为“private”。

class foo { }


private extension foo {
func somePrivateHelperFunction01() { }
func somePrivateHelperFunction02() { }
func somePrivateHelperFunction03() { }
}

为了获得更好的可维护代码,这可能是一个好主意。你可以很容易地切换到非私有(例如单元测试),只需要改变一个词。

苹果文档

Swift 4 / Swift 5

正如Swift文档-访问控制中提到的,Swift有5访问控制:

  • openpublic:可以从它们的模块实体和导入定义模块的任何模块实体访问。

  • internal:只能从它们模块的实体访问。这是默认的访问级别。

  • filprivate private:只能在您定义的有限范围内进行有限访问。



开放公共之间的区别是什么?

开放与之前版本的Swift中的public相同,它们允许来自其他模块的类使用和继承它们,即:它们可以从其他模块继承子类。此外,它们允许来自其他模块的成员使用和重写它们。同样的逻辑也适用于它们的模块。

公共允许来自其他模块的类使用它们,但继承它们,即:它们不能被其他模块子类化。此外,它们允许来自其他模块的成员使用它们,但不允许重写它们。对于它们的模块,它们具有相同的open's逻辑(它们允许类使用和继承它们;它们允许成员使用和重写它们)。


fileprivate私人之间的区别是什么?

fileprivate可以从它们的整个文件中访问。

私人只能从它们的单个声明和同一文件中该声明的扩展中访问;例如:

// Declaring "A" class that has the two types of "private" and "fileprivate":
class A {
private var aPrivate: String?
fileprivate var aFileprivate: String?


func accessMySelf() {
// this works fine
self.aPrivate = ""
self.aFileprivate = ""
}
}


// Declaring "B" for checking the abiltiy of accessing "A" class:
class B {
func accessA() {
// create an instance of "A" class
let aObject = A()


// Error! this is NOT accessable...
aObject.aPrivate = "I CANNOT set a value for it!"


// this works fine
aObject.aFileprivate = "I CAN set a value for it!"
}
}



Swift 3和Swift 4的访问控制有什么不同?

正如se - 0169提案中提到的,Swift 4中唯一的改进是扩展了私人访问控制范围,可以从同一文件中声明的扩展访问;例如:

struct MyStruct {
private let myMessage = "Hello World"
}


extension MyStruct {
func printMyMessage() {
print(myMessage)
// In Swift 3, you will get a compile time error:
// error: 'myMessage' is inaccessible due to 'private' protection level


// In Swift 4 it should works fine!
}
}

因此,没有必要将myMessage声明为fileprivate以在整个文件中可访问。

在swift 2.0之前,只有三个访问级别[公共,内部,私有] 但是在swift 3.0中,苹果增加了两个新的访问级别,即[Open, fileType] 现在在swift 3.0中有5个访问级别 这里我想明确一下这两个访问级别的作用 1. Open:这与Public非常相似,但唯一的区别是Public 可以访问子类并重写,而开放访问级别不能访问这张照片取自Medium网站,它描述了开放和公共访问之间的区别

现在到第二个新的访问级别 2. 文件类型是更大版本的私有或更低的访问级别比内部 fileType可以访问[class, struct, enum]的扩展部分 而且private不能访问扩展部分的代码,它只能访问 词法作用域 这张照片取自Medium网站,描述了fileType和Private访问级别之间的区别 < / p >