I want to make a delegate in separate class like:
class MapViewAnnotationDelegate: NSObject, SomeDelegate { //...}
and then in my controller I want to assign:
someView.delegate = MapViewAnnotationDelegate()
but the delegate is nil... how to achieve such effect? I read something about strong in Objective-C but as far as I happen to know the strong is default in Swift.
What you're doing is fine, but you need something to hold onto your instance. delegate properties by tradition are weak, so they don't ensure that the object is retained. Something else must retain it. The most common solution is to add a property to the class that owns someView, and assign your MapViewAnnotationDelegate to that property before assigning it as the delegate. That way it won't be deallocated as long as the containing objet lives. But anything that retains it is ok.
Your current code currently does this:
Create MapViewAnnotationDelegate
Assign it to someView.delegate
Note that there are no strong references to MapViewAnnotationDelegate (since someView.delegate is a weak reference)
Destroy MapViewAnnotationDelegate
Set someView.delegate to nil
One way this may look would be like this:
class Owner {
let mapViewAnnotationDelegate = MapViewAnnotationDelegate()
let someView: ...
init() {
someView.delegate = mapViewAnnotationDelegate
}
}
In this configuration, Owner (which is the owner of someView) holds onto the delegate for its lifetime.
Related
How can I override readonly property of a Swift superclass, from a subclass, to make the property read-write?
I want to do that, because that is what the iOS UIResponder documentation says is required. However, I'm getting an error when I try to implement what I think the Swift 5 documentation says can be done:Swift 5 documentation
Inheritance
Overriding Property Getters and Setters:
You can present an inherited read-only property as a read-write property by providing both a getter and a setter in your subclass property override.
What Went Wrong:
Based on aforementioned Swift docs statement, for which I found no accompanying example, I created the following subclass, at which XCode generates this error message:
"Cannot assign to property: 'inputAccessoryViewController' is a get-only property"
My Subclass:
class InputAccessoryEnabledTextView : UITextView {
override var inputAccessoryViewController: UIInputViewController? {
get { super.inputAccessoryViewController }
set { super.inputAccessoryViewController = newValue }
}
}
UIResponderDeclarationvar inputAccessoryViewController: UIInputViewController? { get }Discussion
This property is typically used to attach an accessory view controller to the system-supplied keyboard that is presented for UITextField and UITextView objects.
The value of this read-only property is nil. If you want to attach custom controls to a system-supplied input view controller (such as the system keyboard) or to a custom input view (one you provide in the inputViewController property), redeclare this property as read-write in a UIResponder subclass. You can then use this property to manage a custom accessory view. When the receiver becomes the first responder, the responder infrastructure attaches the accessory view to the appropriate input view before displaying it.
Note: I do see a question from 6 years ago on Stack Overflow that got no accepted answers and doesn't seem to properly answer the question, so please don't flag this as a dup without a good reason. Perhaps these questions can be merged later
super.inputAccessoryViewController is not settable.
Your overridden implementation in the subclass, self.inputAccessoryViewController is.
By adding a setter to the property in a subclass, you don't automatically also add the same thing in the superclass. What's in the subclass stays in the subclass.
So it's not that you can't override a property by adding a setter, you just can't set this here:
override var inputAccessoryViewController: UIInputViewController? {
get { super.inputAccessoryViewController }
set { super.inputAccessoryViewController = newValue }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
You can do other things, like:
override var inputAccessoryViewController: UIInputViewController? {
get { super.inputAccessoryViewController }
set { print("I just go set to \(newValue)") }
}
But that's not very useful. What you want is probably:
private var myInputAccessoryController: UIInputViewController?
override var inputAccessoryViewController: UIInputViewController? {
get { myInputAccessoryController }
set { myInputAccessoryController = newValue }
}
I have a class with a weak reference to its delegate. In a background operation, I need to set the delegate, perform an operation on the class, and then have the delegate released.
The code below works in Debug mode, but fails in Release mode, because in Release mode the delegate is released right away.
protocol DocumentDelegate:class { ... }
class MyDocument {
weak var delegate:DocumentDelegate?
func save() {
assert(self.delegate =! nil)
}
}
// Later:
// (#1) Using "var" does not work:
var delegate:DocumentDelegate? = InterimDelegate()
let document = MyDocument()
document.delegate = delegate
// Compiled in Release mode, at this time the delegate is already nil!
document.save()
delegate = nil
// (#2) Using "let" does work:
let delegate:DocumentDelegate = InterimDelegate()
let document = MyDocument()
document.delegate = delegate
// Compiled in Release mode, at this time the delegate is already nil!
document.save()
I assumed that the last instruction delegate = nil would cause the compiler to keep the delegate around until then (i.e. the "last" time the variable is used). However, thinking about it, it does make sense that the compiler optimizes the code and releases the delegate instance right away, since there are no other strong references.
However, I do not understand why the compiler does not behave the same way in the second case when using "let". Here as well the compiler could see that the delegate is not referenced via a strong reference anywhere else, but it does keep it around until the end of the block.
What would be a good way to think about this and what is a good way to keep a strong reference to the weak delegate?
While I wholeheartedly agree with Rob Napier’s analysis, for the sake of completeness, I should note that you can also make the lifetime of the object explicit:
let delegate = InterimDelegate()
withExtendedLifetime(delegate) {
let document = MyDocument()
document.delegate = delegate
document.save()
}
What you're describing is just a strong delegate. Get rid of weak, you don't mean it.
If you need an object to exist as long as you have a reference to it, and you plan to manually remove that reference at some point, that's a strong reference. The important thing for a delegate is that it at some point release its reference to avoid cycles. That's typically done with a weak reference because it makes things easy. But it can be done manually with strong reference. This is how URLSession's delegate works, for example.
If you intend the delegate to always be released after save, however, you may want to set it to nil in save. That can be nicer than having the caller do it (and matches how URLSession works; it automatically releases its delegate when it completes).
Just to explain what's happening in your code, ARC is allowed to release a reference after its last use.
// Example 1
// delegate is a strong reference
var delegate:DocumentDelegate? = InterimDelegate()
let document = MyDocument()
// Last read of delegate.
document.delegate = delegate
// delegate is released here. `document.delegate` is weak,
// so object is deallocated and set to nil.
// Compiled in Release mode, at this time the delegate is already nil!
document.save()
// This assignment is a no-op. The system was allowed to set it
// to nil earlier, so this doesn't matter.
delegate = nil
// Example 2
// This creates a strong reference
let delegate:DocumentDelegate = InterimDelegate()
let document = MyDocument()
// Last read of delegate.
document.delegate = delegate
// delegate is released here. `document.delegate` is weak,
// so object is deallocated and set to nil.
// Compiled in Release mode, at this time the delegate is already nil!
document.save()
I have a separate class which when called upon updates the ToolTip (a text property) for an NSButton in a pistonViewController via its IBOutlet.
However, whenever I try to perform the action, I get the error
"Unexpectedly found nil while implicitly unwrapping an Optional value"
since pistonViewController.piston.tooltip didn't work, I created an instance above the class:
let pistonView = pistonViewController();
and then from within the separate class called pistonView.set_piston();
func set_piston(index: Int) {
piston1.toolTip = "yay it worked!";
}
I get the same error: found nil.
How to get the correct instance of the pistonViewController (the one that appears on viewDidLoad) so that piston1 will not be nil?
There is this solution, but it looks needlessly complex. This one appears to only work on iOS, using a storyboard.InstantiateViewController command that does not work on MacOS. This MacOS solution is poorly explained and does not appear to work.
"[How do I] Modify IBOutlet property from outside of viewDidLoad"
(But what you're really asking is how you modify a view controller's views from outside of the view controller.)
The short answer is "Don't do that." It violates the principle of encapsulation. You should treat a view controller's view properties as private, and only modify them inside the view controller's code.
(To misquote Groucho Marx: "Doc, it crashes when I do this". "Then don't do that!")
Instead, add a public property (pistonToolTip) in your PistonViewController (Class names should begin with upper-case letters).
class PistonViewController: UIViewController {
var pistonToolTip: String {
didSet {
piston?.tooltip = pistonToolTip
}
}
}
And in case you set pistonToolTip before your PistonViewController has loaded its views, add this line to viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
piston?.tooltip = pistonToolTip
// The rest of your viewDidLoad code
}
Ultimately I just set it up in viewDidLoad, with a timer waiting for the other program to get the variables that will then be assigned to the pistons.
The lack of effective pointers to instances of View Controllers makes anything else not possible or perhaps just arcane and difficult.
I have a series of View Controllers which pass a struct model object down the chain.
If a user modifies the value of a property on the model, I update the view controller's model instance, and now I need to inform the parent view controllers that this object's value has changed.
Previously I would have used classes over structs for my model object and so I wouldn't have this issue as the object would have been directly written to.
But since structs are pass by value, I have to update the state on other view controllers. I have been using a singleton Manager object to handle state changes through a call to updateModel(). Is there a better way?
I have used something similar to this; keep a reference to the neighbouring view controller (with care to avoid a reference cycle) and a property observer on the struct property to update it when it changes.
This could also be updated prior to presenting a new view controller or before a segue, depending on your needs.
class myViewController: UIViewController {
// Your struct
var model: MyStruct? {
didSet {
if let pvc = previousVC {
pvc.model = model
}
}
}
// Keep a reference to the previous view controller on your stack
var previousVC: UIViewController?
override viewDidLoad() {
super.viewDidLoad()
self.model = MyStruct()
}
}
I viewed some Stanford iOS development classes on Youtube, and I found something that's not clear to me.
In the lecture the professor explains how to create custom views and custom data source classes, and the code is the following:
FaceView.swift
protocol FaceViewDataSource: class {
// some stuff here
}
class FaceView: UIView {
// some uninteresting properties here
weak var dataSource: FaceViewDataSource?
// other stuff here
}
HappinessViewController.swift
class HappinessViewController: UIViewController, FaceViewDataSource {
#IBOutlet weak var faceView: FaceView!
// other stuff here
}
The professor said that the dataSource property must be declared as a weak property to avoid retain cycles between the view and the view controller.
My question is: why do we have a retain cycle if we declare the dataSource property as strong? Since the outlet property is weak, isn't the retain cycle already avoided?
No, it's not. See the description below.
View controller keeps strong reference to his view.
View controller's view (not view controller) keeps strong reference to faceView.
FaceView keeps strong reference to View controller.