如何在Swift中声明一个“protected”变量

48
我想创建一个继承自另一个类的类,而该类在不同的文件中。
例如:
Class1.swift
class Class1
{
    protected var 
    //Do Stuff
}

Class2.swift

class Class2:Class1
{
    //Do stuff
}
我如何能够访问Swift中的'protected'变量/函数? 当我声明一个私有变量/函数时,我只能在那个类中使用它。如果我使用'fileprivate',我的另一个类必须在与Class1相同的文件中。我想做的是将我的类保留在不同的文件中,并使用Xcode内部的Groups来知道哪个类属于哪个类别。

可能是Swift有访问修饰符吗?的重复问题。 - Sulthan
@Sulthan 我看到那篇帖子,因为它没有提到“protected”。这可能是在 Swift 的后续版本中添加的内容,或者以不同的方式进行访问。 - iProgram
在Xcode中的一个组与编译或语言本身无关。它只是一种逻辑上将项目中的文件分组的方式,就像文件系统中的文件夹一样。如果您想要分离实现,您将需要使用模块,也就是项目中的目标。然后使用internal - Sulthan
我链接的帖子已经更新为Swift 3。 - Sulthan
3
我知道这些组。我喜欢将类放在不同的文件中,这样我就可以轻松地看到每个类等在文件结构中的位置。 - iProgram
5个回答

71
为此,您需要使用internal,因为Swift没有提供protected关键字(与许多其他编程语言不同)。internalfileprivatepublic之间的唯一访问修饰符:
引用块: 内部访问允许实体在其定义模块中的任何源文件中使用,但不能在该模块之外的任何源文件中使用。通常在定义应用程序或框架的内部结构时使用内部访问。
有一篇博客文章更详细地解释了为什么语言设计者选择不提供protected关键字(或任何等效物)。
其中一些原因是:
引用块: 它实际上并没有提供任何真正的保护,因为子类始终可以通过新的公共方法或属性公开“受保护”API。
同时,protected会在扩展方面造成问题,因为不清楚扩展是否也应该访问protected属性。

谢谢你的回答。我知道有内部的,但是由于所有代码都在一个单元中,这意味着我可以将其设置为公共的。 - iProgram
@iProgram 但这就是问题所在。你把所有的代码放在一个模块里了。 - Sulthan
认为这就是应用程序的本质。因此,在制作应用程序时,所有内容都应该以不同的单位表示?如果是这样,最好的方法是什么?还是需要另一个主题来讨论?虽然我不是开发新手,但我的意思是,我还有很多要学习的。 - iProgram
有许多设计模式,但最常见的是Model-View-Controller - Alex S
">它实际上并没有提供任何真正的保护,因为子类总是可以通过新的公共方法或属性来暴露“protected”API。同样,internal关键字也适用于这一点,但它们允许将internal作为包中类的默认修饰符。此外,我认为protected并不仅仅是为了“保护”,而是为了外部可见接口的清晰性,有些方法只是无用的,如果消费者不理解类的内部工作原理,暴露这些方法只会使其困惑。但嘿,这是苹果,他们有自己的观点也不让我感到意外。" - Norman Xu

7
即使Swift没有提供protected访问控制,我们仍然可以通过使用fileprivate访问控制来实现类似的访问控制。
我们需要记住的唯一一件事是,我们需要在与父类声明相同的文件中声明所有子类。 Animal.swift
import Foundation

class Animal {
    fileprivate var protectedVar: Int = 0
}

class Dog: Animal {
    func doSomething() {
        protectedVar = 1
    }
}

OtherFile.swift

let dog = Dog()
dog.doSomething()

25
问题在于超类和子类必须位于同一个文件中,这将成为一个代码设计问题。 - DEADBEEF
1
虽然“继承”是一条“轴”,但设计师们决定不使用它,因此没有更好的答案。唯一实用的解决方案是使用文件分离实例变量,并智能地使用文件来分离那个被D4ttatraya忽略的访问控制“轴”。 - Aaron

5

请使用下划线来命名受保护的变量 :/

内容就是这么简单...

class RawDisplayThing {
    
    var _fraction: CGFloat = 0 {
        didSet { ... }
    }


class FlightMap: RawDisplayThing() {

    var currentPosition() {
        ...
        _fraction = 
        ...
    }
}

每个人都必须“统一认可”,只有概念类可以使用"_"变量和函数。
至少,这样就相对容易地在每个"_"项目上进行全局搜索,并确保它只被超类使用。
或者,给这些概念添加前缀,比如说"local", "display",或可能是类的名称(或任何对你的团队有意义的名称)。
class RawDisplayThing {
    
    var rawDisplayFraction: ...
    var rawDisplayTrigger: ...
    var rawDisplayConvolution: ...

很抱歉,我的中文水平是母语级别的,但我可以尽力回答您的英文问题。

1
不明白为什么这种语言还没有提供如此基本的功能。作为一名来自Java/Android的开发者,我必须花费数小时来寻找DIY解决方案,以实现自己的基本C++功能,例如受保护或抽象类,而许多其他语言已经设置好了(PHP、C#、Java、Kotlin等)。 - Jerem Lachkar
1
(FTR此好文章https://medium.com/ios-os-x-development/swift-needs-protected-access-level-b966001a1d08链接到Swift Guys的写作中确实证明了这个问题的确切位置。对我来说,这一切都很糟糕,给我汇编 :)) - Fattie
1
哈哈,我实际上发现Java在编译时执行规则非常好,这来自于UML业务模型。很多语言都有类似C++的特性。事实上,Swift没有强制我花时间重新审视我的UML业务逻辑,这让我感到相当沮丧... - Jerem Lachkar
为了完整的好苹果/坏苹果体验,请尝试使用CoreData!哦,我的天... - Fattie

2

如果您不想让两个类属于同一个文件,可以这样做:

class Parent {
    internal private(set) var myProtectedBoolean = false
}

然后在子类中:
class Child: Parent {
   private var _myProtectedBoolean: Bool = false;

   override var myProtectedBoolean: Bool {
       get { return _myProtectedBoolean }
       set { _myProtectedBoolean = newValue }
   }
}

0
在我看来,下一个最好的选择是将两个类放在同一个文件中,并使用fileprivate
class Class1 {
   fileprivate var foo: String
}

final class Class2: Class1 {
   func bar() {
      // Class1.foo is accessible from Class1 but nothing else
      self.foo = "bar" 
   }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接