Swift self что это
Self and self in Swift
Here’s what we’ll discuss:
“self” as an Object in Swift
Let’s start with the most simple and most common occurrence of self in Swift. It’s the “self” that’s written with in lowercase, and it’s generally followed by a dot and a property or function name.
This self is a reference to the current object (“instance”) of a class (or struct), within that class. You can work with self as if it’s a variable or object within the class (or struct), that is the class (or struct). It’s an essential concept of Object-Oriented Programming (OOP).
What’s going on in the above code?
Explicit “self”
But there’s more… Within the Teapot struct, self refers to the current instance of a Teapot object. In the above code, self is identical to the potts constant on the last line. In fact, self is identical to any object as seen from the perspective of within the struct or class.
Implicit “self”
In the previous example we’ve used self explicitly to set a property from inside the struct. In Swift, the use of self is often implicit though. Check out the following example:
When Do You Use “self” in Swift?
When do you use self in Swift?
A counter-intuitive way to work with self is inside extensions. Check this out:
The above isPalindrome() function returns true when a String is a palindrome, i.e. it reads the same left-to-right as right-to-left. The function is part of an extension, which means the function is “tacked on” the already present String type. The self inside the function refers, of course, to a String object as seen from within the String struct.
For now though, let’s work with the idea that self within a class or struct refers to the current instance of that class (or struct) from within the class (or struct). It literally refers to the “self”, seen from within. Awesome!
Note: Us humans, we often think that we are our thoughts. When a thought like “I’m not good at this” pops up in your mind, you may believe that thought, identify with it, and see it as “true”. Sounds familiar? Then give this exercise a try:
Now, ask yourself: Who’s doing the watching? It’s you, watching yourself and your thoughts. If that’s true, then what is “me”, “I” and “my thoughts”? Your thoughts may not be as solid as you think. This may lead you to believe that you are not your thoughts, which I think is true. You can watch your thoughts, even watch how you respond to them, and that could give you a sense of clarity, confidence and calm. This exercise is called mindfulness meditation.
“Self” as a Type in Swift
In Swift, Self refers to a type – usually the current type in the current context. Just as lowercase self can mean any current object, uppercase Self can mean just about any current type. It’s intriguing!
“Self” in Classes and Structs
Let’s start with a simple example:
We’re working with the same Teapot struct as before, so it has a property canTalk and a function poke() that makes it talk (or not).
Take a look at the return type of the createTeapots() function. It’s [ Self ] ; an array of Self objects. This is Self-as-a-type. We don’t have a Self class or struct in our code. And frankly, the code compiles OK, so what’s the concrete return type of the function!?
“Self” in Protocols and Extensions
But what if you’re in a context where the concrete type you’re using is not that clear? For example with generics, protocols and extensions. They can refer to a multitude of types.
Check out the following example:
This extension for Numeric is made for a protocol type, which means that the squared() function is added to any type that conforms to the Numeric protocol. As such, we don’t know what type we’ll be multiplying within the squared() function. That’s self-evident for the self * self code (pun intended), but what should be the return type of the squared() function?
OK, let’s look at another example. It covers the same principles as before, but it’s a bit more complex. Check it out:
The squareAll() function will calculate the power of 2 for all numbers in the sequence, regardless of whether those numbers are integers, doubles or belong to a range. The return value of the function is an array of… an array of what, exactly?
Further Reading
In this tutorial, we’ve discussed what self and Self are in Swift and how you can use them. Here’s a quick summary:
Want to learn more? Check out these resources:
Тот факт, что структуры и перечисления могут определять методы в Swift, является основным отличием от C и Objective-C. В Objective-C классы являются единственными типами, которые могут определять методы. В Swift вы можете выбрать, определять ли класс, структуру или перечисление, и при этом иметь возможность определять методы для создаваемого вами типа.
Методы экземпляра
Вы пишете метод экземпляра в открывающей и закрывающей скобках того типа, к которому он принадлежит. Метод экземпляра имеет неявный доступ ко всем другим методам экземпляра и свойствам этого типа. Метод экземпляра может быть вызван только для конкретного экземпляра того типа, к которому он принадлежит. Его нельзя вызвать изолированно без существующего экземпляра.
Вот пример, который определяет простой Counter класс, который может использоваться для подсчета количества раз, когда действие происходит:
Counter Класс определяет три метода экземпляра:
Вы вызываете методы экземпляра с тем же точечным синтаксисом, что и у свойств:
Собственная собственность
increment() Метод в приведенном выше примере мог бы быть написана так:
Основное исключение из этого правила возникает, когда имя параметра для метода экземпляра имеет то же имя, что и свойство этого экземпляра. В этой ситуации имя параметра имеет приоритет, и становится необходимым ссылаться на свойство более квалифицированным способом. Вы используете self свойство, чтобы различать имя параметра и имя свойства.
Здесь self устраняется неоднозначность между вызываемым параметром метода x и свойством экземпляра, которое также вызывается x :
Изменение типов значений из методов экземпляра
Тем не менее, если вам нужно изменить свойства вашей структуры или перечисления в конкретном методе, вы можете выбрать мутацию поведения для этого метода. Затем метод может видоизменять (то есть изменять) свои свойства изнутри метода, и любые изменения, которые он вносит, записываются обратно в исходную структуру после завершения метода. Метод также может назначить совершенно новый экземпляр своему неявному self свойству, и этот новый экземпляр заменит существующий, когда метод завершится.
Вы можете включить это поведение, поместив mutating ключевое слово перед func ключевым словом для этого метода:
Обратите внимание, что вы не можете вызвать метод мутации для константы типа структуры, потому что ее свойства не могут быть изменены, даже если они являются переменными свойствами, как описано в разделе Хранимые свойства экземпляров константной структуры :
Присвоение себе внутри метода мутации
Эта версия moveBy(x:y:) метода мутации создает новую структуру, чьи значения x и y значения установлены в целевом местоположении. Конечный результат вызова этой альтернативной версии метода будет точно таким же, как и при вызове более ранней версии.
Методы мутации для перечислений могут установить неявный self параметр, который будет отличаться от того же перечисления:
Методы типа
В Objective-C вы можете определять методы уровня типа только для классов Objective-C. В Swift вы можете определить методы уровня типа для всех классов, структур и перечислений. Каждый метод типа явно ограничен типом, который он поддерживает.
Методы типа вызываются с точечным синтаксисом, как методы экземпляра. Однако вы вызываете методы типа для типа, а не для экземпляра этого типа. Вот как вы вызываете метод типа для класса с именем SomeClass :
В теле метода типа неявное self свойство ссылается на сам тип, а не на экземпляр этого типа. Это означает, что вы можете использовать self для устранения неоднозначности между свойствами типа и параметрами метода типа, так же, как вы делаете для свойств экземпляра и параметров метода экземпляра.
В более общем смысле любые неквалифицированные имена методов и свойств, которые вы используете в теле метода типа, будут ссылаться на другие методы и свойства уровня типа. Метод типа может вызывать другой метод типа с именем другого метода без необходимости ставить перед ним префикс имени типа. Точно так же методы типа в структурах и перечислениях могут обращаться к свойствам типа, используя имя свойства типа без префикса имени типа.
В дополнение к его свойству type и методам type, он LevelTracker отслеживает прогресс отдельных игроков в игре. Он использует свойство экземпляра, вызываемое currentLevel для отслеживания уровня, который в данный момент играет игрок.
LevelTracker Структура используется с Player классом, как показано ниже, чтобы отслеживать и обновлять ход отдельного плеера:
Вы можете создать экземпляр Player класса для нового игрока и посмотреть, что произойдет, когда игрок завершит первый уровень:
Если вы создаете второго игрока, которого пытаетесь перейти на уровень, который еще не разблокирован ни одним игроком в игре, попытка установить текущий уровень игрока завершится неудачно:
What is «self» used for in Swift?
I am new to Swift and I’m wondering what self is used for and why.
I have seen it in classes and structures but I really don’t find them essential nor necessary to even mention them in my code. What are they used for and why? In what situations it’s necessary to use it?
I have been reading lots of questions and answers for this question but none of them fully answers my questions and they always tend to compare it with this as in Java, with which I’m not familiar whatsoever.
9 Answers 9
Yes it is the same as this in Java and self in Objective-C, but with Swift, self is only required when you call a property or method from a closure or to differentiate property names inside your code, such as initializers. So you can use almost all of your class components safely without using self unless you are making the call from a closure.
The increment() method in the example above could have been written like this:
The main exception to this rule occurs when a parameter name for an instance method has the same name as a property of that instance. In this situation, the parameter name takes precedence, and it becomes necessary to refer to the property in a more qualified way. You use the self property to distinguish between the parameter name and the property name.
Here, self disambiguates between a method parameter called x and an instance property that is also called x :”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 2 Prerelease).”
This is how Ray Wenderlich recommends the use of self in Swift for their tutorials:
Use of Self
For conciseness, avoid using self since Swift does not require it to access an object’s properties or invoke its methods.
Use self when required to differentiate between property names and arguments in initializers, and when referencing properties in closure expressions as required by the compiler:
And this is GitHub’s recommendations on self for their applications:
Only explicitly refer to self when required
Only include the explicit keyword when required by the language — for example, in a closure, or when parameter names conflict:
Rationale: This makes the capturing semantics of self stand out more in closures, and avoids verbosity elsewhere.
How to Use Correctly ‘self’ Keyword in Swift
In Swift self is a special property of an instance that holds the instance itself. Most of the times self appears in an initializer or method of a class, structure or enumeration.
The motto favor clarity over brevity is a valuable strategy to follow. It applies efficiently in most of the cases, and helps to increase the code readability, but. affects code shortness.
The answer is that Swift permits and even encourages you to omit self keyword when it can be done. The challenge is to determine the scenarios when self is obligatory and when optional.
Challenge accepted! Let’s dive into an interesting review of self keyword in Swift.
1. Access instance properties and methods
For example the following structure Weather uses self in its initializer and isDayForWalk() method:
The structure looks nice.
As mentioned in the introduction, Swift encourages you to omit the self keyword whenever possible.
In the previous example, it is recommended to remove self from isDayForWalk() method. This makes the method shorter:
Let’s make an experiment and still remove self from the initializer:
2. To be, or not to be
The obligatory usage of self brings the benefits of consistency and favors clarity over brevity. You can clearly see the difference between the instance properties (that are prefixed with self. ) from locally defined variables. Maybe.
In my opinion, when you have troubles to distinguish instance properties from local variables within a method: you have a different, deeper problem.
When a structure or class has an excessive number of properties (so called God object), you’re probably breaking the Single responsibility principle.
If a method uses a big number of these properties and declares correspondingly many local variables, then such method does too many things. Using an explicit self to distinguish somehow properties from variables is a temporary workaround for a bad code.
It should not be that way.
Now imagine a well designed structure or class, which has a single responsibility. It contains only strictly necessary properties. A well written method is performing one determined action and as result its code is simple and obvious.
Surely you don’t have the problem to distinguish local variables from instance properties in such a method. The usage of self may be even discouraged, since your adding to obvious code redundant explanations.
So design your classes and structures well, and don’t let the methods grow to thousands of lines of code. Then you can omit self without difficulties, and your code becomes even more expressive and concise.
3. Access type properties and methods
self refers to a type (rather than to an instance) when used in a type method.
Interestingly that you can access type properties using two additional forms.
Secondly you can indicate the type Const explicitly when accessing the properties:
Const.minLimit is my personal preference when accessing type properties. Const serves as a namespace that groups constants.
I find great that Swift allows 3 ways to access static properties. Use the one you like!
4. Access instance properties and methods in a closure
A closure is a block of code that can be referenced, passed around and invoked when necessary. A closure has access to variables from the environment where it was defined, also called the closure scope.
Sometimes you need to define a closure in a method. The closure can access the local method variables. What’s more important you have to explicitly write self to access instance properties or methods within the closure.
Let’s define a closure inside a method:
Collection is a class that holds some prime numbers.
The method getAppendClosure() returns a closure that when invoked appends a new number to the collection. To access numbers property within the closure you have to explicitly use self keyword: < self.numbers.append($0) >.
To create a strong reference cycle, the common scenario is when a property of an instance is a closure that captures the instance itself. Let’s see a sample:
The strong reference cycle creates the problem.
The instance holds the closure reference leader.sayMyName and simultaneously the closure captures and holds the instance reference < return self.name >. Under such circumstances the reference cycle cannot be broken and as result both leader instance and the closure cannot be deallocated.
The solution is to define inside the closure a capture list and mark self as an unowned reference. Let’s fix the previous example:
Notice that [unowned self] is added in the closure, which marks self as an unowned reference. Now the closure does not keep a strong reference to self instance. The strong reference cycle between instance and closure is no longer created.
I recommend to read more about instances lifetime at Unowned or Weak? Lifetime and Performance.
Important. When you access self in a closure, you should always verify whether a strong reference cycle is not created.
4. Individual self usage
Of course there are plenty of situations when you need to return or modify self directly. Let’s enumerate the common scenarios.
When working with classes, you might want to implement a method chaining. Such practice is useful to chain multiple method calls on the same instance, without storing the intermediate results.
The following example is implementing a simple Stack data structure. You can push or pop elements in the stack. Let’s take a look:
To find what case holds the enumeration within its method, you can easily query self property with a switch statement.
For example, let’s get a string message that describes the enumeration cases:
The method getOccupation() accesses self to determine the current enumeration case.
New structure instance
In a structure you can dynamically modify the current instance by assigning to self a new value:
In the mutating method stop() the assignment self = Movement(speed: 0) modifies the current instance to a new one.
self is a property on the instance that refers to itself. It’s used to access class, structure and enumeration instance within methods.
When self is accessed in a type method ( static func or class func ), it refers to the actual type (rather than an instance).
Swift allows to omit self when you want to access instances properties. My advice is to rely on shortness and skip self whenever possible.
When a method parameter have the same name as instance property, you have to explicitly use self.myVariable = myVariable to make a distinction.
Notice that method parameters have a priority over instance properties.
Do you think self should be omitted or kept? Feel free to write your opinion in the comments section below!
Swift Features
Generic протоколы
Под этим термином я подразумеваю любые протоколы, в которых есть открытые typealias (associatedtype в Swift 2.2). В моем первом приложении на Swift было два таких протокола: (для примера я немного упростил их)
DataObservable отвечает за отслеживание изменения данных. При этом не важно, где эти данные хранятся (на сервере, локально или еще как). DataObserver получает оповещения о том, что данные изменились. В первую очередь нас будет интересовать протокол DataObservable, и вот его простейшая реализация.
Тут все просто: сохраняем ссылку на последний observer, и вызываем у него метод didDataChangedNotification, когда данные по какой-то причине изменяются. Но погодите… этот код не компилируется. Компилятор выдает ошибку «Protocol ‘DataObserver’ can only be used as a generic constraint because it has Self or associated type requirements». Все потому, что generic-протоколы могут использоваться только для накладывания ограничений на generic-параметры. Т.е. объявить переменную типа DataObserver не получится. Меня такое положение дел не устроило. Немного покопавшись в сети, я нашел решение, которое помогает разобраться со сложившейся проблемой, и имя ему Type Erasure.
Это паттерн, который представляет собой небольшой обертку над заданным протоколом. Для начала введем новый класс AnyDataObserver, который реализует протокол DataObserver.
Тело метода didDataChangedNotification пока оставим пустым. Идем дальше. Вводим в класс generic init (для чего он нужен расскажу чуть ниже):
В него передается параметр sourceObserver типа TObserver. Видно, что на TObserver накладываются ограничения: во-первых он должен реализовать протокол DataObserver, во-вторых его DataType должен в точности соответствовать DataType нашего класса. Собственно sourceObserver это и есть исходный observer-объект, который мы хотим обернуть. И наконец финальный код класса:
Собственно тут и происходит вся «магия». В класс добавляется закрытое поле observerHandler, в котором хранится реализация метода didDataChangedNotification объекта sourceObserver. В самом методе didDataChangedNotification нашего класса мы просто вызываем эту реализацию.
Теперь перепишем SimpleDataObservable:
Теперь код компилируется и прекрасно работает. Могу отметить, что некоторые классы из стандартной библиотеки Swift работают по схожему принципу (например AnySequence).
Тип Self
В определенный момент мне потребовалось ввести в проект протокол копирования:
Но что же должен возвращать метод copy? Any? CopyableType? Тогда при каждом вызове пришлось бы писать let copyObject = someObject.copy as! SomeClass, что не очень хорошо. В добавок к тому же этот код небезопасен. На помощь приходит ключевое слово Self.
Таким образом мы сообщаем компилятору, что реализация этого метода обязана вернуть объект того же типа, что и объект, для которого он был вызван. Тут можно провести аналогию с instancetype из Objective-C.
Рассмотрим реализацию этого протокола:
Для создание нового экземпляра используется ключевое слово dynamicType. Оно служит для получения ссылки на объект-тип (все это напоминает метод class из Objective-C). После получения объекта-типа, у него вызывается init (для гарантии того, что init без параметров действительно есть в классе, мы вводим его с ключевым словом required). После чего копируем в созданный экземпляр все нужные поля и возвращаем его из нашей функции.
Как только я закончил с копированием, возникла необходимость использовать Self еще в одном месте. Мне потребовалось написать протокол для View Controller, в котором бы был статический метод создания нового экземпляра этого самого View Controller.
Так как этот протокол никак напрямую не был связан с классом UIViewController, то я его сделал достаточно общим и назвал AutofactoryType:
Попробуем использовать его для создания View Conotroller:
Все бы хорошо, но этот код не скомпилируется: “Cannot convert return expression of type ViewController to return type ‘Self’” Дело в том, что компилятор не может преобразовать ViewController к Self. В данном случае ViewController и Self — это одно и то же, но в общем случае это не так (например, при использовании наследования).
Как же заставить этот код работать? Для этого есть не совсем честный (по отношению к строгой типизации), но вполне рабочий способ. Добавим функцию:
Ее назначение — это преобразование объекта одного типа к другому типу. Если преобразование не удается, то функция просто завершается с ошибкой.
Используем эту функцию в createInstance:
Благодаря автоматическому выводу типов, newInstance теперь преобразуется к Self (чего нельзя было сделать напрямую). Этот код компилируется и работает.
Специфичные расширения
Расширения типов в Swift не были бы такими полезными, если бы нельзя было писать специфичный код для разных типов. Возьмем, к примеру, протокол SequenceType из стандартной библиотеки и напишем для него такое расширение:
В расширении введено ограничение на элемент последовательности, он должен быть типа String. Таким образом для любой последовательности, состоящей из строк (и только для них), можно будет вызвать функцию concat.
Это позволяет значительную часть кода выносить в расширения, и вызывать его в нужном контексте, получая при этом все плюсы повторного использования.
Реализация методов протокола по умолчанию.
Реализация методов протокола по умолчанию.
Как следует из описания, любой тип реализующий этот протокол, должен обладать уникальным идентификатором uniqueId типа String. Но если немного подумать, то становится понятно, что в рамках одного модуля для любого типа уникальным идентификатором является его название. Так давайте напишем расширение для нашего нового протокола:
В данном случае ключевое слово Self используется для того, чтобы накладывать ограничения на объект-тип. Логика этого кода примерно следующая: «если этот протокол будет реализован классом UIViewController (или его наследником), то можно использовать следующую реализацию uniqueId». Это и есть реализация протокола по-умолчанию. На самом деле можно написать это расширение и без каких-либо ограничений:
И тогда все типы, реализующие UniqueIdentifierProvider, получат uniqueId “из коробки”.
Прелесть в том, что в классе может быть своя реализация этого метода. И в этом случае реализация по-умолчанию будет игнорироваться:
Явное указание Generic аргумента
В своем проекте я использовал MVVM, и за создание ViewModel отвечал метод:
Соответственно, так он использовался:
В данном случае в функцию createViewModel в качестве generic аргумента будет поставляться MyViewModel. Все благодаря тому, что Swift сам выводит типы из контекста. Но всегда ли это хорошо? На мой взгляд, это не так. В некоторых случаях может даже привести к ошибкам:
В первом case в метод createViewModel подставляется NormalViewModel.
Во втором мы забыли написать «as PreviewViewModel», из-за чего в метод createViewModel подставляется тип ViewModelBase (что в лучшем случае приведет к ошибке в runtime).
Значит, необходимо сделать указание типа явным. Для этого в createViewModel мы добавим новый параметр viewModelType типа TViewModel.Type. Type тут означает, что метод принимает в качестве параметра не экземпляр типа, а сам объект-тип.
После этого наш switch-case выглядит так:
Теперь В функцию createViewModel передается аргументы NormalViewModel.self и PreviewViewModel.self. Это объекты-типы NormalViewModel и PreviewViewModel. В Swift есть довольно странная особенность: если у функции один параметр, можно не писать self.
Но если аргументов два или больше, ключевое слово self необходимо.