惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threatpost
博客园 - 叶小钗
T
The Blog of Author Tim Ferriss
Recent Announcements
Recent Announcements
D
DataBreaches.Net
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
罗磊的独立博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
N
Netflix TechBlog - Medium
Microsoft Azure Blog
Microsoft Azure Blog
Microsoft Security Blog
Microsoft Security Blog
B
Blog
U
Unit 42
有赞技术团队
有赞技术团队
博客园 - 聂微东
GbyAI
GbyAI
宝玉的分享
宝玉的分享
F
Full Disclosure
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
MyScale Blog
MyScale Blog
Jina AI
Jina AI
Martin Fowler
Martin Fowler
IT之家
IT之家
酷 壳 – CoolShell
酷 壳 – CoolShell
D
Docker
P
Proofpoint News Feed
A
About on SuperTechFans
I
InfoQ
博客园 - 【当耐特】
C
Check Point Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Privacy & Cybersecurity Law Blog
T
Threat Research - Cisco Blogs
Y
Y Combinator Blog
Project Zero
Project Zero
WordPress大学
WordPress大学
小众软件
小众软件
AWS News Blog
AWS News Blog
博客园 - 司徒正美
T
The Exploit Database - CXSecurity.com
L
LINUX DO - 热门话题
I
Intezer
Engineering at Meta
Engineering at Meta
C
CXSECURITY Database RSS Feed - CXSecurity.com
J
Java Code Geeks
T
Tenable Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes

Dmitri Pavlutin Blog

Pure Functions in JavaScript: A Beginner's Guide Record Type in TypeScript: A Quick Intro How to Write Comments in React: The Good, the Bad and the Ugly 4 Ways to Create an Enum in JavaScript React forwardRef(): How to Pass Refs to Child Components TypeScript Function Types: A Beginner's Guide How to Use v-model to Access Input Values in Vue Mastering Vue refs: From Zero to Hero Environment Variables in JavaScript: process.env 5 Must-Know Differences Between ref() and reactive() in Vue How to Destructure Props in Vue (Composition API) Triangulation in Test-Driven Development How to Use nextTick() in Vue Programming to Interface Vs to Implementation A Smarter JavaScript Mapper: array.flatMap() Array Grouping in JavaScript: Object.groupBy() How to Access ES Module Metadata using import.meta JSON Modules in JavaScript How to Trim Strings in JavaScript TypeScript Function Overloading How to Debounce and Throttle Callbacks in Vue How to Show/Hide Elements in Vue Sparse vs Dense Arrays in JavaScript How to Fill an Array with Initial Values in JavaScript Covariance and Contravariance in TypeScript What are Higher-Order Functions in JavaScript? How to Use TypeScript with React Components Index Signatures in TypeScript How to Use React useReducer() Hook unknown vs any in TypeScript A Guide to React Context and useContext() Hook How to Use Promise.any() 2 Ways to Remove a Property from an Object in JavaScript 'return await promise' vs 'return promise' in JavaScript How to Use Promise.allSettled() How to Use fetch() with JSON JavaScript Promises: then(f,f) vs then(f).catch(f) What is a Promise in JavaScript? How to Use Promise.all() A Simple Guide to Component Props in React Don't Stop Me Now: How to Use React useTransition() hook A Simple Explanation of JavaScript Variables: const, let, var ES Modules Dynamic Import How to Memoize with React.useMemo() How to Cleanup Async Effects in React Why Math.max() Without Arguments Returns -Infinity How to Debounce and Throttle Callbacks in React Don't Confuse Function Expressions and Function Declarations in JavaScript How to Use ES Modules in Node.js Solving a Mystery Behavior of parseInt() in JavaScript How to Use Array Reduce Method in JavaScript 3 Ways to Merge Arrays in JavaScript A Guide to Jotai: the Minimalist React State Management Library The Difference Between Values and References in JavaScript How to Implement a Queue in JavaScript A Helpful Algorithm to Determine "this" value in JavaScript React useRef() Hook Explained in 3 Steps 7 Interview Questions on "this" keyword in JavaScript. Can You Answer Them? How to Greatly Enhance fetch() with the Decorator Pattern 7 Interview Questions on JavaScript Closures. Can You Answer Them? What's a Method in JavaScript? array.sort() Does Not Simply Sort Numbers in JavaScript How to Solve the Infinite Loop of React.useEffect() The New Array Method You'll Enjoy: array.at(index) What's the Difference between DOM Node and Element? Why Promises Are Faster Than setTimeout()? Everything About Callback Functions in JavaScript How React Updates State 5 Mistakes to Avoid When Using React Hooks 5 Best Practices to Write Quality JavaScript Variables Type checking in JavaScript: typeof and instanceof operators 3 Ways to Check if a Variable is Defined in JavaScript React Forms Tutorial: Access Input Values, Validate, Submit Forms Prototypal Inheritance in JavaScript How to Timeout a fetch() Request How to Learn JavaScript If You're a Beginner A Simple Explanation of React.useEffect() A Simple Explanation of JavaScript Iterators How to Use React Controlled Inputs Everything about null in JavaScript How to Use Fetch with async/await Getting Started with Arrow Functions in JavaScript An Interesting Explanation of async/await in JavaScript Front-end Architecture: Stable and Volatile Dependencies Is it Safe to Compare JavaScript Strings? How to Access Object's Keys, Values, and Entries in JavaScript What Actually is a String in JavaScript? 3 Ways to Shallow Clone Objects in JavaScript (w/ bonuses) Checking if an Array Contains a Value in JavaScript JavaScript Event Delegation: A Beginner's Guide How to Parse URL in JavaScript: hostname, pathname, query, hash 3 Ways to Detect an Array in JavaScript How to Get the Screen, Window, and Web Page Sizes in JavaScript 3 Ways to Check If an Object Has a Property/Key in JavaScript How to Compare Objects in JavaScript Object.is() vs Strict Equality Operator in JavaScript Own and Inherited Properties in JavaScript 5 Differences Between Arrow and Regular Functions How to Use Object Destructuring in JavaScript Your Guide to React.useCallback()
How to Use Correctly 'self' Keyword in Swift
Dmitri Pavlutin · 2016-11-09 · via Dmitri Pavlutin Blog

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.

Applying this motto to self, the following 2 opposites arise:

  • Should you apply the clarity strategy and keep always self to access instance properties self.property?
  • Or accessing the property from a method brings sufficient context to omit self from self.property without significant readability loss?

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:


struct Weather {

let windSpeed: Int // miles per hour

let chanceOfRain: Int // percent

init(windSpeed: Int, chanceOfRain: Int) {

self.windSpeed = windSpeed

self.chanceOfRain = chanceOfRain

}

func isDayForWalk() -> Bool {

let comfortableWindSpeed = 5

let acceptableChanceOfRain = 30

return self.windSpeed <= comfortableWindSpeed

&& self.chanceOfRain <= acceptableChanceOfRain

}

}

// A nice day for a walk

let niceWeather = Weather(windSpeed: 4, chanceOfRain: 25)

print(niceWeather.isDayForWalk()) // => true


self special property inside init(windSpeed:chanceOfRain:) and isDayForWalk() is the current instance of Weather structure. It allows to set and access the structure properties self.windSpeed and self.chanceOfRain.

The structure looks nice.

Nevertheless accessing every time self property may be excessive. The structure initializer and method provide enough context: everything happens inside the structure. Is it possible to get rid of self?

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:


struct Weather {

/* ... */

func isDayForWalk() -> Bool {

let comfortableWindSpeed = 5

let acceptableChanceOfRain = 30

return windSpeed <= comfortableWindSpeed

&& chanceOfRain <= acceptableChanceOfRain

}

}


Because isDayForWalk() method is always invoked in the context of Weather's instance, Swift enables access of windSpeed and chanceOfRain without self.

Inside the initializer init(windSpeed:chanceOfRain:) the parameters and structure properties have the same names windSpeed and chanceOfRain. Contrary to previous case, now you cannot remove self keyword, because it would create an ambiguity between the parameter and property names.

self helps making an explicit distinction between parameters (windSpeed and chanceOfRain used without self) and structure properties (self.windSpeed and self.chanceOfRain, accessed with self).

Let's make an experiment and still remove self from the initializer:


struct Weather {

/* ... */

init(windSpeed: Int, chanceOfRain: Int) {

windSpeed = windSpeed

chanceOfRain = chanceOfRain

}

/* ... */

}


Looking at the assignment windSpeed = windSpeed, how Swift can understand which variable is a parameter and which is a property? It's an ambiguity, so Swift plainly decides that you mean only the parameters.

As result an error is thrown: cannot assign to value: 'windSpeed' is a 'let' constant. It happens because let constant is assigned to itself windSpeed = windSpeed, which is not allowed.

Self keyword in Swift

2. To be, or not to be

There were plenty of discussions about the obligatory usage of self to access properties, or contrary to skip self.

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.

Let's define following Const structure with type properties minLimit, maxLimit and type method getLimitRange():


struct Const {

static let minLimit = 0

static let maxLimit = 250

static func getLimitRange() -> ClosedRange<Int> {

return self.minLimit...self.maxLimit

}

}

print(Const.getLimitRange()) // => 0...250


Within the type method getLimitRange() the type properties are accessed using self.minLimit and self.maxLimit. In this case self refers to Const type directly.

Interestingly that you can access type properties using two additional forms.

Firstly you can omit self, and Swift cleverly deduces you're accessing type properties:


struct Const {

static let minLimit = 0

static let maxLimit = 250

static func getLimitRange() -> ClosedRange<Int> {

return minLimit...maxLimit

}

}

print(Const.getLimitRange()) // => 0...250


Secondly you can indicate the type Const explicitly when accessing the properties:


struct Const {

static let minLimit = 0

static let maxLimit = 250

static func getLimitRange() -> ClosedRange<Int> {

return Const.minLimit...Const.maxLimit

}

}

print(Const.getLimitRange()) // => 0...250


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:


class Collection {

var numbers: [Int]

init(from numbers: [Int]) {

self.numbers = numbers

}

func getAppendClosure() -> (Int) -> Void {

return {

self.numbers.append($0)

}

}

}

var primes = Collection(from: [2, 3, 5])

let appendToPrimes = primes.getAppendClosure()

appendToPrimes(7)

appendToPrimes(11)

print(primes.numbers) // => [2, 3, 5, 7, 11]


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) } .

The explicit use of self in a closure is an intentional design. Because closure captures the scope variables, including self reference, you may create a strong reference cycle between the closure and self. And you should beware of this potential problem.

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:


class Person {

let name: String

lazy var sayMyName: () -> String = {

return self.name

}

init(withName name: String) {

self.name = name

}

deinit {

print("Person deinitialized")

}

}

var leader: Person?

leader = Person(withName: "John Connor")

if let leader = leader {

print(leader.sayMyName())

}

leader = nil


When the variable leader is assigned with nil, normally the instance should be deinitianilazied. You can expect deinit method to be called that prints "Person deinitialized" message. However this does not happen.

The strong reference cycle creates the problem.

Two lazy cats like a strong reference cycle

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:


class Person {

/* ... */

lazy var sayMyName: () -> String = {

[unowned self] in

return self.name

}

/* ... */

deinit {

print("Person deinitialized")

}

}

var leader: Person?

leader = Person(withName: "John Connor")

if let leader = leader {

print(leader.sayMyName())

}

leader = nil

// => "Person deinitialized"


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.

Since leader instance is not necessary leader = nil, the memory is deallocated correctly. The deinitializer deinit is called as expected, which prints "Person deinitialized" message to the console.

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.

Method chaining

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:


class Stack<Element> {

fileprivate var elements = [Element]()

@discardableResult

func push(_ element: Element) -> Stack {

elements.append(element)

return self

}

func pop() -> Element? {

return elements.popLast()

}

func printElements() {

print(elements)

}

}

var numbers = Stack<Int>()

numbers

.push(8)

.push(10)

.push(2)

numbers.printElements() // => [8, 10, 2]


The method push(:) returns the current instance self. This enables the method chaining calls to push multiple elements 8, 10 and 2 at once into the stack. Notice that @discardableResult attribute for push(:) method suppresses the warning that the result of the last method call in the chain is unused.

Enumeration case

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:


enum Activity {

case sleep

case code

case learn

case procrastinate

func getOccupation() -> String {

switch self {

case .sleep:

return "Sleeping"

case .code:

return "Coding"

case .learn:

return "Reading a book"

default:

return "Enjoying laziness"

}

}

}

let improving = Activity.learn

print(improving.getOccupation()) // => "Reading a book"


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:


struct Movement {

var speed: Int

mutating func stop() {

self = Movement(speed: 0)

}

}

var run = Movement(speed: 20)

print(run.speed) // => 20

run.stop()

print(run.speed) // => 0


In the mutating method stop() the assignment self = Movement(speed: 0) modifies the current instance to a new one.

5. Conclusion

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!