In Swift 3 the dispatch_once function was removed and the migration guide suggests to use initializing closure:
let myGlobal = { … global contains initialization in a call to a closure … }()
_ = myGlobal // using myGlobal will invoke the initialization code only the first time it is used.
I'd like to access 'self' instance variables from within the initializing closure like so:
class SomeClass {
var other = SomeOtherClass()
let initialize: () = {
// self.other - this doesn't work, complains about unresolved identifier 'self'
// how to access self.other here?
} ()
func doSomething() {
// initialize will only be called once
initialize
}
}
Why is 'self' not accessible in the closure and how can make it to be?
This quoted example of the Migration Guide is misleading because it's related to a global variable.
The closure of a instance let constant is called (once) immediately when the class is initialized. That's the reason why it cannot use other variables declared on the same level.
What you can do is to initialize initialize (the variable name is not the best one ;-) ) lazily. The closure is also called only once but – as the guide describes – only the first time (when) it is used.
class SomeClass {
let other = SomeOtherClass()
lazy var initialize : () = {
let test = self.other
test.doSomething()
}()
func doSomething() {
// initialize will only be called once
_ = initialize
}
}
When an instance of the 'SomeClass' class is created, it will first create all of the variables and constants on that instance. During this time, self may not be fully initialised, because it may be halfway through setting up. Because of this, self is not available until after the initialisation step has completed.
In the example, they were talking about a global variable which has no concept of self, or a static constant on the class which also has no concept of self.
If it needs to be an instance method/variable you could:
a) make it a lazy var like
lazy var initialise : ()->Void = {
return {
// can access self here
}
}()
which will be created the first time you call it, rather than during initialisation. Of course you lose the constant that way, and you have to store the closure which is wasteful since you're only executing it once.
b) put the code inside of an init method:
init() {
// if your class doesn't have a super class, you can access self.other here.
// If it does have a super class (like NSObject) you must first call super.init() here to complete the initialisation.
// This can only be done after all other variables have been set.
}
Related
Lets say you have a class.
class SomeClass: UIViewController {
private var _dataSource = SomeOtherClass()
init() {
// --> breakpoint
super.init(nibName: nil, bundle: nil)
}
}
At the breakpoint in init - I can see that _dataSource is already allocated. At what point is the class actually getting allocated, and when do the class iVars also get init?
Are there some docs on what happens under the hood?
And what's the difference compared to this?
class SomeClass: UIViewController {
private var _dataSource: SomeOtherClass!
init() {
_dataSource = SomeOtherClass()
super.init(nibName: nil, bundle: nil)
}
}
Thanks,
p.s. Coming from Obj-C to Swift world.
In swift, iVar's are assimilated with properties, so I'll be using the term "property" in the rest of the answer.
Property initialization code runs right before running the code of any initializer, as part of a compiler-genarated "initializer". One method to find out about this is by initializing the property from a function, and set a breakpoint inside that function:
class SomeClass: UIViewController {
private var _dataSource = {
// --> breakpoint
return SomeOtherClass()
}()
init() {
// --> breakpoint
super.init(nibName: nil, bundle: nil)
}
}
You should see a stacktrace like this
BTW, in your second code snippet you don't need to declare the property as implicitly unwrapped, as long as you give it a value either inline or within the initializer the compiler will be happy.
The answer to your second question is that, at runtime, there are no differences between assigning the property the value inline (at the declaration site), or within the initializer. There are other advantages/disadvantages with both approaches, at compile time, like with multiple initializers giving a value inline avoids code duplication.
Swift does not expose allocation or bare iVars. Only init methods and properties are exposed.
Do not make assumptions about how allocation is done or how iVars are initialized. The compiler is free to optimize those details in any way it chooses.
Declaration of properties are straight forward.
class SomeClass: UIViewController {
// This is an instance property. It must be set in init(…) before the call to
// super.init(…).
var _dataSource1: SomeOtherClass
// This is an instance property that is set in its declaration. A way to think
// of this is to imagine it being set after allocation and before init(…) is
// called.
var _dataSource2 = SomeOtherClass()
// This is a lazy property. Instead of being set in init(…), lazy properties
// are set when they are first used.
lazy var _dataSource3 = SomeOtherClass()
// This is a class property accessed with SomeClass._dataSource4. All static
// properties are lazy. So, they are set on first use.
static var _dataSource4 = SomeOtherClass()
}
Initialization in Swift is a two-phase process. In the first phase, each property is assigned an initial value. Whereas in Objective-C this meant assigning zeroes and null values, in Swift it also assigns default property values, like here
class SomeClass: UIViewController {
private var _dataSource = SomeOtherClass()
}
Second phase is when initializers are being called. That is why by the time your breakpoint triggers, the default property value for _dataSource has already been assigned.
The difference between
private var _dataSource = SomeOtherClass()
and
private var _dataSource: SomeOtherClass!
is that in the second example you are only declaring a property, which needs to be initialized somewhere further along the road, whereas in the first one you declare it and provide it with a default value.
Note that in the first example, you don't need to explicitly specify the type of the property, because the compiler can infer it from the default value.
Here is my code:
class Base
{
init(){
print("Super!")
}
}
class Test : Base
{
internal var y:Int
convenience init(_ a:Int)
{
self.init()
print("\(a)")
}
override init()
{
super.init() //Error!!! Property 'self.y' not initialized at super.init call
y = 123
}
}
I think this should be compiled:
y is not visible inside class 'Base',whether order of initializations of y's and super class's doesn't really matter.
Your argument
I think this should be compiled:
y is not visible inside class 'Base',whether order of initializations
of y's and super class's doesn't really matter.
is not correct, that would not be safe.
The superclass init can call an instance
method which is overridden in the subclass. That is (at least one)
reason why all subclass properties must be initialized before super.init() is called.
A simple example:
class Base
{
init(){
print("enter Base.init")
setup()
print("leave Base.init")
}
func setup() {
print("Base.setup called")
}
}
class Test : Base
{
internal var y:Int
override init()
{
y = 123
print("before super.init")
super.init()
print("after super.init")
}
override func setup() {
print("Test.setup called")
print("y = \(y)")
}
}
Output:
before super.init
enter Base.init
Test.setup called
y = 123
leave Base.init
after super.init
As you can see, the y property of the subclass is accessed
during the super.init() call, even if it is not known to the
superclass.
It might be interesting to compare the situation in Objective-C
where self = [super initXXX] is always called first. This has the
consequence that property access self.prop in init/dealloc methods
is unsafe and direct access to the instance variable _prop is
recommended because the object may be in a "partially constructed state".
See for example Should I refer to self.property in the init method with ARC?.
So this is one of the issues which have been solved in Swift
(at the cost of stricter requirements).
From the documentation:
Safety check 1
A designated initializer must ensure that all of the
properties introduced by its class are initialized before it delegates
up to a superclass initializer.
As mentioned above, the memory for an object is only considered fully
initialized once the initial state of all of its stored properties is
known. In order for this rule to be satisfied, a designated
initializer must make sure that all its own properties are initialized
before it hands off up the chain.
Source: Swift Language Guide: Initialization
Just exchange the two lines in init
override init()
{
y = 123
super.init()
}
Have a look on following code
class Example {
let action: String -> ()
init() {
action = method //error: Variable self.action used before initialized
}
func method(s: String) {
println(s)
}
}
I am setting property of closure type to a class method. To reference class method I need to have the single properties initialized but to have it properly inicialized I need to reference that method. How do I get out of the cycle?
I know I can do something like
init() {
action = {_ in }
action = method //error: Variable self.action used before initialized
}
but that just is not nice.
The actual thing I need to do is more complex and makes much more sense bt this is the essence.
You can declare the property as implicitly unwrapped optional:
let action: (String -> ())!
That's one of the few cases when implicitly unwrapped are useful and can be safely used.
Use a lazy var as such:
The first time you access action the expression self.method will be evaluated (and by that time self is valid).
If you are uncomfortable with var action being settable, you can use (a common Apple pattern) of:
lazy private var _action : (String) -> () = self.method
var action { return _action } // only the 'getter' thus `var` is actually `let`
and if you are uncomfortable with even these two, you can use private(set) to ensure that no setter is visible. And thus,
class Example {
lazy private(set) var action: (String -()) = self.method
func method (s:String) { println (s) }
}
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?
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:
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.
The code does not need to be updated if you decide to introduce a parameter or variable with the same name as the property
Code can be easily copied in and out of closures that do require self
Most of the time we can skip self. when we access class properties.
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)
})
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
}
}
other places where we CAN use self.
before property just to be expressive about were variable/constant comes from.
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"
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.
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.
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
I am baffled by the errors arising while trying to initialize an instance of an array in a class. The comments below are the errors xcode 6 is showing.
I have created a class. It is having instance of NSMutableArray. I want to initialize the array (hence calling self.instancename.init()). It complains if I don't. It complains if I do.
import Foundation
class testclass:NSObject {
var list_of_things:NSMutableArray;
init (){ // Designated initializer for 'testclass' cannot delegate (swith self.init);
// did you means this to be a convenience initializer?
self.list_of_things.init();
// 'init' can only refer to the initializers of 'self' or 'super'
super.init()
// Initializer cannot both delegate ('self.init') and chain to a superclass
// initializer ('super.init')
}
}
You need to assign a value to the variable, there is nothing in that variable to call init on:
init () {
self.list_of_things = NSMutableArray()
super.init()
}
Also a few notes:
You do not need semicolons at the end of lines (I know that habit is hard to break)
You do not need to inherit from NSObject
You should prefer to use native swift arrays (Array) instead of NSMutableArray
Classes should always start with capital letters
Variable names should use camel case instead of underscores
This would be my cleaned up version of your test class:
class TestClass {
var listOfThings: [AnyObject]
init () {
self.listOfThings = []
super.init()
}
}
And actually, if you just want to initialize to an empty array, you don't even need to implement init or specify the type explicitly:
class TestClass {
var listOfThings = []
}
To call the initializer of another class, you simply call it like this:
self.list_of_things = NSMutableArray()
There's no need to implicitly call the init() function, it's implied when adding the () to the class name.
You could also initialize it when you create your property, like this:
var list_of_things:NSMutableArray = NSMutableArray()
That's not how you call init on NSMutableArray.
class testclass:NSObject {
var list_of_things:NSMutableArray
init (){
self.list_of_things = NSMutableArray()
super.init()
}
}
And get rid of those semicolons! What is this? last week?
End to end answer. No superclass, using Swift arrays, init list_of_things at declaration side, in init() no superclass to initialize.
class testClass {
var list_of_things = []
init () {
}
}
126> var abc = testClass ()
abc: testClass = {
list_of_things = #"0 objects"
}
127> abc.list_of_things
$R55: __NSArrayI = #"0 objects"