today I faced a problem , the vc never call deinit, so I added a weak
func showAddCityViewController() {
weak var vc:SWAddCityViewController!
vc = SWAddCityViewController()
vc.delegate = self
let nav = UINavigationController(rootViewController: vc)
dispatch_async(dispatch_get_main_queue(), {
self.presentVC(nav)
})
}
I run this function and then get a
fatal error: unexpectedly found nil while unwrapping an Optional value
the vc just go to nil ,but I don't know why , what should I do to make this code happy?
You wrote this:
weak var vc:SWAddCityViewController!
vc = SWAddCityViewController()
The vc variable is an “implicitly unwrapped Optional”, which means it can either point to an existing (not-deallocated) object, or it can be nil.
You create a new SWAddCityViewController object and assign it to vc. After the assignment statement completes, there is exactly one weak reference to the new object (in vc) and there are no strong references to it. An object is deallocated as soon as it has no strong references, so it is deallocated as soon as the assignment statement completes.
Since vc was a weak reference to the object, part of deallocating the object sets vc to nil. When you try to set vc.delegate on the next line, Swift generates code to unwrap vc automatically (since you declared it with !). Since vc is nil, you get a fatal error. You cannot unwrap an optional that's set to nil because it's not wrapping anything.
I don't see any reason to declare vc weak in that function. Just get rid of the weak attribute.
Your other complaint is that (with weak) the object doesn't get deallocated later. You have a “retain cycle”. Did you declare the delegate property of SWAddCityViewController using weak? You usually want to declare delegate properties weak.
If that doesn't fix the problem, you need to look for other places where you have a retain cycle involving the object.
Related
My sample code like this (just a sample):
[self.view touchActionWithCompeletion:^(NSSting *text){
self.label.text = text;
}];
The block is a parameter of a method, the method is a instance method of self.view, then I access self in block. If self.view is a strong property, will this situation create retain cycle? And will self.view strong reference the block?
Adding my comment above as answer, after testing the code myself to confirm the logic I mentioned,
I think it should not, dead lock ( I mean memory leak, two strongly held objects holding the reference to each other and never being de-allocated hence I mentioned deadlock) will happen only if you pass a strong reference of an object to the block (in this case self) and then the passed object holds a strong reference to block itself (directly or indirectly).
Hoping that method touchActionWithCompeletion will not save the block passed to it with a strong reference this should not result in a retain cycle
EDIT:
Tested your code and deinit gets called as expected.
Here is what I tried,
class MyView : UIView {
func touchActionWithCompeletion(block :(NSString)->()) {
block("abcd");
}
}
class ThirdViewController: UIViewController {
var myViewInstance = MyView()
#IBOutlet var c: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.myViewInstance.touchActionWithCompeletion { (abcd) in
self.c.text = abcd as String
}
}
deinit {
print("deinit called")
}
}
As expected deinit called.
Only thing to notice here, method touchActionWithCompeletion will not store the block passed to it with strong reference. It simply executes it. So my answer above holds true in this case.
EDIT 2:(Clarifying my statement in answer)
I happened mention the passed object holds a strong reference to block itself (directly or indirectly) I guess I need to explain why I mentioned indirectly.
Consider this case, Deadlock will happen if View holds a strong reference to the block passed to its method. Though the strong object passed here to the block is self and self does not hold the reference to block directly, it still result in deadlock if View holds a strong reference to block.
Reason Self - holds a strong reference -> View - holds a strong reference -> Block - holds a strong reference -> Self
Hence deadlock. Though self does not hold the block directly, because it holds the block indirectly, hence deinit on self will not be called. Hence I happened to mention the passed object holds a strong reference to block itself (directly or indirectly)
Hope it helps
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var _email: MaterialField!
#IBOutlet weak var _pass: MaterialField!
#IBOutlet weak var _signIn: UIButton!
#IBAction func attemptLoginWithEmail(sender : UIButton!) {
if let email = _email.text where email != "", let password = _pass.text where password != "" {
print(email)
}
}
Program keeps crashing , the _email.text and _pass.text keep returning nil. tried for a few hours but couldn't get it to work. any help would be appreciated
Just simply remove weak keyword from at the time of object preparation and now run your project its worked well.
Note :
A strong reference (which you will use in most cases) means that you want to "own" the object you are referencing with this property/variable. The compiler will take care that any object that you assign to this property will not be destroyed as long as you point to it with a strong reference. Only once you set the property to nil will the object get destroyed (unless one or more other objects also hold a strong reference to it).
In contrast, with a weak reference you signify that you don't want to have control over the object's lifetime. The object you are referencing weakly only lives on because at least one other object holds a strong reference to it. Once that is no longer the case, the object gets destroyed and your weak property will automatically get set to nil. The most frequent use cases of weak references in iOS are:
1.) delegate properties, which are often referenced weakly to avoid retain cycles, and
2.) subviews/controls of a view controller's main view because those views are already strongly held by the main view.
I am not sure what it was but I deleted func attemptLogin and made another function exactly the same referring the same button and it worked. thanks for those of you who commented
I have set up my view controllers so that they send a notification once their -viewDidLoad method is about to return. For example:
class MyViewController: UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
//Do Stuff
var notificationCenter = NSNotificationCenter.defaultCenter();
notificationCenter.postNotificationName("AViewControllerDidLoadNotification", object: self);
}
}
My AppDelegate class is listening for this notification and implementing the method shown in this picture.
In case the picture isn't loading, the method takes the notification sent by the view controllers as it's only argument and then tests whether the UIViewController's title property has a non-nil value. If the title property is non-nil it logs the title.
However, as you can see in the debugger panel, the title property of the view controller is nil and the if statement is still evaluating to true.
I am admittedly new to optional values. But I have recreated this situation in a swift playground and the if statement evaluates to false. Any ideas?
You've gotten yourself into rather an odd situation with your very peculiar use of the expression notification.object?.title, because notification.object is not, of itself, a UIViewController. It is an AnyObject.
Now, an AnyObject has no known properties, so it has no title and your expression, it would seem, should not even compile. But, by a special dispensation coming from certain oddities of Objective-C, you are in fact allowed to ask about an AnyObject's properties anyway. But when you do, the result is itself an Optional, because no such property might exist.
Thus, you are actually testing, not the value of a view controller's title property, but whether this unknown object has a title property in the first place; and if in fact it does have a title property at all, the value of that title property is double-wrapped inside that Optional.
To see this clearly, just test this (silly) code:
let n = NSNotification(name: "Howdy", object: "Hi")
let t = n.object?.title
Look at what type t is. It is not a String?; it is a String??. That's your double-wrapped Optional. This means that it would be an Optional-wrapping-a-String in case this object turns out to have a title property, but just in case, that value has itself been wrapped in an Optional.
Thus, your test doesn't do what you want it to do. To do what you want to do, just speak much more plainly and simply. You need to cast the object to a UIViewController first, and then examine its title. Like this:
func aViewControllerDidLoad(notification:NSNotification) {
if let vc = notification.object as? UIViewController {
if vc.title != nil {
// ...
}
}
}
I started getting this error randomly (may be due to updating swift) but when I reach, didSet in detailItem, I call configureView. In configureView I check to see if detailItem is indeed set and then start assigning values to my outlets.
if let detail: Posting = self.detailItem {
print("detail title is ")
println(detail.title)
// Title
self.titleLabel.text = detail.title
However this crashes with output:
detail title is Skiing in Vail
fatal error: unexpectedly found nil while unwrapping an Optional value
The error is on the line:
self.titleLabel.text = detail.title
I don't understand why it is crashing when it is is clearly set...
Note that this doesn't happen if I call configureView from within viewDidLoad.
This only happens when I call it from
var detailItem: Posting? {
didSet { self.configureView() }
}
Something I'm missing? Is this working asynchronously or something?
Referencing your comment on how to implement this, there are a few trains of thought. First, the reason you can modify it directly from your MasterVC is because the IBOutlet hasn't been instantiated in the DetailVC and thus isn't available to be modified yet.
One option, which I'd probably follow, is to have a helper variable on your detail view where you can place the value. Then viewWillAppear() you can take whatever's in the variable and set it as the label's text. Here's a tutorial on it, but to answer your specific quesiton please jump to the prepareForSegue method in this tutorial. It gives a good rundown of the whole process:
http://www.codingexplorer.com/segue-swift-view-controllers/
Let me know if this doesn't solve your problem.
I am trying to send a double value from a UIView (which is loaded from a XIB) to a ViewController using a delegate
Here is my protocol, it is just sending a double to the main ViewController on a button click.
protocol HoursWorkedDelegate{
func sendHoursWorked(hoursWorked: Double);
}
class HoursWorkedView: UIView{
var delegate: HoursWorkedDelegate?;
#IBAction func calculateHoursWorked(sender: AnyObject){
// Do some calculations for the hoursWorked value
// Give value
delegate!.sendHoursWorked(hoursWorked);
}
}
// This class now wants that Double value
class ViewController: UIViewController, HoursWorkedDelegate{
// Conform to protocol
func sendHoursWorked(hoursWorked: Double){
// Lets say we just want to assign this value to a textField
hoursWorkedTextField.text = NSString(format: "%.4f", hoursWorked);
}
}
The error message I get is Thread 1: EXC_BAD_INSTRUCTION(code = EXC_I386_INVOP, subcode=0x0)
Any help would be much appreciated, Thank You!
As a start, change the exclamation point in this snippet to a question mark:
delegate!.sendHoursWorked(hoursWorked);
This is what's likely causing the crash, as you are force-unwrapping the optional delegate property. A question mark means we'll only call sendHoursWorked() on the delegate if the delegate exists.
That fix will now probably mean that your program is no longer crashing, but you still don't get the desired results, because sendHoursWorked() is never called. We have to tell our HoursWorkedView object who is delegating it.
Somewhere in your code, you might have something like this:
let hoursWorkedView = HoursWorkedView()
self.view.addSubview(hoursWorkedView)
It's right here where we should be setting the delegate:
let hoursWorkedView = HoursWorkedView()
hoursWorkedView.delegate = self
self.view.addSubview(hoursWorkedView)
Though if it's me, I probably add a constructor to HoursWorkedView that accepts the delegate property:
init(delegate: HoursWorkedDelegate) {
super.init()
self.delegate = delegate
}
And now we can just do this:
let hoursWorkedView = HoursWorkedView(delegate: self)
self.view.addSubview(hoursWorkedView)
I think you're getting your view and your viewcontroller mixed up: a ViewController controls things; a view just displays them. The viewController tells the view what to display.
So, you want to connect your button to the viewController -- not the view. And you don't need a custom view class or a delegate.
Set it up like this:
create a textField and a button
create an outlet for the textField
put calculateHoursWorked directly in your viewController
create an action to connect the button to calculateHoursWorked
in calculateHoursWorked, set self.textField.text to the result of the calculation (where "textField" is whatever you named your outlet)
You wouldn't use a delegate in this context because the viewController knows everything the view does. The delegate pattern is for cases where one object has no visibility into another.
EDIT:
That being said, the bug here is that the delegate isn't actually being set anywhere.
Swift Optionals (the ! and ?) help prevent cases like this. If you explicitly unwrap an optional using !, you have to make sure it's always defined. In this case, since delegate is defined as optional (?) you have to check it:
#IBAction func calculateHoursWorked(sender: AnyObject){
// Do some calculations for the hoursWorked value
// Give value
if let currentDelegate = self.delegate {
currentDelegate.sendHoursWorked(hoursWorked)
}
}