Параметры '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.