Как я могу наложить NSMutableArray на массив Swift определенного типа?
Я переношу свой проект iOS на Swift. Я занимаюсь этим классом по классам. Когда я вызываю методы Objective C из Swift, многие типы Objective C преобразуются в их Swift-копии.
В моем случае Objective C NSMutableArray
преобразуется в Swift Array<AnyObject>
. Теперь вот моя проблема. В моем классе Swift я получаю такой массив из объекта Objective C. Теперь, когда я нахожусь в мире Swift, я хотел бы передать этот массив конкретному типу вместо AnyObject
, потому что я точно знаю, какие объекты существуют в этом массиве.
Компилятор не позволит мне это сделать! Позвольте мне упростить мою проблему, сказав, что я хочу передать массив, содержащий строки. Это то, что я пробовал:
var strings = myObjcObject.getStrings() as [String]
Я получаю следующую ошибку от компилятора:
'String' не идентичен "AnyObject"
Я должен согласиться с компилятором, поскольку String действительно не идентичен AnyObject. Но я не понимаю, почему это проблема. Я могу сбрасывать AnyObject в String, если хочу, правильно?
Я также пробовал:
var strings = myObjcObject.getStrings() as? [String]
Кажется, это шаг в правильном направлении, но getStrings() возвращает NSMutableArray
, поэтому я получаю следующую ошибку:
"NSArray" не является подтипом "NSMutableArray"
Есть ли способ сделать то, что я пытаюсь сделать здесь?
Ответы
Ответ 1
Вы можете сделать эту работу с двойным нажатием, сначала на 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]
Update:
В более поздних версиях Swift (как минимум 1,2) компилятор будет жаловаться на as [String]
. Вместо этого вы должны использовать if let
с условным нажатием as?
:
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]
Ответ 2
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 }
Ответ 3
С Swift 1.2 будет работать следующее:
let mutableArray = NSMutableArray(array: ["a", "b", "c"])
let swiftArray = NSArray(array: mutableArray) as? [String]
Ответ 4
let mutableArray = NSMutableArray()
mutableArray.add("Abc")
mutableArray.add("def")
mutableArray.add("ghi")
if let array = mutableArray as? [String] {
print(array) // ["Abc", "def", "ghi"]
}
Ответ 5
в Xcode 6.3 я использовал следующее:
var mutableArray = NSMutableArray(array:"1", "2", "3")
let swiftArray = mutableArray as AnyObject as! [String]
Ответ 6
для быстрого 3
вы можете рассмотреть следующий код
let array: [String] = nsMutableArrayObject.copy() as! [String]
Ответ 7
В моем случае компилятор хотел, чтобы я написал это так, чтобы подавить все предупреждения и проблемы компиляции, так что не только этот восклицательный знак, даже если поле imagesField уже объявлено с одним, но также скобки и "как!" чтобы убедиться, что никто не жалуется.
(imagesField!.images as! [UIImage]) 🤮
Это заставило меня чувствовать себя довольно неловко... Свифт может быть лучше, его новый язык так... Я сделал расширение:
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])
}
Ответ 8
Я создал расширение для NSArray для этого.
extension NSArray {
func swiftArray<T>() -> [T] {
let result: [T] = self.compactMap { $0 as? T }
return result
}
}
Использование:
class Test {
var title: String
init(title: String) {
self.title = title
}
}
let mutableArray = NSMutableArray()
mutableArray.add(Test(title: "1"))
mutableArray.add(Test(title: "2"))
mutableArray.add(Test(title: "3"))
mutableArray.add(Test(title: "4"))
let tests: [Test] = mutableArray.swiftArray()