I have a struct set up that accepts a reference as a single initialization parameter:
internal struct NodeState: Equatable {
weak var node: Node! = nil
// ...
init(node: Node) {
self.node = node
}
}
I want to instantiate a NodeState as a member of the Node class, passing self in to set that weak reference:
public class Node: NSObject {
internal var state = NodeState(node: self)
// ...
}
…but I am getting this weird compile error:
Cannot convert value of type 'NSObject -> () -> Node' to expected argument type 'Node'
Am I not allowed to reference self in a member declaration in Swift?
In general you can't reference self in a member class declaration but you can if you make the property lazy and initialize it with a closure. Changing your Node class to something like this should work:
public class Node: NSObject {
internal lazy var staticState: NodeState = { NodeState(node: self) }()
}
It works because the lazy property isn't initialized until after self is initialized. self has to be fully initialized before it can be used.
Am I not allowed to reference self in a member declaration in Swift?
Sort of. You can't reference self (e.g. calling methods, passing self as a parameter) until the object is fully initialized.
You could use a lazy var in this case, which would work since it can't be accessed until the object is initialized. Here's an example:
public class Node: NSObject {
internal lazy var staticState: NodeState = {
return NodeState(node: self)
}()
}
Reference self in a closure?
public class Node: NSObject {
lazy var staticState: () -> (NodeState) = {
[unowned self] in
return NodeState(node: self)
}
}
I'm explicitly decorating self as unowned in the closure to prevent a retain cycle.
Related
I need to implement a WatchKit force-touch MenuItem to call a saveWorkout() method that is located in a separate class that does not subclass WKInterfaceController.
I realize that every class needs at least one designated initializer. I am guessing this is the key?
Btw, my "saveSession() reached" print statement logs to the console when using the sim but not when I use a device. All other print statements log to the console even when using the device. A bit odd.
My attempts at initialization throw various errors such as:
1.fatal error: use of unimplemented initializer 'init()' for class 'DashboardController'
2.Missing argument for parameter 'context' in call
Dashboard.swift
class DashboardController: WKInterfaceController {
#IBOutlet var timerLabel: WKInterfaceTimer!
#IBOutlet weak var milesLabel: WKInterfaceLabel!
// var wSM: WorkoutSessionManager
//init(wSM: WorkoutSessionManager) {
// self.wSM = wSM
// super.init()
// }
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
addMenuItemWithItemIcon(.Accept, title: "Save", action: #selector(DashboardController.saveSession))
}
override func willActivate() {
super.willActivate()
print("Dashboard controller reached")
}
func saveSession() {
//wSM.saveWorkout()
print("saveSession() reached")
}
WorkoutSessionManager.swift
class WorkoutSessionContext {
let healthStore: HKHealthStore
let activityType: HKWorkoutActivityType
let locationType: HKWorkoutSessionLocationType
init(healthStore: HKHealthStore, activityType: HKWorkoutActivityType = .Other, locationType: HKWorkoutSessionLocationType = .Unknown) {
self.healthStore = healthStore
self.activityType = activityType
self.locationType = locationType
}
}
protocol WorkoutSessionManagerDelegate: class {
// ... protocol methods
}
class WorkoutSessionManager: NSObject, HKWorkoutSessionDelegate {
let healthStore: HKHealthStore
let workoutSession: HKWorkoutSession
init(context: WorkoutSessionContext) {
self.healthStore = context.healthStore
self.workoutSession = HKWorkoutSession(activityType: context.activityType, locationType: context.locationType)
self.currentActiveEnergyQuantity = HKQuantity(unit: self.energyUnit, doubleValue: 0.0)
self.currentDistanceQuantity = HKQuantity(unit: self.distanceUnit, doubleValue: 0.0)
super.init()
self.workoutSession.delegate = self
}
func saveWorkout() {
guard let startDate = self.workoutStartDate, endDate = self.workoutEndDate else {return}
// ...code...
The fatal error is (or was) caused by this line:
let wSM = WorkoutSessionManager()
That line creates a new instance of WorkoutSessionManager and calls init() on it.
Swift provides a default initializer called init() for any structure or class that provides default values for all of its properties and does not provide at least one initializer itself. But WorkoutSessionManager does not provide default values for the healthStore and workoutSession properties (and those properties are not optionals), and it provides its own initializer named init(context:), so it has no default initializer.
You need to either create your instance of WorkoutSessionManager using the designated initializer init(context:) (passing an appropriate instance of WorkoutSessionContext) or provide a default initializer for WorkoutSessionManager named init().
The precise manner in which you should do the former depends on the implementation of the rest of your app and the presentation of your DashboardController. I assume you are trying to recreate the "Fit" app shown in WWDC 2015 Session 203.
In that demonstration, the initial controller is an instance of ActivityInterfaceController, and that controller is responsible for presenting the next interface (via segues created in the storyboard). You can see the following code in the ActivityInterfaceController class:
override func contextForSegueWithIdentifier(segueIdentifier: String) -> AnyObject? {
let activityType: HKWorkoutActivityType
switch segueIdentifier {
case "Running":
activityType = .Running
case "Walking":
activityType = .Walking
case "Cycling":
activityType = .Cycling
default:
activityType = .Other
}
return WorkoutSessionContext(healthStore: self.healthStore, activityType: activityType)
}
The function above creates and returns a new instance of WorkoutSessionContext using an instance of HKHealthStore held by the initial controller. The context returned by that function is passed to the destination interface controller for the relevant segue through awakeWithContext.
For transitions in code, you can pass a context instance using equivalent functions such as pushControllerWithName(context:) which also lead to awakeWithContext.
If your initial controller is similar to the above, you can access the passed context in awakeWithContext in your DashboardController class and use it to configure a new instance of WorkoutSessionManager:
class DashboardController: WKInterfaceController
{
// ...
var wSM: WorkoutSessionManager?
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
if context is WorkoutSessionContext {
wSM = WorkoutSessionManager(context: context as! WorkoutSessionContext)
}
addMenuItemWithItemIcon(.Accept, title: "Save", action: #selector(DashboardController.saveSession))
}
// ...
}
Creating an instance of WorkoutSessionManager in that way avoids calling the (non-existent) init() initializer and permits reuse of the HKHealthStore instance. Whether that approach is open to you depends on the rest of your code and the way you are presenting your DashboardController.
Note that you should avoid creating multiple instances of WorkoutSessionManager. Use a singleton to provide a single instance of WorkoutSessionManager that is shared across your extension.
I’ve got a car and a driver. They mutually reference each other. In the car’s init() I create a driver and assign it to the driver member. The driver member has a didSet method which is supposed to set the driver’s car, thus mutually link them to each other.
class GmDriver {
var car: GmCar! = nil
}
class GmCar {
var driver: GmDriver {
didSet {
driver.car = self
}
}
init() {
driver = GmDriver()
}
}
let myCar = GmCar()
println(myCar.driver.car) // nil
However, the didSet never fires. Why?
Apple Documentation:
The willSet and didSet observers of superclass properties are called when a property is set in a subclass initializer, after the superclass initializer has been called. They are not called while a class is setting its own properties, before the superclass initializer has been called.
init() {
defer {
driver = GmDriver()
}
}
I have a simple Result object which should contain an object (class, struct or enum) and a Bool to say whether it was cancelled or not. I need to interrogate this object along its path (before it gets to its destination, where the destination knows what kind of object to expect) to determine whether it was cancelled or not (without worrying about the accompanying object at that moment). My object looks like:
import Foundation
#objc protocol Resultable {
var didCancel: Bool { get }
}
class Result<T>: Resultable {
let didCancel: Bool
let object: T?
init(didCancel: Bool, object: T?) {
self.didCancel = didCancel
self.object = object
}
}
The idea being that my Result object can wrap the didCancel flag and the actual object (which can be of any type), and the fact that it implements the Resultable protocol means that I can interrogate it at any point to see whether it was cancelled by casting it to Resultable.
I understand (while not liking it) that the protocol has to be prefixed with #objc so that we can cast to it (according to the Apple docs). Unfortunately, when I run the code below (in a playground or in a project), I get a nasty "does not implement methodSignatureForSelector:" error message:
let test = Result(didCancel: false, object: NSArray())
println(test.didCancel)
// transform the object into the form it will be received in
let anyTest: AnyObject = test
if let castTest = anyTest as? Resultable {
println(castTest.didCancel)
}
It seems that despite the protocol being prefixed with #objc, it also wants the actual class to inherit from NSObject (and this is not a requirement that Apple makes explicit). This is obviously a problem for a generic class.
Is there anything I am missing here? Any way to get this to work? Failing that, are there any workarounds (although I strongly believe that this kind of thing should be possible - perhaps we can hope that Apple will do away with the #objc protocol casting requirement at some stage)?
UPDATE
It looks like this is solved with Swift 1.2
You can now cast to a non-ObjC protocol, and without the object having to inherit from NSObject.
It seems that despite the protocol being prefixed with #objc, it also wants the actual class to inherit from NSObject
This is not true. For example, the following code works as expected:
#objc protocol MyProtocol {
var flag: Bool { get }
}
class MyClass: MyProtocol {
let flag = true
}
let foo:AnyObject = MyClass()
if let p = foo as? MyProtocol {
println(p.flag)
}
The problems is that: Any methods/properties declared in Swift Generic classes are invisible from Objective-C. Hence, from the perspective of #objc protocol Resultable, didCancel property declared in Result<T> is invisible. That's why methodSignatureForSelector: is called.
The workaround here is very annoying: You have to have non Generic base class for Result that implements didCancel.
#objc protocol Resultable {
var didCancel: Bool { get }
}
class ResultBase: Resultable {
let didCancel: Bool
init(didCancel: Bool) { self.didCancel = didCancel }
}
class Result<T>: ResultBase, Resultable {
// ^^^^^^^^^^^^
// You have to explicitly conforms `Resultable` here as well for some reason :/
let object: T?
init(didCancel: Bool, object: T?) {
self.object = object
super.init(didCancel: didCancel)
}
}
let test = Result(didCancel: false, object: NSArray())
let anyTest: AnyObject = test
if let castTest = anyTest as? Resultable {
println(castTest.didCancel) // -> outputs "false"
}
While playing in a Swift playground I noticed that Self, with capital "S", is available along with the lowercase self. Is there any difference between them? If so, what are usages for these two, especially for Self?
Self refers to the type of the current "thing" inside of a protocol (whatever is conforming to the protocol). For an example of its use, see Protocol func returning Self.
The official docs I've found for Self is in Protocol Associated Type Declaration in The Swift Programming Language. It surprisingly is not documented in the sections on protocols or on nested types:
However, now there is a paragraph about Self Type including a code example in the official Swift Programming Language's chapter about Types
Self can also be used in classes, and is useful. Here is an article about it.
Here is an example. You have a class called MyClass. MyClass have methods returning instances of it. Now you add a subclass of MyClass called MySubclass. You want these methods to return instances of MySubclass instead of MyClass. The following code shows how to do it. Note that the methods can be either instance methods or class methods.
class MyClass: CustomStringConvertible {
let text: String
// Use required to ensure subclasses also have this init method
required init(text: String) {
self.text = text
}
class func create() -> Self {
return self.init(text: "Created")
}
func modify() -> Self {
return type(of: self).init(text: "modifid: " + text)
}
var description: String {
return text
}
}
class MySubclass: MyClass {
required init(text: String) {
super.init(text: "MySubclass " + text)
}
}
let myClass = MyClass.create()
let myClassModified = myClass.modify()
let mySubclass = MySubclass.create()
let mySubclassModified = mySubclass.modify()
print(myClass)
print(myClassModified)
print(mySubclass)
print(mySubclassModified)
The following line printed out:
// Created
// modifid: Created
// MySubclass Created
// MySubclass modifid: MySubclass Created
in protocol & Extension declaration use Self else self
extension protocolName where Self: UIView
{
func someFunction()
{
self.layer.shadowColor = UIColor.red.cgColor
}
}
I think this question could use a simpler answer, more focussed on the difference between Self and self, and perhaps aimed at people newer to Swift.
self - explicit reference to the current type or instance of the type in which it occurs.
class MyClass {
func showClass() {
print("\(self)")
}
}
let someClass = MyClass()
someClass.showClass()
// prints "MyClass"
Self - Used specifically in protocol and extension declarations to refer to the eventual type that will conform to the protocol.
protocol MyProtocol {
static func returnSelf() -> Self
}
class MyClass: MyProtocol {
// define class
}
MyClass.returnSelf()
// returns MyClass
The difference is that self is used in types and instances of types to refer to the type that it's in; and Self is used in protocols and extensions where the actual type is not yet known.
In even simpler terms, self is used within an existing type; Self is used to refer to a type that Self is not yet in.
Read more here:
Self - https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-XID_543
self - https://docs.swift.org/swift-book/ReferenceManual/Expressions.html
Both -
https://medium.com/the-traveled-ios-developers-guide/swift-keywords-v-3-0-1-f59783bf26c
I understand Self as a type name(class name for example) and self as an instance of a class/struct , for example:
struct Person {
static var documentNumner = "9484930"
var name: String
var computedFullName: String {
return ("\(self.name) with document number: \(Self.documentNumner)")
}
}
You can't use self with a static property but you can use Self
self-refers to the instance or object of a class.
class SampleClassOne {
let name: String
init(name: String) {
self.name = name
}
}
Self-refers to the type of protocol or extension.
class SampleClassTwo {
static let name: String = "Hello"
private func printName() {
print("\(Self.name)")
}
}
protocol SampleProtocol {
func sampleFunc() -> Self
}
Another example to simply understanding together.
extension Int {
var add: Self { //refer to the Type
return self + 100 //refer to the instance
}
}
print(5.add)
Self can also be used as a return type in the protocol extension method body which will return confirming type instance, and for type casting with "as". Please see example below:
extension <Protocol-Name> where Self:<Class-Name> {
static func foo(_ param:Type)-> Self{
guard let abc = method() as? Self else{
return xyz
}
}}
In the nutshell, Self can be used to refer the Type which confirms to the protocol.
I've got a memory leak in this case, if I pass a reference to any method, the self comes with it which increases it's reference count I guess, how can I make non class types to be weak
public class Observer {
weak private var method: ((message: AnyObject) -> ())! //error here
weak private var context: AnyObject!
}
public init(method: (AnyObject -> ())?, context: AnyObject?) {
self.method = method
self.context = context
}
in another class I guess self.callback creates a strong reference to the caller object and passes on.
var observer = Observer(method: self.callback, context: self) //pass of self.callback is a strong reference
Edit:
Working on the above, my attempt using an example that further clarifies the situation using two classes. deinit never gets called.
class Test {
private var ref: Observer?
init() {
ref = Observer(method: self.callback, context: self)
}
func callback(message: AnyObject) {
}
deinit {
println("deinit test")
}
}
public class Observer {
private var method: ((message: AnyObject) -> ())?
weak private var context: AnyObject!
public init(method: (AnyObject -> ())?, context: AnyObject?) {
self.method = method
self.context = context
}
deinit {
println("deinit observer")
}
}
From looking at your code, it seems like you are talking about a retain cycle where the Test object holds onto the Observer object through the variable ref, the Observer object holds onto the closure formed by doing self.callback, which holds onto self.
Generally in such cases, you don't want the closure property itself to be weak. Rather, you want the closure to capture a weak reference to self (the Test object is passing a "callback" to itself to another object). However, that is somewhat confusing here as we are not explicitly using closure syntax (rather, you are getting a closure by accessing a method on an instance and not calling it). The problem of capturing a weak reference to self in this case was covered in this question.
The best solution is:
ref = Observer(method: {[unowned self] in self.callback($0)}, context: self)
Try this:
public class Observer {
private var method: ((message: AnyObject) -> ())?
weak private var context: AnyObject!
public init(method: (AnyObject -> ())?, context: AnyObject?) {
self.method = method
self.context = context
}
}
I tried it and it doesn't create a strong reference cycle. But I also tried with ! instead of ?, and that didn't caused as well, and I hope somebody is out there to explain that.