内容简介:作者:Olivier Halligon,译者:在 Swift 中,协议中声明的属性没有访问控制的能力。如果协议中列出了某个属性,则必须使遵守协议的类型显式声明这些属性。
作者:Olivier Halligon, 原文链接 ,原文日期:2018-09-02
译者: 灰s ;校对: numbbbbb , 小铁匠Linus ;定稿: Forelax
在 Swift 中,协议中声明的属性没有访问控制的能力。如果协议中列出了某个属性,则必须使遵守协议的类型显式声明这些属性。
不过有些时候,尽管你会在协议中声明一些属性,但你是要利用这些属性来提供你的实现,并不希望这些属性在类型的外部被使用。让我们看看如何解决这个问题。
一个简单的例子
假设你需要创建一个专门的对象来管理你的视图控制器(ViewControllers)导航,比如一个协调器(Coordinator)。
每个协调器都有一个根控制器 UINavigationController
,并共享一些通用的功能,比如在它上面推进(push)和弹出(pop)其他 ViewController。所以最初它看起来可能是这样:
// Coordinator.swift protocol Coordinator { var navigationController: UINavigationController { get } var childCoordinator: Coordinator? { get set } func push(viewController: UIViewController, animated: Bool) func present(childViewController: UIViewController, animated: Bool) func pop(animated: Bool) } extension Coordinator { func push(viewController: UIViewController, animated: Bool = true) { self.navigationController.pushViewController(viewController, animated: animated) } func present(childCoordinator: Coordinator, animated: Bool) { self.navigationController.present(childCoordinator.navigationController, animated: animated) { [weak self] in self?.childCoordinator = childCoordinator } } func pop(animated: Bool = true) { if let childCoordinator = self.childCoordinator { self.dismissViewController(animated: animated) { [weak self] in self?.childCoordinator = nil } } else { self.navigationController.popViewController(animated: animated) } } }
当我们想要声明一个新的 Coordinator
对象时,会像这样做:
// MainCoordinator.swift class MainCoordinator: Coordinator { let navigationController: UINavigationController = UINavigationController() var childCoordinator: Coordinator? func showTutorialPage1() { let vc = makeTutorialPage(1, coordinator: self) self.push(viewController: vc) } func showTutorialPage2() { let vc = makeTutorialPage(2, coordinator: self) self.push(viewController: vc) } private func makeTutorialPage(_ num: Int, coordinator: Coordinator) -> UIViewController { … } }
问题:泄漏实现细节
这个解决方案在 protocol
的可见性上有两个问题:
-
每当我们想要声明一个新的
Coordinator
对象,都必须显式的声明一个let navigationController: UINavigationController
属性和一个var childCoordinator: Coordinator?
属性。 虽然,在遵守协议的类型现实中,我们并没有显式的使用他们 - 但它们就在那里,因为我们需要它们作为默认的实现来供protocol Coordinator
正常工作。 -
我们必须声明的这两个属性具有与
MainCoordinator
相同的可见性(在本例中为隐式internal(内部)
访问控制级别),因为这是protocol Coordinator
的必备条件。这使得它们对外部可见,就像在编码时可以使用MainCoordinator
。
所以问题是我们每次都要声明一些属性——即使它只是一些实现细节,而且这些实现细节会通过外部接口被泄漏,从而允许类的访问者做一些本不应该被允许的事,例如:
let mainCoord = MainCoordinator() // 访问者不应该被允许直接访问 navigationController ,但是他们可以 mainCoord.navigationController.dismissViewController(animated: true) // 他们也不应该被允许做这样的事情 mainCoord.childCoordinator = mainCoord
也许你会认为,既然我们不希望它们是可见的,那么可以直接在第一段代码的 protocol
中不声明这两个属性。但是如果我们这样做,将无法通过 extension Coordinator
来提供默认的实现,因为默认的实现需要这两个属性存在以便它们的代码被编译。
你可能希望 Swift 允许在协议中申明这些属性为 fileprivate
,但是在 Swift 4 中,你不能在 protocols
中使用任何访问控制的关键字。
所以我们如何才能解决这个“既要提供用到这个属性的默认实现,有不让这些属性对外暴露”的问题呢?
一个解决方案
实现这一点的一个技巧是将这些属性隐藏在中间对象中,并在该对象中将对应的属性声明为 fileprivate
。
通过这种方式,尽管我们依旧在对应类型的公共接口中声明了属性,但是接口的访问者却不能访问该对象的内部属性。而我们对于协议的默认实现却能够访问它们 —— 只要它们在同一个文件中被声明就行了(因为它们是 fileprivate
)。
看起来就像这样:
// Coordinator.swift class CoordinatorComponents { fileprivate let navigationController: UINavigationController = UINavigationController() fileprivate var childCoordinator: Coordinator? = nil } protocol Coordinator: AnyObject { var coordinatorComponents: CoordinatorComponents { get } func push(viewController: UIViewController, animated: Bool) func present(childCoordinator: Coordinator, animated: Bool) func pop(animated: Bool) } extension Coordinator { func push(viewController: UIViewController, animated: Bool = true) { self.coordinatorComponents.navigationController.pushViewController(viewController, animated: animated) } func present(childCoordinator: Coordinator, animated: Bool = true) { let childVC = childCoordinator.coordinatorComponents.navigationController self.coordinatorComponents.navigationController.present(childVC, animated: animated) { [weak self] in self?.coordinatorComponents.childCoordinator = childCoordinator // retain the child strongly } } func pop(animated: Bool = true) { let privateAPI = self.coordinatorComponents if privateAPI.childCoordinator != nil { privateAPI.navigationController.dismiss(animated: animated) { [weak privateAPI] in privateAPI?.childCoordinator = nil } } else { privateAPI.navigationController.popViewController(animated: animated) } } }
现在,遵守协议的 MainCoordinator
类型:
-
仅需要声明一个
let coordinatorComponents = CoordinatorComponents()
属性,并不用知道CoordinatorComponents
类型的内部有些什么(隐藏了实现细节)。 -
在
MainCoordinator.swift
文件中,不能访问coordinatorComponents
的任何属性,因为它们被声明为fileprivate
。
public class MainCoordinator: Coordinator { let coordinatorComponents = CoordinatorComponents() func showTutorialPage1() { let vc = makeTutorialPage(1, coordinator: self) self.push(viewController: vc) } func showTutorialPage2() { let vc = makeTutorialPage(2, coordinator: self) self.push(viewController: vc) } private func makeTutorialPage(_ num: Int, coordinator: Coordinator) -> UIViewController { … } }
当然,你仍然需要在遵守协议的类型中声明 let coordinatorComponents
来提供存储,这个声明必须是可见的(不能是 private
),因为这是遵守 protocol Coordinator
所要求的一部分。但是:
- 只需要声明 1 个属性,取代之前的 2 个(在更复杂的情况下会有更多)。
- 更重要的是,即使它可以从遵守协议的类型的实现中访问,也可以从外部接口访问,你却不能对它做任何事情。
当然,你仍然可以访问 myMainCoordinator.coordinatorComponents
,但是不能使用它做任何事情,因为它所有的属性都是 fileprivate
!
结论
Swift 可能无法提供你想要的所有功能。你可能希望有朝一日 protocols
允许对它声明需要的属性和方法使用访问控制关键字,或者通过某种方式将它们在公共 API 中隐藏。
但与此同时,掌握这些技巧和变通方法可以使你的公共 API 更好、更安全,避免泄露实现细节或者访问在实现之外不应该被修改的属性,同时仍然使用 Mixin pattern 并提供默认实现。
.这是一个简化的例子;不要将注意力集中在 Coordinator 的实现 - 它不是这个例子的重点,更应该关注的是需要在协议中声明公开可访问的属性。
本文由 SwiftGG 翻译组翻译,已经获得作者翻译授权,最新文章请访问http://swift.gg。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 私有属性的实现
- JavaScript 新语法详解:Class 的私有属性与私有方法
- 外部调用类的私有属性
- [译] ECMAScript 类 —— 定义私有属性
- JavaScript Class(类) 中的 Private(私有) 和 Public(公有) 属性
- TC39 在 GitHub 通过一条 EMCAScript 私有属性的草案
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Paradigms of Artificial Intelligence Programming
Peter Norvig / Morgan Kaufmann / 1991-10-01 / USD 77.95
Paradigms of AI Programming is the first text to teach advanced Common Lisp techniques in the context of building major AI systems. By reconstructing authentic, complex AI programs using state-of-the-......一起来看看 《Paradigms of Artificial Intelligence Programming》 这本书的介绍吧!