【发布时间】:2025-12-01 08:30:01
【问题描述】:
我想知道在使用协议时,当我希望一些函数是公开的,而一些函数对我来说是内部的时,最佳实践是什么。
我正在 Swift 3 中写一个 AudioManager strong> 将 AVPlayer 包装为框架。
我希望将一些方法公开,例如使用 AudioManager 的 ViewController 可以访问某些方法,但某些方法不会暴露在框架之外
-> 即具有访问修饰符 internal 而不是 public。
我在写具有协议驱动设计的框架,几乎每个部分都应该有一个协议。
因此,协议与框架内的协议进行对话。
例如主类 - AudioManager - 有一个 AudioPlayer,并且应该能够在其上调用一些 internal 函数,
例如pause(reason:) 但该方法应该是 internal 并且不能暴露在框架之外。
这是一个示例。
internal enum PauseReason {
case byUser
case routeChange
}
// Compilation error: `Public protocol cannot refine an internal protocol`
public protocol AudioPlayerProtocol: InternalAudioPlayerProtocol {
func pause() // I want
}
internal protocol InternalAudioPlayerProtocol {
func pause(reason: PauseReason) // Should only be accessible within the framework
}
public class AudioPlayer: AudioPlayerProtocol {
public func pause() {
pause(reason: .byUser)
}
// This would probably not compile because it is inside a public class...
internal func pause(reason: PauseReason) { //I want this to be internal
// save reason and to stuff with it later on
}
}
public protocol AudioManagerProtocol {
var audioPlayer: AudioPlayerProtocol { get }
}
public class AudioManager: AudioManagerProtocol {
public let audioPlayer: AudioPlayerProtocol
init() {
audioPlayer = AudioPlayer()
NotificationCenter.default.addObserver(self, selector: #selector(handleRouteChange(_:)), name: NSNotification.Name.AVAudioSessionRouteChange, object: nil)
}
func handleRouteChange(_ notification: Notification) {
guard
let userInfo = notification.userInfo,
let reasonRaw = userInfo[AVAudioSessionRouteChangeReasonKey] as? NSNumber,
let reason = AVAudioSessionRouteChangeReason(rawValue: reasonRaw.uintValue)
else { print("what could not get route change") }
switch reason {
case .oldDeviceUnavailable:
pauseBecauseOfRouteChange()
default:
break
}
}
}
private extension AudioManager {
func pauseBecauseOfRouteChange() {
audioPlayer.pause(reason: .routeChange)
}
}
// Outside of Audio framework
class PlayerViewController: UIViewController {
fileprivate let audioManager: AudioManagerProtocol
@IBAction didPressPauseButton(_ sender: UIButton) {
// I want the `user of the Audio framwwork` (in this case a ViewController)
// to only be able to `see` `pause()` and not `pause(reason:)`
audioManager.audioPlayer.pause()
}
}
我知道我可以通过将方法 pauseBecauseOfRouteChange 更改为如下所示来使其工作:
func pauseBecauseOfRouteChange() {
guard let internalPlayer = audioPlayer as? InternalAudioPlayerProtocol else { return }
internalPlayer.pause(reason: .routeChange)
}
但我想知道是否有更优雅的解决方案?
类似于标记AudioPlayerProtocol 精炼InternalAudioPlayerProtocol...
或者各位程序员是怎么做的?
如果框架不暴露供内部使用的方法和变量,框架会更漂亮!
谢谢!
【问题讨论】:
标签: ios swift swift3 access-modifiers swift-protocols