对于 Swift 方法参数,将非泛型 ObjC 类 向上转换为父 class

Upcast non-generic ObjC Classes to parent class for Swift method parameter

我在 Objective-C 中定义了一些与此类似的 classes:

@interface Type: NSObject {
    
}
@end

@interface SubType1: Type {
    
}
@end

@interface SubType2: Type {
    
}
@end

@interface Parent <T: __kindof Type *> : NSObject
@property (nonatomic, strong) NSArray <T> *anArray;
@property (nonatomic, strong) T anObject;
@end

@interface SubParent1: Parent<SubType1 *> {
    
}
@end

@interface SubParent2: Parent<SubType2 *> {
    
}
@end

我正在尝试制作一个 Swift 函数,它可以接受 Parent 的任何子 class。我尝试了以下方法:

func callFunc(parent: Parent<Type>) {
                
}
callFunc(parent: SubParent1())

我得到错误:Cannot convert value of type 'Parent<SubType1> to expected argument type 'Parent<Type>'

也尝试过:

func callFunc<T>(parent: T) where T: Parent<Type>{

}
callFunc(parent: SubParent1())

我收到错误 Type of expression is ambiguous without more context

一般来说,我想要一种方法可以处理任何类型的子父 class(SubParent1SubParent2),它具有父 class 的子类型type (SubType1, SubType2) 因为该方法只需要访问 Parent 上定义的属性。由于某些其他限制,切换 Objective-C classes 不是一个可行的选项,因此我正在寻找一种解决方案,以保持 Objective-C 中定义的 classes。不确定是否可能,但如果可能的话,如果参数预期为 Parent?

,我可以在之后向下转换吗?

稍后编辑:此外,我如何允许函数 return Swift 中的任何子父类型,例如:

enum AnEnum {
    case subParent1(_ : SubParent1)
    case subParent2(_ : SubParent2)
    
    func subParent<T: Type>() -> Parent<T>? {
        switch self {
        case .subParent1(let sub1):
            return sub1
        case .subParent2(let sub2):
            return sub2
        }
    }
}

编译器抱怨 sub1sub2 'Type of expression is ambiguous without more context'。

这里的问题是您期望协变,但是 Swift 泛型通常不支持协变。

网上关于协方差的解释很多,这里就不多说了。但是我将确切地展示您的示例类型的协方差是如何不合理的。考虑这段代码:

let sp1 = SubParent1()
let p = sp1 as Parent<Type>  // (1) Swift forbids this cast...
let t: Type = SubType2()
p.anObject = t               // (2) ...because this assigment would be unsound.

假设 Swift 允许禁止的演员表 (1)。然后赋值 (2) 会将 SubType2 分配给只能容纳 SubType1.

的 属性

您可以通过使 callFunc 函数对 Parent 子类型和 Type 子类型通用来解决此问题:

func callFunc<T, P>(parent: P) where T: Type, P: Parent<T> { }

编辑:Rob Napier 的回答正确地表明您只需要在 T 上通用,而不是在 P.

上通用

您正在对 Parent 而不是 Type 进行参数化,而您实际上正在更改的是 Type。你的意思是:

func callFunc<T>(parent: Parent<T>) { ... }

虽然您可以显式调用 T: Type,但这并不是真正必要的,因为父级已经强制执行了。