Ответ 1
Нет. Swift заставляет вас указывать значение по умолчанию, точно так же, как вы обрабатываете переменные и поля. Единственный случай, когда Swift имеет концепцию значения по умолчанию для необязательных типов, где nil
(Optional.None
).
Я пытаюсь перенести пример Matrix из книги Swift в общий.
Вот что я получил до сих пор:
struct Matrix<T> {
let rows: Int, columns: Int
var grid: T[]
init(rows: Int, columns: Int, repeatedValue: T) {
self.rows = rows
self.columns = columns
grid = Array(count: rows * columns, repeatedValue: repeatedValue)
}
func indexIsValidForRow(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
subscript(row: Int, column: Int) -> T {
get {
assert(indexIsValidForRow(row, column: column), "Index out of range")
return grid[(row * columns) + column]
}
set {
assert(indexIsValidForRow(row, column: column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
}
Обратите внимание, что мне пришлось передать repeatedValue: T
в конструктор.
В С# я бы просто использовал default(T)
, который был бы 0
для чисел, false
для booleans и null
для ссылочных типов. Я понимаю, что Swift не разрешает nil
для необязательных типов, но мне все же интересно, если передача явного параметра является единственным способом, или если у меня есть какой-то эквивалент default(T)
там.
Нет. Swift заставляет вас указывать значение по умолчанию, точно так же, как вы обрабатываете переменные и поля. Единственный случай, когда Swift имеет концепцию значения по умолчанию для необязательных типов, где nil
(Optional.None
).
Если "ДА". Вы можете использовать ограничения протокола, чтобы указать требование, чтобы ваш общий класс или функция работали только с типами, реализующими функцию init по умолчанию (без параметров). Последствия этого, скорее всего, будут плохими (это не работает так, как вы думаете, что оно делает), но это самое близкое к тому, что вы просили, вероятно, ближе, чем ответ "НЕТ".
Для меня я нашел это лично полезным при разработке нового родового класса, а затем, в конце концов, я удалю ограничение и исправлю оставшиеся проблемы. Требование только типов, которые могут принимать значение по умолчанию, ограничит полезность вашего общего типа данных.
public protocol Defaultable
{
init()
}
struct Matrix<Type: Defaultable>
{
let rows: Int
let columns: Int
var grid: [Type]
init(rows: Int, columns: Int)
{
self.rows = rows
self.columns = columns
grid = Array(count: rows * columns, repeatedValue: Type() )
}
}
Существует способ получить эквивалент default(T)
в swift, но он не является бесплатным и имеет связанную с ним опасность:
public func defaultValue<T>() -> T {
let ptr = UnsafeMutablePointer<T>.alloc(1)
let retval = ptr.memory
ptr.dealloc(1)
return retval;
}
Теперь это явно хак, потому что мы не знаем, инициализируется ли alloc()
чему-то познаваемому. Это все 0? Вещи, оставшиеся в куче? Кто знает? Более того, сегодня это может быть завтра.
Фактически, использование возвращаемого значения для ничего, кроме заполнителя, опасно. Скажем, у вас есть такой код:
public class Foo { /* implementation */
public struct Bar { public var x:Foo }
var t = defaultValue<Bar>();
t = someFactoryThatReturnsBar(); // here our problem
В строке проблем Swift считает, что t
был инициализирован, потому что семантика Swift говорит: вы не можете иметь переменную типа значения, которая не инициализирована. За исключением того, что это потому, что default<T>
нарушает эти семантики. Когда вы выполняете задание, Swift выдает вызов в таблицу показаний значения, чтобы уничтожить существующий тип. Это будет включать код, который вызовет release
в поле x
, потому что семантика Swift говорит, что экземпляры объектов никогда не nil
. И тогда вы получите краху времени выполнения.
Однако у меня была причина взаимодействовать с Swift с другого языка, и мне пришлось пройти дополнительный тип. К сожалению, Swift не дает мне способ построить необязательный во время выполнения из-за причин (по крайней мере, я не нашел способа), и я не могу легко их издеваться, потому что опции реализуются с точки зрения общего перечисления и перечисления используют слабо документированную стратегию 5, чтобы упаковать полезную нагрузку перечисления.
Я работал над этим, передавая кортеж, который я собираюсь называть кортежем Medusa только для усмешек: (value: T, present: Bool)
, у которого есть контракт, что если present
есть true
, то value
гарантированно будет действителен, недействителен. Теперь я могу использовать это безопасно для взаимодействия:
public func toOptional<T>(optTuple: (value:T, present:Bool)) -> T?
{
if optTuple.present { return optTuple.value }
else { return nil }
}
public func fromOptional<T>(opt: T?) -> (T, Bool)
{
if opt != nil { return (opt!, true) }
else {
return (defaultValue(), false)
}
}
Таким образом, мой код вызова переходит в кортеж вместо необязательного и принимающего кода и превращает его в необязательный (и наоборот).