Can you send messages to nil in Swift the same way you can in Objective-C without causing a crash?
I tried looking into the documentation and couldn't find anything relating to this.
Not exactly, you have to use Optional Chaining. In swift, an instance can only be nil if it is declared as an "optional" type. Normally this looks like this:
var optionalString : String?
Notice the ? after the String That is what makes it possible to be nil
You cannot call a method on that variable unless you first "unwrap" it, unless you use the aforementioned Optional Chaining.
With optional chaining you can call multiple methods deep, that all allow for a nil value to be returned:
var optionalResult = optionalString.method1()?.method2()?.method3()
optionalResult can also be nil. If any of the methods in the chain return nil, methods after it are not called, instead optionalResult immediately gets set to nil.
You cannot deal directly with an optional value until you explicitly handle the case that it is nil. You can do that in one of two ways:
Force it to unwrap blindly
println(optionalString!)
This will throw a runtime error if it is nil, so you should be very sure that it is not nil
Test if it is nil
You can do this by using a simple if statement:
if optionalString {
println(optionalString!)
}
else {
// it was nil
}
or you can assign it to a scoped variable so that you don't have to forcefully unwrap it:
if let nonoptionalString = optionalString {
println(nonoptionalString)
}
else {
// it was nil
}
Related
This is a fairly short question, but I'm a bit confused on how to fix it.
for item in filteredAndSortedDates {
print(item.datesSectionHeader()) // Returns a value
print(item.value(forKeyPath: "datesSectionHeader") as Any) // Returns nil
// The "as Any" part up above is just to keep the compiler quiet. It doesn't have any meaning as this is just for testing purposes.
}
I'm a bit confused on why this is happening. How come valueForKeyPath is returning nil when the above is returning a value? I'm calling this on an NSDictionary.
This is the log I'm getting:
HAPPENING THIS WEEK
nil
HAPPENING THIS WEEK
nil
HAPPENING THIS WEEK
nil
HAPPENING WITHIN A YEAR
nil
Here's how I'm declaring datesSectionHeader:
extension NSDictionary {
// FIXME
func datesSectionHeader() -> String {
// Doing some work in here.
}
}
NSDictionary modifies the standard behavior of Key-Value Coding so that it accesses the dictionary's contents rather than its properties. It does this by overriding value(forKey:) (which is, in turn, used by value(forKeyPath:)).
As documented, its override of value(forKey:) checks if the key is prefixed by "#". If it's not, it returns the result of object(forKey:), accessing the dictionary contents. If it is prefixed with "#", it strips the "#" and returns the result from the superclass's implementation, which accesses the dictionary's properties.
So, in this particular case, you can access the results from your datesSectionHeader() getter method using the following:
item.value(forKeyPath: "#datesSectionHeader")
From what I understand, Swift was presented as an upgrade from Objective-C for developers to use in their applications. One new concept that went with it is the concept of "optional variables," or any sort of variable that may hold nothing.
In Objective-C, this was almost implicit. You could assign a value of nil to many kinds of variables, but, in Swift, the variable has to be an optional.
For instance, this sort of statement is completely okay in Objective-C:
SKNode *someNode = [SKNode new];
// some methods appear that may change the value of "someNode."
// they are right here. These "methods" might leave "someNode"
// equal to "nil". If not, they might set "someNode" to a node
// equal to a node that exists already.
// check if it's nil.
if (someNode == nil) {
// code to run if it exists
}
else {
// code to run if it doesn't exist
}
And in Swift, this code:
var node = SKNode.new()
// this "node" is created/used like "someNode" is used above.
if node != nil {
// code that will run if node exists
}
else {
// code to run if node doesn't exist
}
will give the error:
Binary operator '!=' cannot be applied to operands of type 'SKNode' and 'nil'
However, change the Swift initialization of node to this, and you'll be gold, because you're explicitly defining node as an optional.
var node : SKNode? = SKNode.new()
May I add, this doesn't work either:
var node = SKNode?.new()
Giving the error:
'SKNode?.Type' does not have a member named 'new'
Why does the node have to be explicitly defined as an optional?
In var node : SKNode? = SKNode.new(), node has to be explicitly defined as an optional because SKNode.new() will never return nil.
The goal of types in Swift is to guarantee that once a variable is defined, its type will never change, and the variable will always have valid data. Defining a variable as an optional (SKNode?) means the variable is an Optional<SKNode> which is NOT equivalent to SKNode (hence 'SKNode?.Type' does not have a member named 'new')
The error Binary operator '!=' cannot be applied to operands of type 'SKNode' and 'nil' that you are receiving is cause because you are trying to check if a non-optional value is Optional.None (or nil), which is a completely unnecessary (and impossible) check in the language.
I want to implement a callback in a swift project just like I used to do in Objective-C
I need a variable of type closure. That closure should take as a parameter an object and return nothing.
var downloadCompleted: (MLBook) -> (Void)!
When I need to trigger the callback I do this:
if self.downloadCompleted {
self.downloadCompleted(book)
}
The compiler complains with this error message:
Type '(MLBook) -> (Void)!' does not conform to protocol 'BooleanType'
If I remove the if statement the compiler says:
Property 'self.downloadCompleted' not initialized
even though it's implicitly unwrapped.
When I try to get the callback:
BookStore.sharedInstance.downloadCompleted{(book: MLBook) -> () in
println("Print if you got the callback")
}
I get this error message:
'(MLBook) -> ()' is not convertible to 'MLBook'
I'm more worried about the last error message as I'm not quite sure what it is trying to tell me.
Any help would be appreciated. Thanks
Here is your working example. You have a number of mistakes which the attached illustrates. Note
I had the download() method return Bool so that the result can be see in this screen shot.
But, your use of an implicitly unwrapped optional (aka '!') is incorrect. Such an optional is used when the value may be nil but will be assigned at a known time and not changed (see Apple documentation for a description). Your downloadCompleted is a true optional (at least in your example use). Thus, better code, which turns out to be slightly simpler is:
2 mistakes. 1st, The whole type should be wrapped in (), then followed a ? or ! as a optional or implicit unwrapped optional. 2nd, you should check with nil, in swift, no implicit boolean conversion.
In your use case, you should use Optional instead of Implicit unwrapped. Because there is big chance that your property has a nil value. With IUO(Implicit unwrapped optional), you skip compiler warning and will get a runtime error.
import Foundation
class MLBook {
var name = "name"
}
class A {
var downloadCompleted: ((MLBook) -> Void)?
func down(){
var book = MLBook()
if let cb = self.downloadCompleted {
cb(book)
}
}
}
var a = A()
a.downloadCompleted = {
(book: MLBook) -> Void in
println(book.name)
}
a.down()
I want to return nil from a function that expects to get id. The compiler complains about this when I'm writing blocks. So, is it ok to return (id)nil;? Or am I misunderstanding something about what nil and id are?
It is valid. nil "should" be type of id (actually it is void * for some reason) but it is totally safe to cast nil to id. Similarly apply to 0 and NULL (they all just 0). And send message to 0 result defined behaviour (nop) so everything is fine.
I have the same problem about return nil in block and this is quote from the answer
:
You're using an inferred return type for your block literal, but you
don't have to.
The correct way to remove that error is to provide a return type for
the block literal:
id (^block)(void) = ^id{
return nil;
};
I have a BOOL property and at the start this property equals NO. I want to set this property to YES from the start of my program, but with opportunity to toggle it.
For this, I made this getter:
-(BOOL)turn
{
if(!_turn)
_turn = YES;
return _turn;
}
This getter set my turn property to YES, but it makes it "constant", always YES.
Why?
I thought
if(!_turn)
construction is specially for the reason where you want set the "not set yet" object value
Can you answer me why this is so? And how can I set my property value to what i want. Thanks.
Look at your action table:
turn: False True
!turn turn = YES Do Nothing
When _turn is false, you flip it too true. When _turn is true, you do nothing (there's no else statement). Then you return the value of _turn. So yea, you are returning true in all cases.
To be honest, the design is not very good. If you want to set the initial value of the variable to a certain value, do that in the init method. Then provide another method that simply toggles the value:
-(BOOL) toggleTurn {
_turn = !_turn;
return _turn;
}
Usually the lazy initialization technique is used with pointers to objects, not with primitive types. This because a BOOL has only two possibile states: NO and YES, there isn't the "undefined state" which is usually associated with nil for objects.
The reason why it doesn't toggle is that you aren't toggling it, you are just setting it to YES when it's equal to NO, but you aren't handling the case when it's equal to YES. If you want to toggle it just do that:
-(BOOL)turn
{
return _turn= !_turn;
}
PS: Whoever could argue that your method isn't properly a getter, because you are altering the variable before returning it. So I suggest to just return _turn without toggling it, and to create another separated method to toggle the variable.
Also I would like to mention that what you are doing is not called lazy initialization, I'll show you a case of lazy initialization:
// In the interface:
#property(nonatomic,readonly) NSNumber* turnObject;
// In newer compiler versions it should be auto synthesized to _turnObject
// In the implementation:
-(BOOL) turn
{
// In this case I am not toggling it
if(!_turnObject) // Equal to if(turnObject==nil)
_turnObject= #(NO); // Equal to _turnObject=[NSNumber numberWithBOOL: NO];
return _turnObject;
}