2016-03-16 2 views
1

Я пытаюсь написать класс, который имеет массив объектов, соответствующий MyProto, и у меня есть способ принять [MyProto] для различной обработки перед добавлением к этому массиву. Здесь есть детская площадка.Swift: передать массив типа T методу, принимающему массивы протокола T

protocol MyProto { 
    func sayHello() 
} 

extension MyProto { 
    func sayHello() { 
     print("hello") 
    } 
} 

struct MyStruct: MyProto { 
} 

class MyClass { 
    var protos: [MyProto] = [] 
    func doSomethingAndThenStore(newProtos: [MyProto]) { 
     for proto in newProtos { 
      proto.sayHello() 
     } 
     protos.appendContentsOf(newProtos) 
    } 
} 

let myStructs = [MyStruct(), MyStruct()] 
let myClass = MyClass() 
myClass.doSomethingAndThenStore(myStructs) 

На последней строке я нахожу ошибку, error: cannot convert value of type '[MyStruct]' to expected argument type '[MyProto]'. Если я изменю его на myStructs as [MyProto], ошибка изменится на error: cannot convert value of type '[MyStruct]' to type '[MyProto]' in coercion.

Как передать в массив конкретных типов метод, который принимает массив протоколов?

ответ

2

Эта проблема связана с тем, что Swift не поддерживает ковариантные дженерики. То есть Array<Subclass> не является Array<Superclass>. В этом случае, хотя MyStruct является MyProto, Array<MyStruct> не является Array<MyProto>.

Причины, по которым Swift не поддерживает это, несколько сложны, но это сводится к тому, что для некоторых операций, таких как поиск массива, обработка Array<MyStruct> как Array<MyProto> действительна, но для других, таких как вставка массива, ассоциация фактически идет наоборот. Вы не смогли бы вставить MyProto в Array<MyStruct>, поэтому Array<MyStruct> не может рассматриваться как Array<MyProto>. На других языках есть механизмы для решения этой проблемы, но Swift в настоящее время не поддерживает их.

Вы не можете передать массив напрямую, но есть несколько обходных путей для этого ограничения. Скорее всего, вы можете сопоставить функцию идентификации над массивом, чтобы контролер типа обнаружил новый тип. Это будет неявно обратным приведение каждого элемента из MyStruct в MyProto индивидуально:

myClass.doSomethingAndThenStore(myStructs.map { $0 }) 

Вы можете альтернативно сделать MyClass общих и добавить ограничения типа:

class MyClass<T: MyProto> { 
    var protos: [T] = [] 
    func doSomethingAndThenStore(newProtos: [T]) { 
     for proto in newProtos { 
      proto.sayHello() 
     } 
     protos.appendContentsOf(newProtos) 
    } 
} 
+0

Это делает много смысла. Я думаю, что я просто добавлю перегрузку для 'doSomethingAndThenStore ', который делает карту и вызывает другую. – wjl