【问题标题】:Does Swift have access modifiers?Swift 有访问修饰符吗?
【发布时间】:2014-07-23 02:59:50
【问题描述】:

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

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

我没有在 Swift 参考中找到任何关于访问修饰符的内容。是否可以限制 Swift 中数据的可见性?

【问题讨论】:

标签: access-modifiers swift


【解决方案1】:

对于 Swift 1-3:

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

一切都是公开的。

更新 自 Swift 4 以来,可能会在此线程中看到其他答案

【讨论】:

  • 这条评论对于当前种子来说是准确的。
  • 对于当前种子。 It will appear in the future.
  • "public" / "protected" / "private" 当前不存在,但您可以使用闭包、协议和内部类来隐藏内容——这使它有点像 JavaScript 中常用的模块模式.请在此处查看我的回复中的示例代码,了解如何执行此操作的示例。如果我对它的工作原理有误,并且我的示例不正确,请指出,因为我还在学习。 :)
  • 它似乎不再有效:​​) 请检查my answer
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    据我所知,没有关键字“公共”、“私人”或“受保护”。这表明一切都是公开的。

    然而,Apple 可能希望人们使用“protocols”(世界其他地方称为接口)和factory design pattern 来隐藏实现类型的详细信息。

    这通常是一个很好的设计模式。因为它允许您更改 实现 类层次结构,同时保持 逻辑 类型系统相同。

    【讨论】:

    • 这很好,因为它还减少了耦合并且可以使测试更容易。
    • 如果有办法隐藏协议的实现类会更好,但似乎没有。
    • 谁能提供这种模式的说明性示例?
    • 嗯,这个答案在以前的 Swift 版本中是有效的,看来它不再有效了:) 请检查my answer
    【解决方案4】:

    As of Swift 3.0.1, there are 4 levels of access,如下所述,从最高(限制最少)到最低(限制最多)。


    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
    

    【讨论】:

    • 有人可以向我解释一下为什么这没什么大不了的吗?
    • OOP 中总是有一些方法或变量应该是私有的或受保护的。这允许实现SOLID design,因为大方法被分成许多较小的方法,每个都有自己的职责,可以被覆盖,但只有“main”方法应该可供公共使用。
    • 我个人不喜欢使用下划线/特殊字符引导“私有”方法的解决方案。即使保证我自己将是唯一查看此代码的人,它也使代码更节省/更不容易出错,因为编译器只会阻止你做你不应该做的事情。所以我认为他们应该尽快摆脱“访问控制机制”,这样人们就不会习惯坏习惯。
    • Xcode 6 beta 发行说明说:“访问控制(公共/私人成员)在这个种子中没有启用。(15747445)”
    • @alcalde 公共接口的想法非常有价值。如果您希望类中的所有代码都必须驻留在作为公共 API 一部分的函数中,我认为这是非常有限的。另一方面,拥有指定的公共 API 允许实现更改(包括使用私有方法)而不会干扰消费者。如果有人“需要”使用内部类方法,我觉得他们误解了类功能的限制(或试图使用有问题的类)。
    【解决方案5】:

    结合使用协议、闭包和嵌套/内部类,现在可以使用类似于模块模式的东西在 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 和神秘数学被隐藏在这里,不让外部使用,尝试挖掘对象应该会导致错误。

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

    【讨论】:

    • 好的,我也想过这个解决方案,但是请解释一下,为什么我无法使用 h.huh.innerVal?
    • Swift 是类型安全的,外界对 h 的唯一了解就是它符合 HuhThing。 HuhThing 不包含有关名为 innerVal 的属性的任何信息,因此尝试访问它是错误的。
    • 仍然可以访问:P reflect(h)[0].1.value // 19
    • 很高兴找到约翰 - 我不知道反射。似乎将对象变成了元组——在 Swift 中是否有关于该函数或其他元编程内容的官方文档?我浏览了 iBooks 上的语言指南,但没有看到。
    • @JohnEstropia 我不认为反射很重要。在Java(一种更成熟的语言)中,有个访问修饰符,但它们也不能阻止反射技巧。
    【解决方案6】:

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

    【讨论】:

      【解决方案7】:

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

      【讨论】:

      • 如果协议不允许我们有默认参数,我如何创建一个仍然符合协议的可选参数的公共方法?
      • 我不明白你的意思。下面创建一个带有可选参数的公共方法。似乎没有问题:gist.github.com/anonymous/17d8d2d25a78644046b6
      • 由于某种原因,可选参数在我的项目中无法正常工作,已经尝试过类似于您的 GitHub 示例的方法。由于我们无法在协议上设置默认参数,所以我被卡住了,最后问了一个问题。感谢您提供帮助。
      • 我们都知道任何东西都是可以破解的。我们只需要一些命令为什么我们需要访问修饰符
      【解决方案8】:

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

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

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

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

      【讨论】:

      • 嗯,这个答案在以前的 Swift 版本中是有效的,看来它不再有效了:) 请检查my answer
      【解决方案9】:

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

      来自Xcode 6 beta 4 realese notes

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

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

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

      【讨论】:

      • 你能发个链接吗?
      • 嗯,这个答案在以前的 Swift 版本中是有效的,看来它不再有效了:) 请检查my answer
      【解决方案10】:

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

      根据其他答案,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
          }
      

      }

      【讨论】:

        【解决方案11】:

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

        • 公开
        • 内部
        • 私人

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

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

        更多信息,请查看Access Control

        【讨论】:

        • 应该有一个受保护的修饰符,它可以简化创建具有更高安全性的类。
        • 嗯,这个答案在以前的 Swift 版本中是有效的,看来它不再有效了:) 请检查my answer
        【解决方案12】:

        访问控制机制为introduced in Xcode 6:

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

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

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

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

        在上面的链接中对我提出的另一点是,您无法“升级”访问级别。如果您对某事物进行子类化,则可以对其进行更多限制,但反之则不行。

        最后一点也会影响函数、元组和其他东西,如果一个函数使用 private 类,那么拥有函数 internal是无效的> 或 public,因为他们可能无法访问 private 类。这会导致编译器警告,您需要将该函数重新声明为 private 函数。

        【讨论】:

        • 嗯,这个答案在以前的 Swift 版本中是有效的,看来它不再有效了:) 请检查my answer
        【解决方案13】:
        【解决方案14】:

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

        1. 打开
        2. 公开
        3. 内部
        4. 文件私有
        5. 私人

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

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

        文件私有访问将实体的使用限制在其自己的定义源文件中。使用文件私有访问隐藏 特定功能的实现细节,当那些 详细信息在整个文件中使用。

        私有访问将实体的使用限制在封闭声明中。使用私有访问隐藏实现细节 仅使用这些细节时的特定功能 在一个声明中。

        开放访问是最高(限制最少)访问级别,私人访问是最低(限制最严格)访问级别。 p>

        默认访问级别

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

        关于该主题的发行说明:

        声明为公共的类不能再在外部子类化 它们的定义模块和声明为 public 的方法不能再 在其定义模块之外被覆盖。允许一个类 外部子类化或要外部覆盖的方法,声明 它们是开放的,这是一个超越公共的新访问级别。进口的 Objective-C 类和方法现在都导入为 open 而不是 比公开的。使用 @testable 导入来导入模块的单元测试 仍然允许子类化公共或内部类 作为覆盖公共或内部方法。 (SE-0117)

        更多信息和细节: The Swift Programming Language (Access Control)

        【讨论】:

        • 嗯,这个答案在以前的 Swift 版本中是有效的,看来它不再有效了:) 请检查my answer
        【解决方案15】:

        Swift 3 和 4 也为变量和方法的访问级别带来了很多变化。 Swift 3 和 4 现在有 4 个不同的访问级别,其中 open/public 访问是最高(限制最少)访问级别,private 访问是最低(最严格)的访问级别:

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

        有趣:

        您可以在类/结构的扩展中覆盖一些方法(例如,通常是辅助函数)并将整个扩展标记为“私有”,而不是将每个方法或成员标记为“私有”。

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

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

        Apple documentation

        【讨论】:

        • 嗯,这个答案在以前的 Swift 版本中是有效的,看来它不再有效了:) 请检查my answer
        【解决方案16】:

        斯威夫特 4 / 斯威夫特 5

        正如Swift Documentation - Access Control 中提到的,Swift 有5 个访问控制

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

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

        • fileprivateprivate:只能在有限范围内访问定义它们。



        openpublic有什么区别?

        open 与 Swift 早期版本中的 public 相同,它们允许来自其他模块的类使用和继承它们,即:它们可以从其他模块继承。此外,它们允许来自其他模块的成员使用和覆盖它们。他们的模块也是同样的逻辑。

        public 允许来自其他模块的类使用它们,但不允许继承它们,即:它们不能从其他模块继承。此外,它们允许其他模块的成员使用它们,但不能覆盖它们。对于它们的模块,它们具有相同的 open 逻辑(它们允许类使用和继承它们;它们允许成员使用和覆盖它们)。


        fileprivateprivate有什么区别?

        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 proposal 中提到的,Swift 4 中添加的唯一改进是 private 访问控制范围已扩展为可以从 extensions 访问该声明在同一个文件中;例如:

        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 即可在整个文件中访问。

        【讨论】:

          【解决方案17】:

          在 swift 2.0 之前只有三个访问级别 [公共、内部、私有] 但是在 swift 3.0 中,苹果添加了两个新的访问级别,即 [ Open, fileType ] 所以 现在在 swift 3.0 中有 5 个访问级别 这里我要明确这两个访问级别的作用 1. Open:这与 Public 非常相似,但唯一的区别是 Public 可以访问子类和覆盖,而Open访问级别不能访问那个this image is taken from Medium website and this describe the difference between open and public access

          现在进入第二个新的访问级别 2. filetype 是 private 的更大版本或访问级别低于 internal fileType 可以访问 [class, struct, enum] 的扩展部分 而私有不能访问代码的扩展部分它只能访问 词汇范围 this image is taken from Medium website and this describe the difference between fileType and Private access level

          【讨论】:

            【解决方案18】:

            内部访问使实体可以在其定义模块的任何源文件中使用,但不能在该模块之外的任何源文件中使用。 例如 - 考虑具有内部类和内部方法的框架或模块 Alpha,如果框架 Beta 导入 Alpha,则 Alpha 的内部类和方法将不适用于框架 Beta

            文件私有访问将实体的使用限制在其自己的定义源文件中。当在整个文件中使用这些细节时,使用文件私有访问来隐藏特定功能的实现细节。 例如 - 考虑具有文件私有成员的 Alpha 类,具有 Alpha 对象的 Beta 类将无法访问文件私有方法,因为它受到文件限制。

            私有访问将实体的使用限制为封闭声明,以及同一文件中该声明的扩展。当这些细节仅在单个声明中使用时,使用私有访问来隐藏特定功能的实现细节。 例如 - 考虑 Alpha 类及其扩展名在同一个文件中,两者都具有私有和文件私有方法,它们可以访问彼此的方法。在同一个文件中定义的 Beta 类不能访问两个私有方法,但可以访问 Alpha 类的文件私有方法。

            公共类和类成员只能在定义模块(目标)内进行子类化和覆盖。

            开放类和类成员可以在定义模块(目标)内部和外部进行子类化和覆盖。

            例如 - 考虑具有公共类、公共方法、开放类和开放方法的框架或模块 Alpha,框架 Beta 导入 Alpha,类和方法都可用于框架 B,但只有开放类是可子类化的,并且可以覆盖开放方法.

            【讨论】:

            猜你喜欢
            • 2021-12-17
            • 1970-01-01
            • 2014-11-03
            • 1970-01-01
            • 2017-12-22
            • 2015-11-26
            • 2017-08-11
            • 2017-08-18
            相关资源
            最近更新 更多