Обходные пути для универсальной переменной в Swift

Итак, у меня есть кортеж typealias

public typealias MyTuple<T> = (key: T, value: String)

В моем ViewController я хочу объявить массив MyTuple с универсальным типом данных, так как я еще не знаю тип ключа. Однако из this в Swift невозможно иметь переменную универсального типа. Есть и другие обходные пути, как показано ниже, но мне не нравится ни один из них. У кого-нибудь есть идеи получше?

class ViewController: UIViewController {
    var array1 = [MyTuple<T>]() // compile error of course

    var array2 = [MyTuple<Any>]() // no point as I'd use `Any` for MyTuple

    func getArray<T>(array: Array<MyTuple<T>>) -> Array<MyTuple<T>> {
        return array // not a good approach
    }
}

person Lawliet    schedule 10.06.2017    source источник
comment
Итак, проблема в том, что VC определит фактический тип T во время выполнения, и из-за этого вы не можете объявить массив [MyTuple<ConcreteType>], верно?   -  person Vadim Popov    schedule 10.06.2017
comment
@VadimPopov Проблема в том, что у меня есть другой контроллер представления (vcA), который в какой-то момент будет вызывать ViewController и передавать данные в массив MyTuple. К тому времени я буду знать, какой будет тип ключа, но не сейчас.   -  person Lawliet    schedule 11.06.2017


Ответы (2)


Вы можете сделать что-то подобное, используя протокол для объявления массива и базовые методы, которые не зависят от типа данных ключа:

protocol KeyValueArray
{
   associatedtype KeyType
   var array:[(key:KeyType,value:String)] { get set }
}

extension KeyValueArray
{
   var array:[(key: KeyType, value:String)] { get {return []} set { } }
}

class ViewController:UIViewController,KeyValueArray 
{
   // assuming this is like an "abstact" base class 
   // that won't actually be instantiated.
   typealias KeyType = Any   

   // you can implement base class functions using the array variable
   // as long as they're not dependent on a specific key type.
}

class SpecificVC:ViewController
{
   typealias KeyType = Int 
   var array:[(key:Int,value:String)] = []
}

Я предполагаю, что в какой-то момент конкретные экземпляры подклассов контроллера представления будут иметь фактический тип для ключей

person Alain T.    schedule 10.06.2017
comment
Пока лучшая идея. Знаете ли вы, как обновить тип KeyType, не создавая подкласс ViewController? - person Lawliet; 11.06.2017
comment
Это потребовало бы некоторой формы стирания типа и, по сути, привело бы к тому, что переменная массива была бы столь же разрешающей, как (key: Any, value: String). Возможно, более подробное описание того, чего вы пытаетесь достичь, могло бы помочь нам сделать лучшее предложение. - person Alain T.; 11.06.2017
comment
Спасибо, в таком случае предложение @zoul мне подходит. Единственным недостатком является то, что я не могу расширить ViewController. - person Lawliet; 12.06.2017
comment
Выяснилось, что это также не будет работать с делегатом, и я не хочу использовать завершение закрытия, поэтому я опубликовал новый вопрос, как было предложено: stackoverflow.com/q/44490152/7113238 - person Lawliet; 12.06.2017

Я думаю, что обычный способ решить эту проблему — «подтолкнуть» решение типа выше по цепочке зависимостей, к контроллеру представления:

class ViewController<T>: UIViewController {
    var array: [MyTuple<T>]
}

Это имеет смысл, поскольку вы, вероятно, думаете о контроллере как о «контроллере foo», где «foo» — это конкретное значение T. («Контроллер питомца», «контроллер продукта» и т. д.) Но, конечно, вы не можете создать экземпляр массива, пока не узнаете конкретный тип.

person zoul    schedule 10.06.2017
comment
Я думал об универсальном контроллере представления, но если я захочу позже расширить свой ViewController, разве это не будет невозможно? - person Lawliet; 11.06.2017