【问题标题】:Can you restrict a protocol to a generic type regardless of the type constraint?无论类型约束如何,您可以将协议限制为泛型类型吗?
【发布时间】:2021-05-05 18:27:06
【问题描述】:

TL:DR

我怎样才能进行编译,以便协议可以应用于任何 BaseObject 的具体实现,特别是,以便它可以访问someFuncUnrelatedToTypeConstraint(这才是真正的目标) ?

@interface BaseObject <__covariant TypeConstraint> : NSObject

- (TestObject*)someFuncUnrelatedToTypeConstraint;

@end
protocol ProtocolOnlyApplicableToBaseObject : BaseObject {}

注意:这不会编译,因为它抱怨我需要为 BaseObject 指定类型参数,但仅此而已......我希望这适用于任何/所有 BaseObject&lt;T&gt; 类型,无论 @987654326 是什么@是。


现在是完整的代码...

在我们的 ObjC 代码库中,我们有一个具体的对象和一个像这样定义的泛型(示例过于简单)。这表示只有 NSObject 或其子类之一可以满足类型要求。

@interface TestObject : NSObject

@end


@interface BaseObject <__covariant TypeConstraint> : NSObject

- (TestObject*)someFuncUnrelatedToTypeConstraint;

@end

我们也有这些(同样,过于简单化的)ObjC 子类,最后一个省略了类型,所以它默认为 NSObject 隐式。

@interface ConcreteObjectA : BaseObject<UIView>

@end

@interface ConcreteObjectB : BaseObject<UIWindow>

@end

@interface ConcreteObjectC : BaseObject

@end

现在我们正在尝试编写一个 Swift 协议和一个只能应用于 BaseObject 实例的配对扩展,因为扩展需要调用函数 someFuncUnrelatedToTypeConstraint()

protocol ProtocolForBaseObject : BaseObject {
  associatedtype ReturnType:SomeTypeAcceptingTestObject
}

extension ProtocolForBaseObject {

    func someTest() -> ReturnType {
        let testObject = self.someFuncUnrelatedToTypeConstraint()
        return ReturnType(testObject)
    }
} 

目标是我们可以在 Swift 中调用它...

extension ConcreteObjectA : ProtocolForBaseObject {
  typealias ReturnType = ReturnTypeA
}
let objA = ConcreteObjectA()
let returnTypeA = objA.someTest()

extension ConcreteObjectB : ProtocolForBaseObject {
  typealias ReturnType = ReturnTypeB
}
let objB = ConcreteObjectB()
let returnTypeB = objB.someTest()

extension ConcreteObjectC : ProtocolForBaseObject {
  typealias ReturnType = ReturnTypeC
}
let objC = ConcreteObjectC()
let returnTypeC = objC.someTest()

问题是 ProtocolForBaseObject 无法编译,因为它说我必须指定泛型类型约束...

Reference to generic type 'BaseObject' requires arguments in <...>

但问题是我不在乎泛型的类型约束是什么。我希望这适用于泛型的 所有 实例,因为我试图访问的函数无论如何都不使用该类型,因此协议没有理由关心该类型是什么。

为了解决这个问题,我尝试添加NSObject 作为类型约束,但这似乎也不起作用,因为BaseObject&lt;NSObject&gt; != BaseObject&lt;UIView&gt; 即使UIViewNSObject 的子类。

protocol ProtocolForBaseObject : BaseObject<NSObject> {}

那么,无论类型约束如何,如何构建协议以应用于BaseObject 泛型的所有具体实例?

【问题讨论】:

  • 嗯有点困惑。为什么不直接将协议应用于 BaseObject 而不是继承 BaseObject 类型的协议,以便稍后再次应用于其自身。 BaseObject 已经继承自 NSObject,所以 BaseObject 没有意义。
  • 问题是我只希望该协议对 BaseObject 类型的对象可用/适用,因为该协议的扩展需要访问 in BaseObject (特别是someFuncUnrelatedToTypeConstraint 方法),所以如果我不将协议限制为BaseObject,它甚至无法访问someFuncUnrelatedToTypeConstraint。有道理?也就是说,如果你能找到解决我刚刚概述的问题的方法,我全力以赴!
  • 另外,我想你可能误解了我的意思。 BaseObject&lt;NSObject&gt;BaseObject 本身NSObject 无关。这说明泛型的 类型约束NSObject 但同样,这只是试图解决编译器抱怨我必须给它一些类型的问题,即使我没有关心那是什么类型。这就是问题所在。
  • 我说得对吗,您需要将BaseObject 设为抽象类吗?

标签: swift objective-c generics subclass


【解决方案1】:

我猜编译器会抱怨是因为BaseObject&lt;__covariant TypeConstraint&gt; 需要一个类型,但是:

protocol ProtocolForBaseObject : BaseObject {
  associatedtype ReturnType:SomeTypeAcceptingTestObject
}

swift 协议不能继承 Objective-C 类

编辑(我也讨厌)

我不会称之为继承,我很惊讶它编译:

@objc class Foo: NSObject {
    let foo: Int
    init(foo: Int) {
        self.foo = foo
    }
}
protocol Bar: Foo {
    
}

class Baz: Bar { // 'Bar' requires that 'Baz' inherit from 'Foo'
    
}

没有,编译器抱怨:

'Bar' 要求 'Baz' 继承自 'Foo'

编译器似乎将protocol ProtocolForBaseObject : BaseObject 视为protocol ProtocolForBaseObject where Self: BaseObject

再次编辑

class Baz: Foo, Bar {
    
}

编译这让我说 Baz 类继承自 Foo 并符合 Bar 但也许它只是吹毛求疵

【讨论】:

  • 我不确定该说法是否正确,因为您可以轻松编写只能应用于NSObject 的协议。例如,这是一个只能应用于 UIView 对象的协议... protocol ViewStuff : UIView {} extension ViewStuff { func testMe(){ print("Success!") } } extension UITableView : ViewStuff {} let myTableView = UITableView() myTableView.testMe() (旁注:我真的讨厌那个你不能在 cmets 中格式化代码!大声笑!)
  • 您的评论 'protocol ProtocolForBaseObject : BaseObject like protocol ProtocolForBaseObject where Self: BaseObject' 是正确的,因为前者只是后者的简写
  • 所以这不是继承,但也许我错了
  • 另外,您的代码是有意义的,因为Foo 只能应用于NSObject 类型,因此Bar 具有相同的限制(因为它基于Foo),并且因为@987654341 @ 不是 NSObject 你不能让它遵守Bar
  • 我看到您更新了您的示例,所以我之前的评论现在并不真正适用,但同样,您所展示的内容是正确且符合预期的。我只是在您的示例中说,如果您将 Foo 设为泛型,您将如何构造 Bar 以使其可以应用于Foo任何具体实现。
【解决方案2】:

您需要创建宏:

@interface _BaseObject <__covariant TypeConstraint> : NSObject
    
@end
@protocol _ProtocolOnlyApplicableToBaseObject

@end
#define BaseObject(Type) _BaseObject<Type><_ProtocolOnlyApplicableToBaseObject>

用法:

@interface ConcreteObjectA : BaseObject(UIView *)

@end

@interface ConcreteObjectB : BaseObject(UIWindow *)

@end

@interface ConcreteObjectC : BaseObject(id)

@end

【讨论】:

    【解决方案3】:

    我建议使用组合而不是继承:

    @protocol ProtocolOnlyApplicableToBaseObject
    
    @interface BaseObject <__covariant TypeConstraint> : NSObject
    
    @property (nonatomic, weak) id<ProtocolOnlyApplicableToBaseObject> delegate
    
    @end
    

    然后您的子类将能够将self 设置为委托或将能够重用其他一些实现

    【讨论】:

    • 想法相同。因为这样就可以检查[obj conformsToProtocol:(Protocol *)] 无论如何都需要。特别是在混合处理 swift 和 objC 时。有趣的事实是,conformsToProtocolclass 是一种可以被覆盖的方法,即使由于输入更多而这并不那么性感。
    【解决方案4】:

    好的,很明显我在这里的想法不够“Swifty”。正如我的问题中所述,我一直试图将协议 ProtocolForBaseObject 限制为仅适用于 BaseObject&lt;T&gt; 实例,无论 T 是什么。问题是 Swift 不允许您以非类型泛型为目标,如上面的编译器消息所示。

    但后来我退后一步,问自己“你到底想在这里解决什么问题?”当我问这个问题时,它澄清了真正的原因是我需要从协议的扩展中访问someFuncUnrelatedToTypeConstraint

    好的,如果您不能通过将协议限制为无类型泛型来做到这一点,是否有其他方法可以解决该特定问题?

    当我问这个问题时,看到解决方案如此明显,我几乎感到尴尬。它一直就在我面前。

    忘记BaseObject&lt;T&gt;。只需让协议将该方法定义为要求!

    所以现在,而不是这个(再次无法编译......)

    protocol ProtocolForBaseObject : BaseObject { // <-- This line won't compile
      associatedtype ReturnType:SomeTypeAcceptingTestObject
    }
    
    extension ProtocolForBaseObject {
    
        func someTest() -> ReturnType {
            let testObject = self.someFuncUnrelatedToTypeConstraint()
            return ReturnType(testObject)
        }
    }
    

    我现在有了这个,它的工作原理和我希望的完全一样,而且肯定更“Swifty”。

    protocol ProtocolForBaseObject {
      associatedtype ReturnType:SomeTypeAcceptingTestObject
      func someFuncUnrelatedToTypeConstraint()
    }
    
    extension ProtocolForBaseObject {
    
        func someTest() -> ReturnType {
            let testObject = self.someFuncUnrelatedToTypeConstraint()
            return ReturnType(testObject)
        }
    }
    

    当然,这种方法有一点缺点。虽然这解决了我的特殊需求,但那是因为我只需要访问那个功能。但是,如果扩展需要访问整个BaseClass 对象,则可能需要将BaseObject&lt;T&gt; 分成两部分并将所有非泛型相关的方法移至BaseObjectBase,然后使BaseObject&lt;T&gt; 继承自该对象。然后我可以将协议限制为BaseObjectBase,因为不需要类型约束。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-12
      • 1970-01-01
      • 1970-01-01
      • 2011-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多