Tehnografi.com - Технологические новости, обзоры и советы

Я и я в Swift

Следующая статья поможет вам: Я и я в Swift

Вернуться в блог

В Swift «self» обычно относится к текущему объекту внутри класса или структуры. Однако у нас есть больше «я»: «я», «Я» и даже «.self». Что все это значит? Приготовьтесь к небольшому самоанализу и метапрограммированию — мы собираемся открыть «Я» в Swift. Не волнуйтесь, здесь нет ничего нового в стиле Нью Эйдж!

Вот что мы обсудим:

  • Что такое self и Self в Swift
  • Как использовать self при работе с классами или замыканиями
  • Когда вам нужно использовать self, а когда это неявно
  • Что имеет в виду Self при работе с протоколами и расширениями
  • Как «Я» часто является заполнителем чего-то другого
  • Зачем нужен Self, если не знаешь конкретный тип

Начнем с самого простого и распространенного случая self в Swift. Это «я», написанное строчными буквами, за которым обычно следует точка и имя свойства или функции.

собственный возраст = 42
self.walk()

Это self является ссылкой на текущий объект («экземпляр») класса (или структуры) внутри этого класса. Вы можете работать с самим собой, как будто это переменная или объект внутри класса (или структуры), то есть класса (или структуры). Это важная концепция объектно-ориентированного программирования (ООП).

Проверь это:

структура чайник
{
вар canTalk:Bool

init(canTalk: Bool) {
self.canTalk = canTalk
}
}

let potts = Чайник (canTalk: true)

Что происходит в приведенном выше коде?

Мы создали структуру Teapot с одним свойством canTalk типа Bool. Структура имеет инициализатор, который принимает один параметр с именем canTalk, назначенный свойству canTalk структуры.

Явное «я»

Внутри инициализатора init(canTalk:) мы используем ключевое слово self. Мы берем константу canTalk, которая предоставляется в качестве параметра функции, и присваиваем ее значение свойству canTalk структуры.

Но это еще не все… Внутри структуры Teapot self ссылается на текущий экземпляр объекта Teapot. В приведенном выше коде self идентично константе Поттса в последней строке. Фактически, self идентично любому объекту, если смотреть с точки зрения структуры или класса.

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

Давайте проведем небольшую аналогию: когда мы с вами говорим о моих забавных ушах, вам придется сказать «Смешные уши Рейндера», а я могу сказать «Мои забавные уши». Я говорю о себе, т.е. self.ears, тогда как вы говорите о reinder.ears. В этом сценарии self === reinder, но в контексте человека self.ears может быть ушами Боба (или кого-либо еще) с точки зрения Боба изнутри.

Неявное «я»

В предыдущем примере мы явно использовали self для установки свойства внутри структуры. Однако в Swift использование self часто неявно. Посмотрите следующий пример:

структура чайник
{
вар canTalk:Bool

функция тыкать () {
если canTalk == true {
print(“Я ЧАЙНИК!!”)
}
}
}

let potts = Чайник (canTalk: true)
поттс.poke()

Это болтливый чайник! Когда тыкаешь в него, он кричит: Я ЧАЙНИК!! если canTalk верно.

Но подождите… Почему мы не используем self.canTalk == true в условном выражении? Это потому, что self, то есть ссылка на текущий объект Teapot, является неявной. Swift знает, что мы имеем в виду свойство canTalk текущего объекта, и это экономит нам время на кодирование, делая это предположение.

Когда вы используете «self» в Swift?

Когда вы используете self в Swift?

  • Если вы работаете со свойством выхода в контроллере представления, например self.usernameLabel?.text = (хотя вы часто можете опустить self.!)
  • Если вы работаете со свойством и возникает конфликт имен, вы используете self.canTalk, чтобы явно сообщить Swift, что вы ссылаетесь на свойство.
  • Если вы работаете с замыканиями и хотите захватить ссылку на текущий экземпляр класса, а замыкание экранируется (Swift 5.3+), вы также будете явно использовать self.
  • Начиная с Swift 5.3, особенно в SwiftUI, вам не нужно явно ссылаться на self при его захвате в замыкании, если это тип значения, например структура.

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

Противоинтуитивный способ работы с самим собой — внутренние расширения. Проверь это:

расширение Строка {
func isPalindrome() -> Bool {
return self == String(self.reversed())
}
}

Вышеуказанная функция isPalindrome() возвращает true, когда строка является палиндромом, т.е. она читается слева направо так же, как и справа налево. Функция является частью расширения, что означает, что функция «прикреплена» к уже существующему типу String. Self внутри функции, конечно, относится к объекту String, как видно из структуры String.

Что противоречит здравому смыслу в этом подходе, так это то, что с точки зрения программиста еще нет никаких строк. Вы работаете внутри структуры; внутри расширения. Возможно, вам придется начать писать функцию типа isPalindrome(string:) с параметром и сначала без использования self. Однако вы можете использовать саму строку с self.

А пока давайте поработаем с идеей, что self внутри класса или структуры ссылается на текущий экземпляр этого класса (или структуры) внутри класса (или структуры). Это буквально относится к «я», увиденному изнутри. Потрясающий!

Note: Мы, люди, часто думаем, что мы — это наши мысли. Когда в вашем уме возникает такая мысль, как «У меня это плохо получается», вы можете поверить в эту мысль, отождествить себя с ней и увидеть в ней «правду». Звучит знакомо? Тогда попробуйте это упражнение:

  1. Сядь в какое-нибудь тихое место и закрой глаза.
  2. Держите глаза закрытыми какое-то время – и тогда в вашем сознании всплывает мысль.
  3. Наблюдайте, как мысль пролетает, как облако по небу или лист по реке.
  4. Потренируйтесь немного наблюдать за мыслями и посмотрите, сможете ли вы отпустить их, когда они пролетели или проплыли мимо.
  5. Открой глаза снова, когда будешь готов

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

В предыдущем разделе мы рассмотрели строчную букву self, которая ссылается на объект. Далее мы собираемся обсудить «Я», которое относится к типу. Это «Я» имеет заглавную букву «S», поэтому вы можете отличить его от строчной буквы «Я».

В Swift Self относится к типу — обычно к текущему типу в текущем контексте. Точно так же, как строчная self может означать любой текущий объект, прописная Self может означать практически любой текущий тип. Это интригующе!

«Я» в классах и структурах

Начнем с простого примера:

структура чайник
{

статическая функция createTeapots(count: Int, canTalk: Bool) -> [Self] {
возврат (0..Мета Note: Вы можете ссылаться на Teapot.self, т.е. на некоторый class.self, который является самим типом. Это называется метатипом; тип типа. Тип Teapot.self — Teapot.Type. Вы можете рассматривать Teapot.self как ссылку или значение самого типа, которое вы можете передавать в своем коде. Примером является tableView.register(UITableViewCell.self, forCellReuseIdentifier: ) для табличных представлений. Вы указываете, что хотите зарегистрировать сам тип в табличном представлении, которым является UITableViewCell.self. (Если вы хотите создать ссылку на свойство, проверьте ключевые пути.)

«Я» в протоколах и расширениях

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

Посмотрите следующий пример:

расширение Числовое {
func Squared() -> Self {
вернуть себя * себя
}
}

пусть число = 42
print(number.squared()) // 1764

Приведенный выше код определяет расширение протокола для Numeric, который является протоколом, которому соответствуют все «числовые» типы, такие как Double и Int. Функция Squared() возвращает степень 2 заданного числа; 1764 для 42 в приведенном выше примере.

Это расширение Numeric создано для типа протокола. Это означает, что функция Squared() добавляется к любому типу, соответствующему Numeric-протоколу. Таким образом, мы не знаем, какой тип мы будем умножать в функции Squared(). Это самоочевидно для кода self * self (каламбур), но каким должен быть тип возвращаемого значения функции Squared()?

Это Self, с заглавной буквой «S». В этом контексте Self относится к типу, который соответствует числовому протоколу. В этом примере Self будет конкретным типом Int, поскольку 42 — целое число. При вызове Squared() для Double Self будет Double. Мы не можем здесь жестко запрограммировать конкретный тип из-за расширения протокола. Это «Я-как-тип»!

Хорошо, давайте посмотрим на другой пример. Он охватывает те же принципы, что и предыдущий, но немного сложнее. Проверьте это:

Последовательность расширения, где Элемент: Числовой {
функция SquareAll() -> [Self.Element] {
вернуть карту { $0 * $0 }
}
}

print((0…10).squareAll())
// Выход: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Приведенный выше код определяет расширение для Sequence, которое представляет собой протокол низкого уровня для коллекций, массивов, словарей, диапазонов и т. д. Ключевое словоwhere используется для ограничения этого расширения для типов, где элемент соответствует числовому. Вы можете использовать SquareAll() с любой последовательностью или набором чисел.

В протоколе Sequence Element является ассоциированным типом. Вы можете рассматривать это как общий заполнитель для протокола. Когда протокол используется в конкретном типе, таком как Array, Element будет типом элементов массива.

Функция SquareAll() вычислит степень 2 для всех чисел в последовательности, независимо от того, являются ли эти числа целыми, двойными или принадлежат диапазону. Возвращаемое значение функции — это массив… массив чего именно?

Давайте сосредоточимся на типе возвращаемого значения функции SquareAll(), который [Self.Element]. Мы хотим что-то вернуть, но не знаем что. Мы объявили расширение протокола, а это означает, что «входными данными» для SquareAll() может быть любая последовательность, элементы которой соответствуют числовому типу.

Вы не можете объявить Self в качестве возвращаемого типа, потому что это тип последовательности. Кроме того, функция map(_:) будет возвращать массив. Если вы сопоставите диапазон, как мы это делаем, вы получите обратно массив. Итак, SquareAll() -> Self или -> [Self] не будет работать.

Однако мы можем использовать Self.Element. В последовательности элемент — это тип элементов последовательности. В диапазоне 0…10 элемент соответствует целым числам. В массиве двойных значений Sequence — это протокол, Self — это массив, а Self.Element — это Double.

Мы на 100% уверены, что map(_:) возвращает массив, нам нужно только знать тип элементов в массиве. Мы не можем быть на 100% тем, что это такое, потому что последовательность, в которой элемент является числовым, может быть очень многим. К счастью, мы можем использовать заполнитель или «подтип» Self.Element. Аккуратный!

В коде, который мы обсуждали в этом разделе, используются дженерики и заполнители. Заполнители — это именно то, что они заменяются конкретным типом, когда Swift компилирует ваш код. Например, когда вы используете SquareAll() с массивом двойных чисел: [Self.Element] становится конкретным типом [Double]. Swift скомпилирует дополнительные варианты этой функции в зависимости от типов, которые вы используете в своем коде. Основная причина, по которой мы используем эти заполнители, заключается в том, что они позволяют создавать функцию SquareAll() для десятков типов: массивов, словарей, диапазонов и т. д. Этот синтаксис делает Swift мощным, хотя поначалу это сбивает с толку.

В этом руководстве по разработке приложений мы обсудили, что такое self и Self в Swift и как их можно использовать. Вот краткое резюме:

  • self (строчная буква «s») относится к текущему объекту класса или структуры внутри этого класса или структуры — представьте себе self.funnyEars изнутри или bob.funnyEars снаружи.
  • Self (заглавная буква «S») относится к текущему типу с контекстом и часто является заполнителем для другого типа — представьте себе Self.Element в массиве целых чисел.