Swift协议方法设置可选参数默认值nil报默认参数不允许错误
Swift协议方法设置默认参数nil的实现方式
Swift 协议本身仅声明方法的必选签名,不支持直接在协议定义内给参数设置默认值,直接书写predicate: NSPredicate? = nil会抛出编译错误:Default argument not permitted in a protocol method。
原错误写法的协议代码:
protocol CoreDataRepositoryProtocol: ExpressibleByNilLiteral{ func fetchData<T:NSManagedObject>(entity: T.Type, enityName: entittytype, predicate: NSPredicate? = nil) -> [T]? func getOne <T:NSManagedObject>(entity: T.Type, enityName: String, predicate: NSPredicate) -> T? func deleteRec<T:NSManagedObject>(entity: T.Type, entityName:entittytype, predicate: NSPredicate, completion: @escaping (String) -> Void) -> Bool func saveObject() }
推荐实现:协议扩展提供重载默认实现
这是Swift中实现协议方法默认参数的标准写法,逻辑可统一复用,所有遵循协议的类型无需重复编写代码:
- 先修改协议定义,移除参数的默认值,仅保留纯方法签名,同时修正原代码中的拼写笔误(原代码中
enityName、entittytype存在拼写错误,entittytype推测为自定义的实体名枚举类型,统一命名为EntityType避免编译报错):
// 可根据实际业务调整自定义实体名类型 enum EntityType: String { case user, post, comment } protocol CoreDataRepositoryProtocol: ExpressibleByNilLiteral{ // 移除predicate的默认值,仅保留参数声明 func fetchData<T:NSManagedObject>(entity: T.Type, entityName: EntityType, predicate: NSPredicate?) -> [T]? func getOne <T:NSManagedObject>(entity: T.Type, entityName: String, predicate: NSPredicate) -> T? func deleteRec<T:NSManagedObject>(entity: T.Type, entityName: EntityType, predicate: NSPredicate, completion: @escaping (String) -> Void) -> Bool func saveObject() }
- 编写协议扩展,在扩展中定义带默认参数的重载方法,内部调用协议要求实现的核心方法,传入默认的nil值:
extension CoreDataRepositoryProtocol { // 重载方法,设置predicate默认值为nil func fetchData<T:NSManagedObject>(entity: T.Type, entityName: EntityType, predicate: NSPredicate? = nil) -> [T]? { // 转发调用协议定义的必须实现方法 fetchData(entity: entity, entityName: entityName, predicate: predicate) } }
完成后所有遵循CoreDataRepositoryProtocol的类型,无需额外实现这个重载方法,就可以直接以两种方式调用fetchData:
- 传predicate参数:
repo.fetchData(entity: User.self, entityName: .user, predicate: somePredicate) - 不传predicate参数,默认用nil:
repo.fetchData(entity: User.self, entityName: .user)
备选实现:在具体遵循类中添加默认参数
如果不需要全局复用默认参数逻辑,也可以在具体遵循协议的实现类中,直接给协议方法的参数加默认值:
class CoreDataRepository: CoreDataRepositoryProtocol { // 实现协议方法时直接给predicate加默认nil值 func fetchData<T:NSManagedObject>(entity: T.Type, entityName: EntityType, predicate: NSPredicate? = nil) -> [T]? { let request = NSFetchRequest<T>(entityName: entityName.rawValue) request.predicate = predicate return try? NSManagedObjectContext.current.fetch(request) } // 其余协议方法按要求实现即可 func getOne<T:NSManagedObject>(entity: T.Type, entityName: String, predicate: NSPredicate) -> T? { // 具体实现 return nil } func deleteRec<T:NSManagedObject>(entity: T.Type, entityName: EntityType, predicate: NSPredicate, completion: @escaping (String) -> Void) -> Bool { // 具体实现 return true } func saveObject() { // 具体实现 } }
这种方式的缺点是每个遵循协议的类型都需要单独设置默认参数,无法统一维护逻辑,适合仅单个类遵循协议的场景。
额外提示:原协议继承了
ExpressibleByNilLiteral,该协议要求实现init(nilLiteral: ())方法,如果没有特殊的nil字面量转换需求,不建议让仓储层协议继承该协议,会增加不必要的实现成本。
内容的提问来源于stack exchange,提问作者smack
相关产品推荐
相关产品推荐

