【问题标题】:Conform to Protocol and Keep Property Private遵守协议并保持财产私有
【发布时间】:2017-10-16 15:16:34
【问题描述】:

我需要使用符合类中的协议属性作为私有。但编译器拒绝这样做。我该如何实现?

protocol ProtocolX: class {
    var x: Int { get set }

    func performAnyActionOnX()
}

extension ProtocolX {
    func performAnyActionOnX() {
        x = 5
        print(x)
    }
}

class A: ProtocolX {
    private var x:Int = 7
}

谢谢。

【问题讨论】:

  • 协议可以被认为是另一种类型必须实现的契约。协议的有用性来自于其他类知道采用该协议的类遵循什么规则这一事实。将变量设置为 private 会破坏此目的。如果您真的需要将变量设为私有,您需要重新考虑。
  • 我认为确保符合条件的类具有特定属性非常合乎逻辑,但我不希望该属性出现在其类之外。
  • @SamehDos,在这种情况下,您需要添加另一层抽象,一些定义实现细节而不用作接口的协议。你可以使用类似python的__privacy。

标签: swift architecture protocols private


【解决方案1】:

由于协议的属性始终具有与您的协议相同的访问级别,您可以创建一个单独的类,您可以将fileprivate 级别应用于它们,例如:

public class Props {
    fileprivate var x: Int = 0
}

public protocol ProtocolX: class {
    var privateProps: Props { get }
}

extension ProtocolX {
    public func performAnyActionOnX() {
        privateProps.x = 5
        print(privateProps.x)
    }
}

public class A: ProtocolX {
    public let privateProps = Props()
    
    public init() {
        privateProps.x = 7
    }
}

如您所见,x 具有 fileprivate 访问级别,因此它可以在 ProtocolX 扩展和 A 类实现中访问,因此它的行为类似于 private,并且您不能更改 privateProps 类的变量 @987654327 @并在外部访问x

let a = A()
a.performAnyActionOnX()
// Prints: 5

let x = a.privateProps.x
// error: 'x' is inaccessible due to 'fileprivate' protection level

【讨论】:

    【解决方案2】:

    正如@TheAppMentor 所提到的,从 Swift 4 开始似乎没有确切的解决方案。

    不过,有两种近似解:

    1.)

    由于 Swift 中的协议默认具有internal 的访问级别,因此将变量也设为internal。为了让编译器强制执行internal,请将协议、类及其所有消费者(用户)移至单独的模块(框架)。

    /* internal */ protocol ProtocolX: class {
        var x: Any { get set }
    
        func performAnyActionOnX()
    }
    
    extension ProtocolX {
        func performAnyActionOnX() {}
    }
    
    /* internal */ class A: ProtocolX {
        internal var x: Any = 0
    }
    

    2.)

    为协议提供private 的访问级别,为变量提供fileprivate 的访问级别。为了使private 协议可以访问,请将协议、类及其所有消费者移动到同一个源文件中。

    private protocol ProtocolX: class {
        var x: Any { get set }
    
        func performAnyActionOnX()
    }
    
    extension ProtocolX {
        func performAnyActionOnX() {}
    }
    
    class A: ProtocolX {
        fileprivate var x: Any = 0
    }
    

    【讨论】:

      【解决方案3】:

      由于@TheAppMentor 的评论中所述的原因,您的问题没有确切的解决方案。但是,如果您的目的是使您的代码易于理解(而不是欺骗编译器),那么有一些解决方法可能会有所帮助。

      在 Swift 4.0 中编译。

      解决方案 1:类 Python 的 __privacy

      ​​>

      快速简单。此解决方案依赖于同意从 _ 开始的属性和函数是私有的且不应访问的用户。

      protocol ProtocolX: class {
          // Public x
          var x: Int { get }
      
          // It's private!
          var _x: Int { get set }
      
          func performAnyActionOnX()
      }
      
      extension ProtocolX {
          var x: Int { return _x }
      
          func performAnyActionOnX(){
              _x = 5
              print(x)
          }
      }
      
      class A: ProtocolX {
          var _x: Int = 7
      }
      

      解决方案 2:附加抽象层

      架构正确。您应该将协议拆分为两部分:私有和公共。

      protocol ProtocolX: class {
          var x: Int { get }
      
          func performAnyActionOnX()
      }
      
      protocol ProtocolXImplementation: class {
          var _x: Int { get set }
      }
      
      extension ProtocolXImplementation {    
          var x: Int { return _x }
      
          func performAnyActionOnX(){
              _x = 5
              print(x)
          }
      }
      
      class A: ProtocolX, ProtocolXImplementation {
          var _x: Int = 7
      }
      
      // ... somewhere later ...
      // Hide the implementation when use `A`:
      let item: ProtocolX = A()
      

      【讨论】:

      • 这两个建议都不能正常工作,因为在这两种情况下A()._x = 911 都会编译。您不能指望消费者从不直接使用_xProtocolXImplementation 来进行变异。
      • 我在我的项目中使用这个解决方案。非常有帮助。而且没有更好的解决方案。
      • 好吧,我同意我的回答很不清楚,只有有经验的用户才能理解。我已经添加了一些解释。
      • 对于解决方案 2,在使用 A 时如何隐藏实现? _x 在 A 实现中不是私有的。
      • @DeclanMcKenna,如果您仅使用公共协议 (ProtocolX) 而不考虑特定实现,则可以使用此解决方案。例如,如果您构建了一个数组[ProtocolX],稍后将在某处访问它。此外,协议实现不能在 Swift 4 中隐藏,它应该是公开的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-03
      • 1970-01-01
      • 2022-08-21
      • 2022-12-18
      • 2012-06-22
      相关资源
      最近更新 更多