如何将NSMutableArray转换为特定类型的Swift数组?

72

我正在将我的iOS项目迁移到Swift。我逐个类地进行这个过程。当我从Swift中调用Objective C方法时,许多Objective C类型会转换为它们的Swift对应类型。

在我的情况下,一个Objective C的NSMutableArray被转换为Swift的Array<AnyObject>。现在问题来了:在我的Swift类中,我从一个Objective C对象中得到了这样的一个数组。既然我处于Swift世界中,我想把这个数组转换成一个特定类型的数组,而不是AnyObject,因为我确定这个数组中存在什么类型的对象。

但编译器不允许我这样做!让我通过说我想转换成包含字符串的数组来简化我的问题:

```swift let array = getArray() as! [String] ```

var strings = myObjcObject.getStrings() as [String]

编译器显示以下错误:

'String'与'AnyObject'不相同

我同意编译器的说法,因为String确实与AnyObject不同。但我不明白这是什么问题。如果需要,我可以将AnyObject强制转换为String,对吗?

我还尝试了:

var strings = myObjcObject.getStrings() as? [String]

看起来这是朝着正确方向迈出的一步,但是 getStrings() 返回一个 NSMutableArray,所以我得到以下错误:

'NSArray' 不是 'NSMutableArray' 的一个子类型

有没有办法在这里实现我想要做的事情?

7个回答

137

您可以通过双重向下转换来实现这一点,首先将其转换为NSArray,然后再转换为[String]

var strings = myObjcObject.getStrings() as NSArray as [String]

测试环境:

import Foundation

var objCMutableArray = NSMutableArray(array: ["a", "b", "c"])
var swiftArray = objCMutableArray as NSArray as [String]

更新:

在后续版本的Swift(至少1.2),编译器将会抱怨 as [String]。取而代之,你应该使用一个带有条件向下转型的 if letas?

import Foundation

var objCMutableArray = NSMutableArray(array: ["a", "b", "c"])
if let swiftArray = objCMutableArray as NSArray as? [String] {
    // Use swiftArray here
}

如果你非常确定你的NSMutableArray可以转换为[String],那么你可以使用as!(但在大多数情况下你不应该使用这个):

import Foundation

var objCMutableArray = NSMutableArray(array: ["a", "b", "c"])
var swiftArray = objCMutableArray as NSArray as! [String]

1
感谢您对我的问题给出了清晰明确的答案!现在它可以正常工作了! - Tom van Zummeren
我也用这个方法解决了手势识别的问题。var arrayOfGestureRecognizers: [UIGestureRecognizer] = self.topViewController.view.gestureRecognizers! as NSArray as [UIGestureRecognizer] 谢谢! - Joshua Dance
@Mike,你能否更新一下你的答案,加上Swift 1.2中的NSArray as! [String]吗? - Chris Conover
1
我想要指出,在ObjC源代码中,你可以向objc属性添加必要的泛型类型。在我看来,这比进行所有这些强制转换更加简洁。 - Oxcug
1
可能我永远也不会喜欢Swift... - Massimo
显示剩余2条评论

37

compactMap在Swift 4.1及以上版本中非常有用,同样适用于Swift 3.3-3.4。这意味着您不需要进行任何重复或强制转换。

let mutableArray = NSMutableArray(array: ["a", "b", "c"])
let swiftArray: [String] = mutableArray.compactMap { $0 as? String }
在之前的 Swift 版本 2.0-3.2 和 4.0 中,您需要使用 flatMap 来达到此目的。用法与 compactMap 相同。
let swiftArray: [String] = mutableArray.flatMap { $0 as? String }

1
这样做的好处是,即使数组中包含一些不属于 String/NSString 类型的元素,因此无法将整个数组作为一个整体进行转换,但仍然可以挽救“兼容”的元素。 - Nicolas Miari

5

使用Swift 1.2,以下内容将会生效:

let mutableArray = NSMutableArray(array: ["a", "b", "c"])
let swiftArray = NSArray(array: mutableArray) as? [String]

当我尝试在Swift 1.2中运行此代码时,会出现以下错误:'NSMutableArray' 无法转换为 '[AnyObject]';你是否意味着使用 'as!' 强制向下转换? - Tom van Zummeren

5
let mutableArray = NSMutableArray()

mutableArray.add("Abc")
mutableArray.add("def")
mutableArray.add("ghi")

if let array = mutableArray as? [String] {
    print(array)    // ["Abc", "def", "ghi"]
}

2
在Xcode 6.3中,我使用了以下代码:
var mutableArray = NSMutableArray(array:"1", "2", "3")
let swiftArray = mutableArray as AnyObject as! [String]

1

对于 Swift 3,您可以考虑以下代码:

let array: [String] = nsMutableArrayObject.copy() as! [String]

1
在我的情况下,编译器希望我这样写以抑制所有警告和编译问题,因此不仅是感叹号,即使字段imagesField已经声明了一个,也需要括号和“as!”来确保没有人抱怨。
(imagesField!.images as! [UIImage]) 

这让我感到相当不舒服... Swift 可以更友好,毕竟它是一门新语言... 我创建了一个扩展:

 public static func cast(_ object: Any) -> Self {
        return object as! Self
    }

将其分配给数组:

extension Array: CSLang {
}

现在我可以用同样的效果来写出同样的语句:

[UIImage].cast(imagesField.images)

无论你喜欢与否,这是我的方式,少些疑问和感叹号更好。我还进行了单元测试:

func testCast() {
    let array: NSMutableArray? = NSMutableArray()
    array?.add("test string 1")
    array?.add("test string 2")
    let stringArray: [String] = [String].cast(array)
    XCTAssertEqual("test string 2", stringArray[1])
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接