Swift是否具有访问修饰符?


273

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

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

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


我也不。Apple至少应为私人引入礼节,例如在python中以下划线为前缀。
Ciantic


Answers:


419

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

37
有人可以向我解释为什么这没什么大不了的吗?
Zaky German 2014年

15
始终是OOP一些方法或变量应该是私有的或保护。这允许实现SOLID设计,因为将大型方法划分为多个较小的方法,每种方法都有自己的职责,可以覆盖,但只有“主要”方法才可以公开使用。
akashivskyy 2014年

19
我个人不喜欢采用下划线/特殊字符领先的“私有”方法的解决方案。即使可以保证我自己是唯一看到此代码的人,它也可以使代码更节省/更少出错,因为编译器将完全阻止您执行不应执行的操作。因此,我认为他们应该尽快摆脱“访问控制机制”,使人们不会习惯不良习惯。
Jonas Eschmann 2014年

10
Xcode 6 beta发行说明说:“此种子未启用访问控制(公共/私有成员)。(15747445)”
Martin Ullrich

9
@alcalde公共接口的想法非常有价值。如果您打算将类中的所有代码都驻留在公共API内的函数内部,那么我认为这是非常有限的。另一方面,拥有指定的公共API可以在不影响使用者的情况下更改实现(包括使用私有方法)。如果有人“需要”使用内部类方法,我会觉得他们误解了类功能的局限性(或试图使用越野车类)。
jinglesthula 2014年

26

迅捷4 /迅捷5

Swift文档-访问控制中所述,Swift具有5个访问控制

  • 打开public:可以从其模块的实体以及导入定义模块的任何模块的实体中进行访问。

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

  • 文件私有私有:只能在定义它们的有限范围内有限地访问。



之间有什么区别 openpublic有什么?

open与以前的Swift版本中的public相同,它们允许其他模块中的类使用和继承它们,即:它们可以从其他模块中继承。此外,它们还允许其他模块中的成员使用和覆盖它们。他们的模块采用相同的逻辑。

公共允许其他模块中的类使用它们,但继承它们,即:它们不能从其他模块中。而且,它们允许其他模块的成员使用它们,但不能覆盖它们。对于它们的模块,它们具有相同的开放逻辑(它们允许类使用和继承它们;允许成员使用和覆盖它们)。


fileprivate和有什么不一样private有什么?

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

只能从其单个声明以及同一文件中该声明的扩展名访问private;例如:

// 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!
    }
}

因此,无需声明myMessagefileprivate即可在整个文件中访问。


17

当谈论在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中为此目的使用的类扩展。


1
如果协议不允许我们使用默认参数,那么如何创建带有仍符合协议的可选参数的公共方法?
bdurao

我不明白你的意思。以下创建带有可选参数的公共方法。似乎没有问题:gist.github.com/anonymous/17d8d2d25a78644046b6
jemmons

由于某种原因,可选参数在我的项目上无法正常运行,并且已经尝试了与GitHub示例类似的操作。由于我们无法在协议上设置默认参数,因此我陷入了困境,最终提出了一个问题。感谢您的帮助。
bdurao 2014年

我们都知道,任何东西都是可入侵的。我们只需要一些命令,这就是为什么我们需要访问修饰符
canbax

14

据我所知,没有关键字“ public”,“ private”或“ protected”。这表明一切都是公开的。

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

无论如何,这通常是一个好的设计模式。因为它可以让您更改实现类层次结构,同时保持逻辑类型系统相同。


这样做很好,因为它还可以减少耦合并可以使测试更加容易。
Scroog14年

4
如果有一种方法可以隐藏协议的实现类,那会更好,但是似乎没有。
David Moles 2014年

谁能提供这种模式的说明性示例?
bloudermilk

好吧,这个答案在以前的Swift版本中是有效的,看来它不再有效了:)请检查我的答案
Ahmad F

12

结合使用协议,闭包和嵌套/内部类,可以立即在模块模式中使用某些东西在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和mysteryMath在这里被外部使用隐藏了,尝试深入研究该对象会导致错误。

我只是阅读Swift文档的一部分,因此,如果有缺陷,请指出,希望知道。


好的,我也考虑过此解决方案,但请解释一下,为什么我不能同意h.huh.innerVal?
2014年

Swift是类型安全的,外界唯一了解h的地方就是它符合HuhThing。HuhThing不包含有关名为innerVal的属性的任何信息,因此尝试访问它是一个错误。
Dave Kapp 2014年

8
reflect(h)[0].1.value // 19
仍可

2
很高兴在那儿找到John-我不知道反射。似乎将对象变成元组-Swift中是否有关于该功能或其他元编程内容的正式文档?我浏览了iBooks上的语言指南,但没有看到。
Dave Kapp 2014年

1
@JohnEstropia我认为反射不重要。在Java(一种更成熟的语言),也访问修饰符,但他们没有阻止反射技巧无论是。
2014年

10

从Xcode 6 beta 4开始,Swift具有访问修饰符。从发行说明中:

Swift访问控制具有三个访问级别:

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

隐式默认值是internal,因此在应用程序目标中,您可以保留访问修饰符,除非您希望限制更多。在框架目标中(例如,如果要嵌入框架以在应用程序和共享或“今日”视图扩展之间共享代码),请使用public来指定要向框架的客户公开的API。


好吧,这个答案在以前的Swift版本中是有效的,看来它不再有效了:)请检查我的答案
艾哈迈德·F

6

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

  1. 打开
  2. 上市
  3. 内部
  4. 文件私有
  5. 私人的

开放访问和公共访问使实体可以在其定义模块的任何源文件中使用,也可以在导入定义模块的另一个模块的源文件中使用。指定框架的公共接口时,通常使用开放访问或公共访问。

内部访问使实体可以在其定义模块的任何源文件中使用,但不能在该模块外部的任何源文件中使用。在定义应用程序或框架的内部结构时,通常使用内部访问。

文件专用访问将实体的使用限制为自己定义的源文件。当在整个文件中使用特定功能的实现细节时,使用文件私有访问权限可以隐藏这些细节。

专用访问将实体的使用限制为包含在声明中。当仅在单个声明中使用特定功能的实现细节时,请使用私有访问权限来隐藏这些细节。

开放访问是最高(最低限制)访问级别,而私有访问是最低(最高限制)访问级别。

默认访问级别

如果您自己未指定显式访问级别,则代码中的所有实体(有一些特定的例外)都具有默认的内部访问级别。因此,在许多情况下,您无需在代码中指定显式访问级别。

关于该主题的发行说明:

声明为public的类不能再在其定义模块之外进行子类化,并且声明为public的方法不能再在其定义模块之外进行重写。为了允许类在外部被子类化或方法在外部被重写,请将它们声明为open,这是超越public的新访问级别。现在,导入的Objective-C类和方法都以开放方式而不是公共方式导入。使用@testable导入来导入模块的单元测试仍将被允许子类化public或内部类,以及重写public或内部方法。(SE-0117)

详细信息和详细信息: Swift编程语言(访问控制)


好吧,这个答案在以前的Swift版本中是有效的,看来它不再有效了:)请检查我的答案
艾哈迈德·F

4

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

  • 上市
  • 内部
  • 私人的

这三个适用于类,协议,功能和属性。

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

有关更多信息,请检查访问控制


应该有一个受保护的修饰符,可以轻松创建具有更高安全性的类。
Kumar C

好吧,这个答案在以前的Swift版本中是有效的,看来它不再有效了:)请检查我的答案
Ahmad F

2

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

来自Xcode 6 beta 4 realese注释

Swift访问控制具有三个访问级别:

  • private 实体只能从定义它们的源文件中访问。
  • internal 可以在定义对象的目标中的任何位置访问实体。
  • public 可以从目标内的任何位置以及导入当前目标模块的任何其他上下文中访问实体。

默认情况下,源文件中的大多数实体都具有内部访问权限。这允许应用程序开发人员在很大程度上忽略访问控制,同时允许框架开发人员完全控制框架的API。


您可以发布一个链接吗?
雪人2014年

好吧,这个答案在以前的Swift版本中是有效的,看来它不再有效了:)请检查我的答案
Ahmad F

2

Xcode 6中引入的访问控制机制:

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

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

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

缺省将其内部访问,并且不需要指定。还要注意的是,私人符确实没有工作的一流水平,但在源文件级别。这意味着要使某个类的各个部分真正私有,您需要将其分成一个单独的文件。这也介绍了一些有关单元测试的有趣案例。

在上面的链接中对我说的另一点是,您不能“升级”访问级别。如果您将某些子类化,则可以对其进行更多限制,但不能反过来。

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


好吧,这个答案在以前的Swift版本中是有效的,看来它不再有效了:)请检查我的答案
Ahmad F

2

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

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

有趣:

不必将每个方法或成员都标记为“私有”,而是可以在类/结构的扩展中介绍某些方法(例如,典型的辅助函数),并将整个扩展标记为“私有”。

class foo { }

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

为了获得更好的可维护代码,这可能是一个好主意。您只需更改一个词就可以轻松地将其切换为非私有(例如,用于单元测试)。

苹果文档


好吧,这个答案在以前的Swift版本中是有效的,看来它不再有效了:)请检查我的答案
Ahmad F

2

对于Swift 1-3:

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

一切都是公开的。

自Swift 4起更新,有可能在此线程中看到其他答案


1
该注释对于当前种子是正确的。
杰斯珀2014年

2
对于当前的种子。它会在将来出现
Jesper 2014年

1
“ public” /“ protected” /“ private”目前尚不存在,但是您可以使用闭包,协议和内部类隐藏事物-这使其有点像JavaScript中常用的模块模式。请在此处的答复中查看我的示例代码,以获取有关如何执行此操作的示例。如果我对它的工作方式有误,并且我的示例不正确,请指出这一点,因为我也在学习。:)
Dave Kapp 2014年

看来它不再有效:)请检查我的回答
Ahmad F

1

您可以使用的选项之一是将实例创建包装到函数中,并在构造函数中提供适当的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

0

语言的语法没有关键字“公众”,“私人”或“保护”。这表明一切都是公开的。当然,可以有一些其他方法来指定没有这些关键字的访问修饰符,但是我在语言参考中找不到它。


0

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

根据其他答案,swift现在提供了“专用”修饰符-它是按文件而不是按类定义的,例如Java或C#中的那些。这意味着,如果要使用受保护的方法,则可以使用快速私有方法(如果它们在同一个文件中)

  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
    }

}



-2

在swift 2.0之前,只有三个访问级别[公共,内部,私有],但是在swift 3.0中,苹果添加了两个新的访问级别,分别是[Open,fileType],因此在swift 3.0中现在有5个访问级别在这里我想清除角色这两个访问级别中的第一个。1. Open:这与Public非常相似,但是唯一的区别是Public可以访问子类并重写,而Open访问级别则不能访问 该图像取自Medium网站,并且这描述了区别在开放和公共访问之间

现在进入第二个新的访问级别2。filetype是private的更大版本,或者比内部访问级别小。fileType可以访问[class,struct,enum]的扩展部分,而private无法访问代码的扩展部分,它只能访问该图像的词法范围 是从Medium网站获取的,它描述了fileType和Private访问级别之间的区别

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.