如何实现Sequence(允许Swift的forin语法)?

时间:2022-09-02 09:39:53

I'm writing an API in Objective-C and would like it to play nicely in Swift. I'm having trouble getting "for..in" syntax working though. I think I need to implement the Sequence protocol, but I can't find any examples doing this from Objective-C. Just referencing Sequence gives me error: no type or protocol named 'Sequence'. Is there a special #import to get access to it or something?

我正在用Objective-C编写一个API,希望它能在Swift中正常运行。我很难得到……在“语法虽然工作。我想我需要实现序列协议,但是我找不到任何实例来实现它。只是引用序列给了我错误:没有类型或协议命名为“序列”。是否有一个特殊的#import来访问它?

I tried implementing the NSFastEnumeration protocol, thinking maybe it'd magically convert to Sequence in Swift, but that didn't work.

我试着实现了NSFastEnumeration协议,想它可能会神奇地转换成Swift的序列,但那没用。

///// Obj-C Code
@interface Foo : NSObject<NSFastEnumeration>
...
@end

///// Swift Code
var foo: Foo = Foo()

// ERROR: Type 'Foo' does not conform to protocol 'Sequence'
for y in foo {
    print("Got a y.")
}

EDIT: It looks like inheriting from NSEnumerator gets me closer, but doesn't quite work either:

编辑:看起来从NSEnumerator继承的东西让我更接近了,但也不是很有效:

///// Obj-C Code
@interface Foo : NSEnumerator<NSString *>
...
@end

///// Swift Code
// ERROR: 'NSFastEnumerationIterator.Element' (aka 'Any') is not convertible to 'String'
for y: String in foo {
    print("Got \(y)")
}

EDIT 2: I still don't have a good solution and have logged a bug: https://bugs.swift.org/browse/SR-2801

编辑2:我仍然没有一个好的解决方案,并且已经记录了一个错误:https://bugs.swift.org/browse/SR-2801。

1 个解决方案

#1


2  

The Swift extension of Foundation includes some support for making classes that adopt NSFastEnumeration also support the Swift Sequence protocol... but not automatically.

Foundation的Swift扩展包括一些支持,使采用NSFastEnumeration的类也支持Swift序列协议……但不会自动。

One way to do it is to extend your ObjC type in Swift and pass through to the NSFastEnumerationIterator type:

一种方法是快速扩展ObjC类型,并传递到NSFastEnumerationIterator类型:

extension Foo: Sequence {
    public func makeIterator() -> NSFastEnumerationIterator {
        return NSFastEnumerationIterator(self)
    }
}

NSFastEnumerationIterator (and all forms of ObjC enumeration) are type erasing, though, so they don't provide any insight on the element type you're iterating through. That means that you can do this (after adding the above extension):

但是,NSFastEnumerationIterator(以及所有形式的ObjC枚举)都是类型擦除,因此它们不会提供您正在遍历的元素类型的任何了解。这意味着您可以这样做(在添加了上述扩展之后):

var foo: Foo = Foo()
for y in foo {
    print("Got a y.")
}

... but the static type of y is always Any. If you want typed access to the members of foo, you'll need a cast or a filtered loop:

…但是y的静态类型总是任意的。如果您想要对foo的成员进行类型化访问,您将需要强制转换或过滤循环:

for y in foo where y is String {
    print("Got \(y)")
}

Sadly, if your class adopts ObjC generics, there doesn't seem to be a way to make this work — you'll get an error "Extension of a generic Objective-C class cannot access the class's generic parameters at runtime", even if you adopt the runtime type introspection method(s) in SE-0057. For non-generic ObjC classes you're good, though.

遗憾的是,如果您的类采用ObjC泛型,那么似乎没有办法使这个工作——您将得到一个错误“通用Objective-C类的扩展不能在运行时访问类的泛型参数”,即使您采用的是SE-0057中的运行时类型自省方法(s)。但是对于非泛型ObjC类,您是很好的。

#1


2  

The Swift extension of Foundation includes some support for making classes that adopt NSFastEnumeration also support the Swift Sequence protocol... but not automatically.

Foundation的Swift扩展包括一些支持,使采用NSFastEnumeration的类也支持Swift序列协议……但不会自动。

One way to do it is to extend your ObjC type in Swift and pass through to the NSFastEnumerationIterator type:

一种方法是快速扩展ObjC类型,并传递到NSFastEnumerationIterator类型:

extension Foo: Sequence {
    public func makeIterator() -> NSFastEnumerationIterator {
        return NSFastEnumerationIterator(self)
    }
}

NSFastEnumerationIterator (and all forms of ObjC enumeration) are type erasing, though, so they don't provide any insight on the element type you're iterating through. That means that you can do this (after adding the above extension):

但是,NSFastEnumerationIterator(以及所有形式的ObjC枚举)都是类型擦除,因此它们不会提供您正在遍历的元素类型的任何了解。这意味着您可以这样做(在添加了上述扩展之后):

var foo: Foo = Foo()
for y in foo {
    print("Got a y.")
}

... but the static type of y is always Any. If you want typed access to the members of foo, you'll need a cast or a filtered loop:

…但是y的静态类型总是任意的。如果您想要对foo的成员进行类型化访问,您将需要强制转换或过滤循环:

for y in foo where y is String {
    print("Got \(y)")
}

Sadly, if your class adopts ObjC generics, there doesn't seem to be a way to make this work — you'll get an error "Extension of a generic Objective-C class cannot access the class's generic parameters at runtime", even if you adopt the runtime type introspection method(s) in SE-0057. For non-generic ObjC classes you're good, though.

遗憾的是,如果您的类采用ObjC泛型,那么似乎没有办法使这个工作——您将得到一个错误“通用Objective-C类的扩展不能在运行时访问类的泛型参数”,即使您采用的是SE-0057中的运行时类型自省方法(s)。但是对于非泛型ObjC类,您是很好的。