当前位置: 首页 > 知识库问答 >
问题:

在协议中使用通用的snft枚举

贡英华
2023-03-14

我经常挠头的情况下,让我们说我有一个通用的管理器类在一个pod中,可以处理权限,在应用程序中,我希望能够扩展它来创建更有意义的方法名称,也就是使用枚举作为参数,使其使用更清晰,更不容易出错。

但在别处创建扩展时,似乎不能调用private方法。

我相信Generic/AssociatedValue会有更干净的方法,或者我的模式是错误的...

以下是它的简化版本:

外部pod中的类:

public class FeatureDataManager {

    public static let shared = FeatureDataManager()

    private var permissionManager: PermissionManager!

    private init() {
        self.permissionManager = PermissionManager()
    }

    private getPermission(forFeature feature: String) -> Bool {
        return self.permissionManager.isEnable(feature)
    }
}

以及应用程序中的扩展:

extension FeatureDataManager {
    enum FeatureType: String {
        case ads = "ads"
        case showBanner = "banner"
        case showFullScreenPub = "showFullScreenPub"
    }

    public func isPermissionEnable(forFeature feature: FeatureType) {
        // Does not compile, visibility issue
        self.getPermission(forFeature: feature.rawValue)
    }
}

澄清:

FeatureDataManager是Pod中的一个类,仅用于检查在使用导入它的许多应用程序中以字符串值形式存在的权限。

我希望每一个使用它的应用程序都能定义一个扩展,该扩展将拥有自己支持的权限的有限枚举。比如说App A支持广告,而不是App B。所以我想有一个通用的方法,当你调用featureManager时。isPermissionEnable(.Ads),即每当应用程序启动时,auto complete只会提供该应用程序支持的权限列表。此外,将我的字符串权限值包装到枚举中的目标是,如果名称发生更改,只需在一个地方进行更改,就可以更容易地防止错误,并且更容易重构。

共有3个答案

谷越
2023-03-14

您必须声明fileprivate访问级别,才能在同一文件中定义的扩展名中访问它。即使扩展依赖于同一个文件,您也无法访问其定义范围之外的私有实体!

巫坚白
2023-03-14

private访问控制限制实体的使用:

私有访问将实体的使用限制为封闭声明,以及同一文件中声明的扩展。

访问控制——Swift编程语言

如果希望通过类扩展名(在另一个文件和同一个模块/包中)访问实体:使用internalaccess control。

如果希望通过类扩展名(在另一个文件和另一个模块/包中)访问实体:使用publicaccess control。

陶锋
2023-03-14

您要寻找的是一个“受保护”的级别,而这在Swift中不存在,如果不创建一个新的保护级别,就不可能存在,因为它会破坏编译器的优化。在您的示例中,由于getPermis(forFeature:)被promise永远不会在这个范围之外被调用,编译器可以自由地内联它。所以在您的扩展想要调用它的时候,这个函数甚至可能不存在。

Swift可以添加“半公开”的“受保护”级别,但Swift没有任何此类功能。您需要重新设计FeatureDataManager才能实现这一点。从您的示例来看,如何做到这一点并不明显,因为您根本没有为权限提供公共接口,所以不清楚“我希望能够扩展它以创建更有意义的方法名”是什么意思目前没有公共方法名。如果真的有这样一个语法,那么用你描述的更方便的语法就很容易了。

你能给出一个你希望这个扩展改进的调用代码的例子吗?

有关语言为何采用这种方式的更多信息,请参阅访问控制和保护。这不是意外。

您注意到,您可以在同一个文件中执行此操作,这是事实。Swift允许这样做是出于风格上的原因(许多人出于代码组织的原因在单个文件中使用扩展名)。Swift将同一文件中的所有扩展名视为主定义中的扩展名。但这不适用于其他文件,当然也不适用于其他模块。

对此的通用解决方案如下所示:

public class FeatureDataManager<Feature>
where Feature: RawRepresentable, Feature.RawValue == String {

    private func getPermission(forFeature feature: String) -> Bool { ... }

    public func isPermissionEnable(forFeature feature: Feature) {
        self.getPermission(forFeature: feature.rawValue)
    }   
}

然后,应用程序将创建一个功能集,并为该功能集创建一个管理器:

enum AppFeature: String {
    case ads = "ads"
    case showBanner = "banner"
    case showFullScreenPub = "showFullScreenPub"
}

let featureDataManager = FeatureDataManager<AppFeature>()
featureDataManager.isPermissionEnable(forFeature: .ads)

这确实阻止了轻松创建。共享实例。这是好是坏是有争议的,但是假设你想要它,你需要把它包起来:

class AppFeatureDataManager {
    enum Feature: String {
        case ads = "ads"
        case showBanner = "banner"
        case showFullScreenPub = "showFullScreenPub"
    }

    static var shared = AppFeatureDataManager()

    let manager = FeatureDataManager<Feature>()

    public func isPermissionEnable(forFeature feature: Feature) {
        manager.isPermissionEnable(forFeature: feature)
    }
}

现在,对于应用程序端来说,这有点太多了(特别是如果有比isPermissionEnable更多的方法),因此您可以通过这种方式删除样板文件(完整代码):

public class FeatureDataManager<Feature>
where Feature: RawRepresentable, Feature.RawValue == String {

    private var permissionManager: PermissionManager

    init() {
        self.permissionManager = PermissionManager()
    }

    private func getPermission(forFeature feature: String) -> Bool {
        self.permissionManager.isEnable(feature)
    }

    public func isPermissionEnable(forFeature feature: Feature) {
        self.getPermission(forFeature: feature.rawValue)
    }
}

protocol AppFeatureDataManager {
    associatedtype Feature: RawRepresentable where Feature.RawValue == String
    var manager: FeatureDataManager<Feature> { get }
}

// Here you can write any necessary pass-through functions so the app doesn't have to
extension AppFeatureDataManager {
    public func isPermissionEnable(forFeature feature: Feature) {
        manager.isPermissionEnable(forFeature: feature)
    }
}

//
// Application Developer writes this:
//
class MyGreatAppFeatureDataManager {
    enum Feature: String {
        case ads = "ads"
        case showBanner = "banner"
        case showFullScreenPub = "showFullScreenPub"
    }

    // This is the only thing that's really required
    let manager = FeatureDataManager<Feature>()

    // They're free make this a shared instance or not as they like.
    // That's not something the framework cares about.
    static var shared = MyGreatAppFeatureDataManager()
    private init() {}
}

综上所述,如果FeatureDataManager真的只是PermissionManager的前端,我认为这层太多了,就像你在这里描述的那样。(也许你的例子非常简单,所以下面的例子不适用。)

如果PermissionManager是公共的,而真正的目标只是拥有一个更好的前端,我会这样写:

protocol FeatureDataManager {
    associatedtype Feature: RawRepresentable where Feature.RawValue == String
    var permissionManager: PermissionManager { get }
}

extension FeatureDataManager {
    func isPermissionEnable(forFeature feature: Feature) {
        permissionManager.isEnable(feature.rawValue)
    }
}

//
// App developer writes this
//
class MyGreatAppFeatureDataManager: FeatureDataManager {
    enum Feature: String {
        case ads = "ads"
        case showBanner = "banner"
        case showFullScreenPub = "showFullScreenPub"
    }

    // This is the only required boilerplate; the protocol can't do this for you.
    let permissionManager = PermissionManager()

    // And the developer can decide to make it a shared instance if they like,
    // but it's not the business of the framework
    static let shared = MyGreatAppFeatureDataManager()
    private init() {}
}
 类似资料:
  • 由于TCP是基于流的,客户端发送的请求数据是像水流一样流入到服务端,服务端探测到有数据到来后应该检查数据是否是完整的,因为可能只是一个请求的部分数据到达服务端,甚至可能是多个请求连在一起到达服务端。如何判断请求是否全部到达或者从多个连在一起的请求中分离请求,就需要规定一套通讯协议。 在WorkerMan中为什么要制定协议? 传统PHP开发都是基于Web的,基本上都是HTTP协议,HTTP协议的解析

  • application提供了千牛移动提供的原生能力,比如打开聊天窗口(openChat)、获取地址位置(location)和打开网址(openWebsite)等,点击查看api列表和具体用法 打开“千牛欢迎页” QN.application.invoke({ api: 'openPlugin', query: { appkey: 23093073 }, settings:

  • 问题内容: 我正在将我的项目从Objective-c转换为Swift,并且正在使用一个Swift类,我正在尝试在Objective- c类中进行访问。我的问题是,在Objective-C类中不可访问。这是我的速成班: 当我查看文件时,看不到: 当我尝试在我的Objective-c类中使用时,出现编译器错误。 有人知道这个问题吗?如何在Objective-c中访问Swift协议? 问题答案: 您需要

  • 问题内容: 我想使用一种接受通用输入并返回通用值的方法来创建协议。 到目前为止,这是我尝试过的方法,但是会产生语法错误。 使用未声明的标识符T。 我究竟做错了什么? 问题答案: 协议略有不同。查看Apple文档中的“关联类型” 。 这就是您在示例中使用它的方式

  • 成熟稳定的商业软件,开源但不完全免费。个人及非盈利组织可以随意使用; 商业用途只需要支付少量的费用并可以获得我们的技术支持服务。 感谢您选择 Highcharts 系列图表软件,Highcharts 系列软件包含 Highcharts JS,Highstock JS,Highmaps JS 共三款软件,均为纯 JavaScript 编写的 HTML5 图表库,全部源码开放。 1、非商用免费授权 以

  • 由于TCP是基于流的,客户端发送的请求数据是像水流一样流入到服务端,服务端探测到有数据到来后应该检查数据是否是完整的,因为可能只是一个请求的部分数据到达服务端,甚至可能是多个请求连在一起到达服务端。如何判断请求是否全部到达或者从多个连在一起的请求中分离请求,就需要规定一套通讯协议。 在WorkerMan中为什么要制定协议? 传统PHP开发都是基于Web的,基本上都是HTTP协议,HTTP协议的解析