Книга: Swift. Основы разработки приложений под iOS, iPadOS и macOS. 5-е изд. дополненное и переработанное
Назад: 30. Удаление экземпляров и ARC
Дальше: 32. Расширения

31. Опциональные цепочки

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

31.1. Доступ к свойствам через опциональные цепочки

Рассмотрим следующий пример.

Разработаны два класса: первый является простейшим описанием сущности «место жительства», а второй — сущности «персона». При этом у персоны имеется свойство опционального типа, которое может содержать экземпляр «места жительства» (листинг 31.1).

Листинг 31.1

class Residence {

    var rooms = 1

}

class Person {

    var residence: Residence?

}

Экземпляры класса Person имеют единственное свойство со ссылкой на экземпляр класса Residence, который также имеет всего одно свойство, характеризующее количество комнат.

Если создать экземпляр класса Person, то свойство residence получит свойство nil, поскольку оно является опционалом. В определенный момент времени мы не сможем сказать, а есть ли значение в данном опционале. Для того чтобы избежать ошибки, необходимо сперва проверить наличие значения в свойстве residence, и только потом обращаться к нему. Мы знаем уже несколько способов сделать это, одним из которых является опциональное связывание (листинг 31.2).

Листинг 31.2

var man = Person()

if let manResidence = man.residence {

    print("Есть дом с \(manResidence.rooms) комнатами")

}else{

    print("Нет дома")

}

Консоль

Нет дома

Представленный подход позволяет решить проблему, но потребует писать лишний код, если, к примеру, вложенность классов в качестве опциональных свойств окажется многоуровневой. Для демонстрации этого создадим новый объектный тип, описывающий сущность «комната», и сделаем на него ссылку в классе Residence (листинг 31.3).

Листинг 31.3

struct Room {

    let square: Int

}

class Residence {

    var rooms:[Room]?

}

class Person {

    var residence: Residence?

}

//создаем объект Персона

var man = Person()

if let residence = man.residence {

    if let rooms = residence.rooms {

        for oneRoom in rooms {

            print("Есть комната площадью \(oneRoom.square)")

        }

    }else{

        // нет комнат

    }

}else{

    // нет резиденции

}

ПРИМЕЧАНИЕ Обратите внимание, что свойство rooms типа Residence теперь является опциональной коллекцией.

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

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

Продемонстрируем использование опциональных цепочек (лис­тинг 31.4)

Листинг 31.4

//создаем объект комната

let room = Room(square: 10)

//создаем объект место проживания

var residence = Residence()

//добавляем в него комнату

residence.rooms = [room]

//создаем объект Персона

var man = Person()

//добавляем в него резиденцию

man.residence = residence

if let rooms = man.residence?.rooms {

    for oneRoom in rooms {

        print("Есть комната площадью \(oneRoom.square)")

    }

}else{

    // нет резиденции или комнат

}

Консоль

Есть комната площадью 10

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

Вы можете использовать опциональные цепочки для вызова свойств, методов и сабскриптов для любого уровня вложенности типов друг в друга. Это позволяет «пробираться» через подсвойства внутри сложных моделей вложенных типов и проверять возможность доступа к свойствам, методам и сабскриптам этих подсвойств. К примеру, если бы у типа Residence было свойство kitchen: Room?, то вы могли бы осуществить доступ к свойству square следующим образом:

man.residence?.kitchen?.square

31.2. Установка значений через опциональные цепочки

Использование опциональных цепочек не ограничивается получением свойств и вызовом сабскриптов и методов. Они также могут быть использованы и для установки значений вложенных свойств.

Вернемся к примеру с жилищем человека. Пусть нам необходимо изменить значение свойства rooms. Мы можем сделать это с помощью опциональных цепочек (листинг 31.5)

Листинг 31.5

let room1 = Room(square: 15)

let room2 = Room(square: 25)

man.residence?.rooms = [room1, room2]

Для решения поставленной задачи используется опциональная цепочка man.residence?.rooms, указывающая на требуемый элемент. Если на каком-то из этапов экземпляр будет отсутствовать, программа не вызовет ошибку, а лишь не выполнит данное выражение.

31.3. Доступ к методам через опциональные цепочки

Как отмечалось ранее, опциональные цепочки могут быть использованы не только для доступа к свойствам, но и для вызова методов. В класс Residence добавим новый метод, который должен обеспечивать вывод информации о количестве комнат (листинг 31.6).

Листинг 31.6

class Residence {

    var rooms:[Room]?

    func roomsCount()->Int {

        if let rooms = self.rooms {

            return rooms.count

        }else{

            return 0

        }

    }

}

Для вызова данного метода также можно воспользоваться опциональной последовательностью (листинг 31.7).

Листинг 31.7

man.residence?.roomsCount()

Назад: 30. Удаление экземпляров и ARC
Дальше: 32. Расширения