When should I access properties with self in swift?

Swift

Swift Problem Overview


In a simple example like this, I can omit self for referencing backgroundLayer because it's unambiguous which backgroundLayer the backgroundColor is set on.

class SpecialView: UIView {
    let backgroundLayer = CAShapeLayer()
    
    init() {
        backgroundLayer.backgroundColor = UIColor.greenColor().CGColor
    }
}

But, just like in Objective-C, we can confuse things by adding local variables (or constants) named similarly. Now the backgroundColor is being set on the non-shape layer:

class SpecialView: UIView {
    let backgroundLayer = CAShapeLayer()
    
    init() {
        var backgroundLayer = CALayer()

        backgroundLayer.backgroundColor = UIColor.greenColor().CGColor
    }
}

(this is resolved by using self.backgroundLayer.backgroundColor)

In Objective-C I always eschewed ivars for properties and properties were always prefixed with self for clarity. I don't have to worry about ivars in swift but are there other considerations for when I should use self in swift?

Swift Solutions


Solution 1 - Swift

The only times self is required are when referencing a property inside a closure and, as you pointed out, to differentiate it from a local variable with the same name.

However, personally, I prefer to always write "self" because:

  1. That is an instant and obvious sign that the variable is a property. This is important because it being a property means that its state can vary more widely and in different ways than a local variable. Also, changing a property has larger implications than changing a local variable.
  2. The code does not need to be updated if you decide to introduce a parameter or variable with the same name as the property
  3. Code can be easily copied in and out of closures that do require self

Solution 2 - Swift

Most of the time we can skip self. when we access class properties.

  1. However there is one time when we MUST use it: when we try to set self.property in a closure:

     dispatch_async(dispatch_get_main_queue(), {
         // we cannot assign to properties of self
         self.view = nil 
    
         // but can access properties
         someFunc(view)
     })
    
  2. one time when we SHOULD use it: so you don't mess a local variable with class property:

     class MyClass {
         var someVar: String = "class prop"
     
         func setProperty(someVar:String = "method attribute") -> () {
             print(self.someVar) // Output: class property
             print(someVar) // Output: method attribute
         }
     }
    
  3. other places where we CAN use self. before property just to be expressive about were variable/constant comes from.

Solution 3 - Swift

Looking at Ray Wenderlich's style guide

> 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 only when required by the compiler (in @escaping closures, or in initializers to disambiguate properties from arguments). In other words, if it compiles without self then omit it.

Swift documentation makes the same recommendation.

>The self Property

>Every instance of a type has an implicit property called self, which is exactly equivalent to the instance itself. You use the self property to refer to the current instance within its own instance methods.

>The increment() method in the example above could have been written like this:

func increment() {
    self.count += 1
}

>In practice, you don’t need to write self in your code very often. If you don’t explicitly write self, Swift assumes that you are referring to a property or method of the current instance whenever you use a known property or method name within a method. This assumption is demonstrated by the use of count (rather than self.count) inside the three instance methods for Counter.

>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:

struct Point {
    var x = 0.0, y = 0.0

    func isToTheRightOf(x: Double) -> Bool {
        return self.x > x
    }
}

let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
    print("This point is to the right of the line where x == 1.0")
}

// Prints "This point is to the right of the line where x == 1.0"

Solution 4 - Swift

I'm going to go against the flow and not use self unless absolutely required.

The reason why is that two of the main reasons to use self is

  • When capturing self in a block
  • When setting self as a delegate

In both cases, self will be captured as a strong reference. This might be what you want, but in many cases, you actually want to use a weak one.

Therefor, forcing the developer to use self as an exception and not a rule will make this strong capture more conscious, and let him reflect on this decision.

Solution 5 - Swift

As Apple documentation says in https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Methods.html

> The self Property > > Every instance of a type has an implicit property called self, which > is exactly equivalent to the instance itself. You use the self > property to refer to the current instance within its own instance > methods. > > The increment() method in the example above could have been written > like this: > > func increment() { > self.count += 1 > } > > In practice, you don’t need to write self in your code very often. If > you don’t explicitly write self, Swift assumes that you are referring > to a property or method of the current instance whenever you use a > known property or method name within a method. This assumption is > demonstrated by the use of count (rather than self.count) inside the > three instance methods for Counter. > > 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: > > struct Point { > var x = 0.0, y = 0.0 > func isToTheRightOf(x: Double) -> Bool { > return self.x > x > } > } > let somePoint = Point(x: 4.0, y: 5.0) > if somePoint.isToTheRightOf(x: 1.0) { > print("This point is to the right of the line where x == 1.0") > } > // Prints "This point is to the right of the line where x == 1.0" > > Without the self prefix, Swift would assume that both uses of x > referred to the method parameter called x.

I would prefer to keep using self whenever I'm using a property to omit these misunderstandings.

Solution 6 - Swift

As Nick said, in objective-c we had ivars + synthesized properties which gave the _internal variable names to delineate things. Eg.

@IBOutlet (nonatomic,strong) UITableView *myTableView;

resulting in _myTableView to be (preferably) referenced internally - and self.myTableView to be reference beyond the class. While this is pretty black and white, consider the exception when programmatically instantiating views, you can gain clarity/ simplicity / reduce boilerplate by removing self.

@interface CustomVC:UIViewController
{
     UITableView *myTableView; 
}

In swift, the public / internal properties clarify this scope. If it's a public property that other classes will interact with err on self. Otherwise if it's internal skip self and avoid the automatic repetition. The compiler will catch you when it's needed.

// UIViewcontroller swift header
public var title: String? // Localized title for use by a parent controller.
public var navigationItem: UINavigationItem { get } 

/// In your class
self.title  = "Clarity"
self.navigationItem.leftBarButtonItem = UIBarButtonItem()

// In superclass  
 @property(nonatomic, copy) NSString *screenName  // use self.screenName in swift subclass

@IBOutlet myTableView:UITableView  // use self
public var myTableView:UITableView  // use self

internal var myTableView:UITableView // skip self
var myTableView:UITableView // skip self 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNickView Question on Stackoverflow
Solution 1 - SwiftdrewagView Answer on Stackoverflow
Solution 2 - SwiftKeenleView Answer on Stackoverflow
Solution 3 - SwiftDoesDataView Answer on Stackoverflow
Solution 4 - SwiftAntziView Answer on Stackoverflow
Solution 5 - SwiftDavid CespedesView Answer on Stackoverflow
Solution 6 - SwiftjohndpopeView Answer on Stackoverflow