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

Концепция области видимости гласит, что, если вы объявили переменную или функцию в одной области вашего кода, вы не сможете использовать ее в другом месте. В этой статье мы сосредоточимся на изучении области видимости и ее значении в разработке под iOS.

Что такое область видимости?

Начнем с примера. Посмотрите на следующий код:

func getAge() -> Int {
    var age = 42
    age += 1
    return age
}
 
var age = 99
var anotherAge = getAge()
anotherAge += 1
 
print(age)
// 99
print(anotherAge)
// 44

В приведенном выше коде мы работаем с двумя типами областей: глобальной и локальной.

В функции присутствует локальная область видимости getAge(), поэтому ее часто называют областью действия функции. Переменные, подобные age, объявленные внутри функции, не могут быть доступны вне ее.

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

Переменная age определена внутри функции getAge(). Мы не можем получить доступ к той же самой переменной за пределами функции.

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

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

Локальная область — это область, в которой вы находитесь в данный момент, то есть это блок кода между фигурными скобками { }. Всегда следите за тем, какова ваша текущая локальная область видимости, и к каким переменным, типам и т. д. у вас есть доступ.

Глобальные и локальные функции, область видимости класса

До сих пор мы рассматривали только глобальные и локальные области видимости на примере области видимости функций, но классы также имеют область видимости. Фактически, фреймворки, модули и сами Swift файлы имеют область видимости.

Давайте начнем с простого класса:

class Product
{
 
}

Данный класс определен в глобальной области видимости. Теперь добавим перечисление в этот класс как вложенный тип:

class Product {
    var kind: Kind = .thing
 
    enum Kind {
        case food
        case thing
    }
}

В приведенном выше коде мы определили перечисление под названием Kind. Мы также добавили свойство экземпляра kind типа Kind, которое по умолчанию инициализируется значением .thing.

  • Область действия класса Product является глобальной. Он определен глобально, поэтому мы можем создавать объекты Product в любом месте кода.
  • Область действия перечисления Kind ограничена классом Product. Мы можем использовать тип Kind только внутри класса.
  • Область действия свойства kind также ограничена классом Product. Мы можем использовать это свойство только внутри класса.

Мы можем использовать класс Product в глобальной области видимости, и, поскольку перечисление Kind имеет уровень доступа по умолчанию internal, мы можем использовать его как тип в любом месте кода — Product.Kind

let banana = Product()
 
if banana.kind == Product.Kind.food {
}

Аналогично свойство kind определяется в области видимости класса, но поскольку оно также общедоступно, мы можем получить доступ к этому свойству для любого объекта типа Product.

Добавим функцию canEat() в класс Product:

class Product {
    var kind: Kind = .thing
 
    enum Kind {
        case food
        case thing
    }
 
    func canEat() -> Bool {
        return kind == .food
    }
}

Мы имеем дело с 3 уровнями области видимости:

  1. Глобальный масштаб, в котором определен класс Product.
  2. Область видимости класса, в которой определено свойство kind, перечисление Kind и функция canEat().
  3. Область видимости функции внутри canEat().

Класс Product определен в глобальном масштабе, так что мы можем использовать его в любом месте нашего приложения. Свойство kind определено в рамках класса, так что мы можем использовать его только в классе Product. То же самое касается перечисления Kind и функции canEat().

Мы используем свойство kind внутри функции canEat(). Это означает, что у областей видимости есть своя иерархия, потому что мы можем получить доступ к свойству из области видимости класса в пределах функции. Однако, если мы определили локальную переменную внутри canEat(), мы не сможем использовать эту переменную в другой функции того же класса, потому что они имеют разные области видимости.

func canEat() -> Bool {
    let hungry = ...
}
 
func isThing() -> Bool {
    // Мы не можем использовать здесь hungry
}

Область видимости для замыканий

Область видимости используются повсюду в разработке для iOS. Всегда полезно знать, к какой переменной вы можете получить доступ из текущей области видимости.

Интересный случай области видимости — это замыкания. Замыкание — это блок кода, который можно использовать в любом месте вашей программы. Замыкание по сути является функцией, за исключением того, что сам код является значением. Вы можете назначить замыкание на переменную, передать его в функцию, после чего оно окажется в другой части вашей программы.

Замыкания часто используются в качестве так называемых обработчиков завершения (completion handlers). Скажем, мы загружаем изображение асинхронно из интернета. Когда загрузка завершится, нам нужно выполнить некоторый код, чтобы показать изображение. Мы определяем этот код в замыкании, передаем функции, которая загружает изображение. Затем эта функция выполняет код замыкания после завершения загрузки.

class DetailViewController: UIViewController {
    @IBOutlet weak var imageView: UIImageView?
 
    func viewDidLoad() {
        network.downloadImage(url, completionHandler: { image in
            imageView?.image = image
        })
    }
}

В приведенном выше коде мы создали класс View Controller со свойством imageView. Внутри функции мы вызываем функцию downloadImage(_:completionHandler:). Второй параметр, обработчик завершения, заключенный между внутренними фигурными скобками, является замыканием. Когда загрузка изображения завершается, мы присваиваем загруженное значение свойству image для imageView, которое выводит загруженное изображение.

Замыкания называется замыканием, потому что они фиксируют («замыкают») значения внешних параметров.

В приведенном выше примере кода замыкание содержит ссылку на свойство imageView. Это свойство понадобится нам позже, чтобы установить изображение. Когда замыкание завершает свою работу, ссылка освобождается.

Захват значений работает, только если замыкание имеет доступ к той же области видимости, что и функция, в которой оно определено. Даже если замыкание является отдельной сущностью, оно может получить доступ к свойству imageView, поскольку функция viewDidLoad() имеет доступ к этой области. Замыкания имеют ту же область действия, что и объект, в котором они определены, и в данном случае это область действия функции.

Читайте также:
Добавить комментарий

Ваш адрес email не будет опубликован.