Параметры 'var' устарели и будут удалены в Swift 3.


120

Хорошо, я просто обновил Xcode до 7.3 и теперь получаю это предупреждение:

Параметры 'var' устарели и будут удалены в Swift 3.

Как исправить это, когда мне нужно использовать var в этой функции:

public func getQuestionList(var language: String) -> NSArray {
    if self.data.count > 0 {
        if (language.isEmpty) {
            language = "NL"
        }
        return self.data.objectForKey("questionList" + language) as! NSArray
    }

    return NSArray()
}

6
Как насчетpublic func getQuestionList(inout language: String) -> NSArray
TotoroTotoro

2
Нет, это не подходящая замена. OP, вероятно, не хочет getQuestionиметь никаких побочных эффектов.
BallpointBen

5
Честно говоря, я понятия не имею, почему они даже рассмотрели возможность удаления этого. Это была одна из функций, сделавших Swift потрясающим!
Дэнни Браво

Сам никогда не пользовался и не разбираюсь в суете.
Майк Таверн

@MikeTaverne (поздний ответ) Рассмотрим следующую функцию: func foo(_ bar: int) { /*use bar*/ bar+=1; foo(bar); }. Это невозможно без параметров var. Вам нужно либо создать отдельную переменную внутри функции и скопировать значение, либо пометить параметр как inout. Первый медленный, второй вызывает неопределенное поведение. Многие алгоритмы используют такую ​​рекурсию.
Кевин

Ответы:


82

Вы пытались назначить новую переменную

public func getQuestionList(language: String) -> NSArray {
    var lang = language
    if self.data.count > 0 {
        if (lang.isEmpty) {
            lang = "NL"
        }
        return self.data.objectForKey("questionList" + lang) as! NSArray
    }

    return NSArray()
}

11
Не совсем то, что, я думаю, хотели ОП
сера

6
Я бы понял вопрос OP так же, как @garana. OP не использует inout в своем вопросе, они просто изменяют уже существующую переменную локально .
Эрик Айя,

11
Собственно это правильное решение. Пожалуйста, ознакомьтесь с проблемой Swift evolution, в которой было предложено это изменение: github.com/apple/swift-evolution/blob/master/proposals/…
Скотт Томпсон,

8
@TimVermeulen Все хотят использовать прогрессивный язык. Apple может развивать свой язык разными способами, не меняя синтаксис каждый месяц. Как вы знаете, тонна онлайн-документов и фрагментов кода устарела или устарела из-за Apple. Из-за этого разработчикам приходится неоднократно приходить на этот сайт, чтобы попросить помощи с множеством глупых вопросов. Синтаксис должен быть твердым с самого начала, если Apple хочет, чтобы больше разработчиков преуспели в этом.
TomSawyer

25
Используйте var language = language, если вы не хотите вводить другое имя переменной (что было главным преимуществом параметра var, в первую очередь, imo)
Харрис,

102

Обсуждение удаления Var из параметра функции полностью задокументировано в этой публикации на GitHub: Remove Var Parameters

В этом документе вы обнаружите, что люди часто путают varпараметры с inoutпараметрами. varПараметр просто означает , что параметр является изменяемым в контексте функции, в то время как с inoutпараметром значение параметра в точке возврата будет скопировано из функции , и в контекст вызывающего абонента.

Правильный способ решить эту проблему - удалить varиз параметра и ввести локальную varпеременную. В верхней части подпрограммы скопируйте значение параметра в эту переменную.


44
Я вообще не понимаю этого изменения, почему необходимость писать еще одну строку для создания изменяемой локальной переменной лучше, чем просто определять параметр как var?
Росс Барбиш

Для меня это изменение хорошо, потому что оно подбирает ситуации, когда я должен был реализовать локальную переменную, но я этого не сделал, потому что я выбрал простой выход и принял (старое) предложение Swift о том, чтобы сделать входной параметр var
dawid

1
Я с @RossBarbish по этому поводу. Итак ... это удаляется, потому что ленивые разработчики не могут различить параметры inout и var? Пффф ...
Дэнни Браво

1
Это кажется ужасно ненужным ... им следовало оставить оба варианта.
Оскар Гомес,

1
Вероятно, Swift все равно объявлял локальную переменную поверх параметра за кулисами. Теперь придется делать это вручную. Никаких изменений в производительности, но мы потеряли удобство, чтобы помочь новичкам с простой концепцией.
могельбастер

62

Просто добавьте эту строку в начало функции:

var language = language

а остальная часть вашего кода может остаться без изменений, например:

public func getQuestionList(language: String) -> NSArray {
    var language = language
    if self.data.count > 0 {
        if (language.isEmpty) {
            language = "NL"
        }
        return self.data.objectForKey("questionList" + language) as! NSArray
    }

    return NSArray()
}

5
На сегодняшний день лучший ответ. Требуется изменить только одну строку.
BallpointBen

Но выглядит так неестественно @James
asyncwait

1
Я считаю, что это лучший ответ, поскольку он носит то же имя. Подобно тому, как это делают другие распространенные языки.
eonist

1
@RiverSatya Почему бы просто не использовать параметр напрямую?
Деклан МакКенна

1
Действительно отличное предложение. Мы реализуем это таким образом в Swiftify :)
Crulex

13

Многие люди предлагают inoutпараметр, но на самом деле они созданы не для этого. Кроме того, он не позволяет вызывать функцию с letконстантой или строковым литералом. Почему бы вам просто не добавить значение по умолчанию в подпись функции?

public func getQuestionList(language language: String = "NL") -> NSArray {
    if data.count > 0 {
        return data.objectForKey("questionList" + language) as! NSArray
    } else {
        return NSArray()
    }
}

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

let list = getQuestionList() // uses the default "NL" language

3
Я также не понимаю, почему все бросились на решение inout, когда OP даже не использовал его в начале ...
Эрик Айя,

1
Они предполагали, что var и inout делают то же самое.
ryantxr

2

Swift 4

public func getQuestionList(language: inout String) -> NSArray {
    if self.data.count > 0 {
        if (language.isEmpty) {
            language = "NL"
        }
        return self.data.objectForKey("questionList" + language) as! NSArray
    }

    return NSArray()
}

getQuestionList(language: &someString)

В некоторых случаях, как я испытал (с более сложными настройками, включающими массивы), создание нового свойства внутри метода и изменение этого свойства может не всегда работать. Не говоря уже о том, что вы загромождаете метод вместо того, чтобы просто добавлять его inoutк параметру и &его аргументу, для чего был создан этот синтаксис.



0

Я думаю, что ответы @Harris и @garanda - лучший подход.

В любом случае, в вашем случае нет необходимости в var, вы можете:

public func getQuestionList(language: String) -> NSArray {
    if self.data.count > 0 {
        return self.data.objectForKey("questionList" + (language.isEmpty ? "NL" : language)) as! NSArray
    }
    return NSArray()
}

0

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html

Параметры входа-выхода

Параметры функции по умолчанию являются константами. Попытка изменить значение параметра функции из тела этой функции приводит к ошибке времени компиляции. Это означает, что вы не можете изменить значение параметра по ошибке. Если вы хотите, чтобы функция изменяла значение параметра, и вы хотите, чтобы эти изменения сохранялись после завершения вызова функции, вместо этого определите этот параметр как параметр входа-выхода.

Вы пишете параметр in-out, помещая ключевое слово inout прямо перед типом параметра. Параметр входа-выхода имеет значение, которое передается в функцию, модифицируется функцией и передается обратно из функции для замены исходного значения. Подробное обсуждение поведения параметров ввода-вывода и связанных оптимизаций компилятора см. В разделе Параметры ввода-вывода.

Вы можете передавать только переменную в качестве аргумента для входящего-выходного параметра. Вы не можете передать константу или буквальное значение в качестве аргумента, потому что константы и литералы не могут быть изменены. Вы помещаете амперсанд (&) непосредственно перед именем переменной, когда вы передаете его в качестве аргумента входному-выходному параметру, чтобы указать, что оно может быть изменено функцией.

НОТА

Параметры ввода-вывода не могут иметь значений по умолчанию, а параметры с переменными параметрами не могут быть помечены как входящие.

Вот пример функции под названием swapTwoInts ( : :), которая имеет два целочисленных параметра входа -выхода, называемых a и b:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

Функция swapTwoInts ( : :) просто меняет местами значение b на a, а значение a на b. Функция выполняет этот обмен, сохраняя значение a во временной константе с именем временнаяA, присваивая значение b переменной a, а затем присваивая временную переменную b.

Вы можете вызвать функцию swapTwoInts ( : :) с двумя переменными типа Int, чтобы поменять местами их значения. Обратите внимание, что имена someInt и anotherInt имеют префикс амперсанда, когда они передаются в функцию swapTwoInts ( : :):

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"

В приведенном выше примере показано, что исходные значения someInt и anotherInt изменяются функцией swapTwoInts ( : :), даже если они изначально были определены вне функции.

НОТА

Параметры ввода-вывода - это не то же самое, что возвращаемое значение из функции. В приведенном выше примере swapTwoInts не определяется тип возвращаемого значения и не возвращается значение, но он по-прежнему изменяет значения someInt и anotherInt. Параметры ввода-вывода - это альтернативный способ для функции иметь эффект, выходящий за рамки ее тела функции.


0

Вот еще одна идея. Мой вариант использования заключался в передаче массива строк для добавления к нему, для чего массив должен передаваться изменчиво. Я тоже не хотел, чтобы в моем классе было такое положение. Итак, я создал класс, содержащий массив, и передал его. В зависимости от вашего варианта использования может показаться глупым иметь класс, содержащий только эту одну переменную.

private class StringBuilder {
    var buffer: [String] = []

    func append(_ str: String) {
        buffer.append(str)
    }

    func toString() -> String {
        return buffer.joined()
    }
}

Я использую только методы appendи joinedв массиве, поэтому было легко изменить тип с минимальными другими изменениями в моем коде.

Пример использования:

private func writeMap(map: LevelMap, url: URL) -> Bool {
    let buffer = StringBuilder()

    if !writeHeader(map: map, buffer: buffer) {
        return false
    }
    if !writeFloors(map: map, buffer: buffer) {
        return false
    }

    let content = buffer.toString()
    do {
        try content.write(to: url, atomically: true, encoding: .utf8)
        return true
    } catch {}
    return false
}

private func writeHeader(map: LevelMap, buffer: StringBuilder) -> Bool {
    buffer.append("something here ...\n")
    return true
}

Я отвечу, если вы ХОТИТЕ изменить исходное значение, которое видит вызывающий. Если вы просто хотите иметь возможность локально переназначить значение, но не влиять на вызывающего абонента, другие ответы выше касаются этого.
webjprgm
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.