I've been playing around with Swift and discovered that when down casting an object to be inserted into a dictionary, I get a weird warning: Treating a forced downcast to 'String' as optional will never produce 'nil'. If I replace as with as? then the warning goes away.
func test() -> AnyObject! {
return "Hi!"
}
var dict = Dictionary<String,String>()
dict["test"]=test() as String
Apple's documentation says the following
Because downcasting can fail, the type cast operator comes in two different forms. The optional form, as?, returns an optional value of the type you are trying to downcast to. The forced form, as, attempts the downcast and force-unwraps the result as a single compound action.
I'm unclear as to why using as? instead of as is correct here. Some testing reveals that if I change test() to return an Int instead of a String, the code will quit with an error if I continue using as. If I switch to using as? then the code will continue execution normally and skip that statement (dict will remain empty). However, I'm not sure why this is preferable. In my opinion, I would rather the program quit with an error and let me know that the cast was unsuccessful then simply ignore the erroneous statement and keep executing.
According to the documentation, I should use the forced form "only when you are sure that the downcast will always succeed." In this case I am sure that the downcast will always succeed since I know test() can only return a String so I would assume this is a perfect situation for the forced form of down casting. So why is the compiler giving me a warning?
Let's take a closer look at your last line, and explode it to see what's happening:
let temporaryAnyObject = test()
let temporaryString = temporaryAnyObject as String
dict["test"] = temporaryString
The error is on the second line, where you are telling the compiler to enforce that temporaryAnyObject is definitely a String. The code will still compile (assuming you don't treat warnings as errors), but will crash if temporaryAnyObject is not actually a String.
The way as works, with no ?, is basically saying "from now on, treat the result of this expression as that type IF the result is actually of that type, otherwise we've become inconsistent and can no longer run.
The way as? works (with the ?) is saying "from now on, treat the result of this expression as that type IF the result is actually of that type, otherwise the result of this expression is nil.
So in my exploded example above, if test() does return a String, then the as downcast succeeds, and temporaryString is now a String. If test() doesn't return a String, but say an Int or anything else not subclassed from String, then the as fails and the code can no longer continue to run.
This is because, as the developer in complete control, you told the system to behave this way by not putting the optional ? indicator. The as command specifically means that you do not tolerate optional behavior and you require that downcast to work.
If you had put the ?, then temporaryString would be nil, and the third line would simple remove the "test" key/value pair from the dictionary.
This might seem strange, but that's only because this is the opposite default behavior of many languages, like Obj-C, which treat everything as optional by default, and rely on you to place your own checks and asserts.
Edit - Swift 2 Update
Since Swift 2, the forced, failable downcast operator as has been removed, and is replaced with as!, which is much Swiftier. The behavior is the same.
You can solve this warning from two angles. 1. The value you return 2. The type that you are expected to return. The other answer speaks about the 1st angle. I'm speaking about the 2nd angle
This is because you are returning a forced unwrapped and casted value for an optional. The compiler is like, "If you really want to just force cast all optionals then why not just make the expected return parameter to be a non-optional"
For example if you wrote
func returnSomething<T> -> T?{ // I'm an optional, I can handle nils SAFELY and won't crash.
return UIViewController as! T // will never return a safe nil, will just CRASH
}
Basically you told yourself (and the compiler) I want to handle nils safely but then in the very next line, you said nah, I don't!!!
The compiler would give a warning:
Treating a forced downcast to 'T' as optional will never produce 'nil'
An alternative is to remove the ?
func returnSomething<T>() -> T{ // I can't handle nils
return UIViewController() as! T // I will crash on nils
}
Having that said, likely the best way is to not use force cast and just do:
func returnSomething<T>() -> T?{ // I can handle nils
return UIViewController() as? T // I won't crash on nils
}
It looks like there is bug open about this warning fo some years now...
https://bugs.swift.org/browse/SR-4209 So it shows even in situations where it is obvious what you are doing and right.
Related
What is difference between
self?.profile!.id!
and
(self?.profile!.id!)!
XCode converts first to second.
The first one contains self? which means self is optional, leads to let related properties (profile!.id! in your case) related to the existence of the self which is Optional Chaining:
Optional chaining is a process for querying and calling properties,
methods, and subscripts on an optional that might currently be nil. If
the optional contains a value, the property, method, or subscript call
succeeds; if the optional is nil, the property, method, or subscript
call returns nil. Multiple queries can be chained together, and the
entire chain fails gracefully if any link in the chain is nil.
To make it more simpler, you could think of id! nullity is also optional, even if you force unwrapping it, because it is related to the existence of self; If self is nil, profile and id will be also nil implicitly because they are related to the existence of self.
Mentioning: (self?.profile!.id!)! means that the whole value of the chain would be force wrapped.
Note that implementing:
self!.profile!.id!
leads to the same output of
(self?.profile!.id!)!
since self! is force unwrapped, the value of id would not be related to the nullity of self because the compiler assumes that self will always has a value.
However, this approach is unsafe, you should go with optional binding.
First of all you are using too many question and exclamation marks!!!
Practically there is no difference. The result is a forced-unwrapped optional.
Usually Xcode suggests that syntax if the result of the last item of the chaining is a non-optional so the exclamation mark would cause an error for example
text?.count!
Then Xcode suggests
(text?.count)!
but in this case be brave and write
text!.count
I'm trying to wrap my head around why what feels intuitive is illegal when it comes to a dictionary containing an array in Swift.
Suppose I have:
var arr = [1,2,3,4,5]
var dict = ["a":1, "b":2, "test":arr]
I can easily access the dictionary members like so:
dict["a"]
dict["b"]
dict["test"]
As expected, each of these will return the stored value, including the array for key "test":
[1,2,3,4,5]
My intuitive reaction to this based on other languages is that this should be legal:
dict["test"][2]
Which I would expect to return 3. Of course, this doesn't work in Swift. After lots of tinkering I realize that this is the proper way to do this:
dict["test"]!.objectAtIndex(2)
Realizing this, I return to my intuitive approach and say, "Well then, this should work too:"
dict["test"]![2]
Which it does... What I don't really get is why the unwrap isn't implied by the array dereference. What am I missing in the way that Swift "thinks?"
All dictionary lookups in Swift return Optional variables.
Why? Because the key you are looking up might not exist. If the key doesn't exist, the dictionary returns nil. Since nil can be returned, the lookup type has to be an Optional.
Because the dictionary lookup returns an Optional value, you must unwrap it. This can be done in various ways. A safe way to deal with this is to use Optional Chaining combined with Optional Binding:
if let value = dict["test"]?[2] {
print(value)
}
In this case, if "test" is not a valid key, the entire chain dict["test"]?[2] will return nil and the Optional Binding if let will fail so the print will never happen.
If you force unwrap the dictionary access, it will crash if the key does not exist:
dict["test"]![2] // this will crash if "test" is not a valid key
The problem is that Dictionary’s key-based subscript returns an optional – because the key might not be present.
The easiest way to achieve your goal is via optional chaining. This compiles:
dict["test"]?[2]
Note, though, that the result will still be optional (i.e. if there were no key test, then you could get nil back and the second subscript will not be run). So you may still have to unwrap it later. But it differs from ! in that if the value isn’t present your program won’t crash, but rather just evaluate nil for the optional result.
See here for a long list of ways to handle optionals, or here for a bit more background on optionals.
In the Objective-C world, it has potential crash if you are trying to access 3 by dict[#"test"][2]. What if dict[#"test"] is nil or the array you get from dict[#"test"] has two elements only? Of course, you already knew the data is just like that. Then, it has no problem at all.
What if the data is fetched from the backend and it has some problems with it? This will still go through the compiler but the application crashes at runtime. Users are not programmers and they only know: The app crashes. Probably, they don't want to use it anymore. So, the bottom line is: No Crashes at all.
Swift introduces a type called Optional Type which means value might be missing so that the codes are safe at runtime. Inside the Swift, it's actually trying to implement an if-else to examine whether data is missing.
For your case, I will separate two parts:
Part 1: dict["test"]! is telling compiler to ignore if-else statement and return the value no matter what. Instead, dict["test"]? will return nil if the value if missing. The terminology is Explicit Unwrapping
Part 2: dict["test"]?[2] has potential crash. What if dict["test"]? returns a valid array and it only has two element? This way to store the data is the same using dict[#"test"][2] in Objective-C. That's why it has something called Optional Type Unwrapping. It will only go the if branch when valid data is there. The safest way:
if let element = dict["test"]?[2] {
// do your stuff
}
Sometime when I use "as" ,xcode prompts failed and suggests change to "as!".Also I see some construstors is "init?".I know some variables could be difined as optional.What the meaning of a constructor to be optionsal?
I looked up the questions in "the swift programming language",but failed to get the answer.
Use
as
When you believe that a constant or variable of a certain class type may actually refer to an instance of a subclass.
Using
as?
will always return an optional value and if the downcasting wasn't possible it will return nil.
Using
as!
is forced unwrapping of the value. Use "as!" when you're sure that the optional has a value.
init?
is used to write fail-able initialisers. In some special cases where initialisations can fail you write fail-able initiasers for your class or structure.
For your as!-Question please look here. as! force unwraps your optional.
Regarding your init?-Question: This is called a failable initializer. Basically this means your init-method can fail and it will return nil. See the Swift Blog for reference.
If your object can only be created if some condition is met then init? makes sense, since it could return an object or nil. As for as! you should only use that if you're absolutely certain the object is of that type, otherwise use this paradigm:
if let object = obj as? String { ... } .
Ok, this is an odd error and it's taken me many hours to track down the exact location (although the cause remains unknown). The error only occurs on 64-bit devices. In other words, it works fine on my iPhone 5, but crashes on my iPhone 6 Plus.
Essentially, I have a private variable in a class:
class Manager {
private lastUpdated:NSDate?
}
Initially, it's not set. This class retrieves messages from a server and then sets the lastUpdated variable to NSDate().
private func checkForNewMessages(complete:Result<[Message]> -> ()) {
self.server.userMessages(u, fromDate: self.lastUpdated) { result in
result.onSuccess { messages in
self.insertMessages(messages)
self.lastUpdated = NSDate()
complete(Result(messages))
}
result.onError { err in complete(Result(err)) }
}
}
I've deleted extraneous code, but nothing inside the code path that's crashing (just stuff around it)
Note: I'm using a Result enum. For more info about this kind of enum, this article is pretty good at explaining it: Error Handling in Swift.
The first time the manager retrieves messages, everything works fine and the lastUpdated value is set correctly. If I then try to retrieve messages again, the app crashes when it tries to set self.lastUpdated = NSDate().
I can get it to work if I delay the assignment by using GCD, dispatch_after. But I don't want to do that.
Does anyone have any idea why this is occurring?
Update
This is only occurring when I assign a NSDate. It's not occurring if I try to set another object type (Int, Bool, String) etc.
The crash occurs if I change the stored variable to NSTimeInterval and attempt to set it from NSDate.timeIntervalSinceReferenceDate()
The crash occurs if I store an array of updates and attempt to simply append a new date to that array.
The crash occurs if I attempt to set lastUpdated before the server is called rather than in the callback.
The crash occurs if I wrap NSDate in another class.
The crash occurs is I set lastUpdated in a background thread
The crash doesn't occur if I println(NSDate()) (removing the assignment)
Ok, it took me numerous hours but I finally figured it out.
This will be a little difficult to explain and I'm not certain I fully understand what is going on. The error actually occurred in the Server class that was making the request. It took the lastUpdated date, converted it to a string and then sent that up to the server. The first run, there was no date, so no issue. In the second run, it took the date and used a date formatter to convert it to a string. However, I thought that stringFromDate returned an Optional. It does not. Normally, the compiler would warn me if I attempted to unwrap something that wasn't an option. However, I use Swift's functional aspects to use Infix operators to unwrap and conditionally pass the unwrapped value to another function, which can then pass it's value on. It allows me to chain unwraps together so I can avoid "nested if-let hell".
Very simple but powerful:
infix operator >>? { associativity left}
func >>? <U,T>(opt: T?, f: (T -> U?)) -> U? {
if let x = opt {
return f(x)
}
return nil
}
Note that the function must return an Optional. For some reason, the Swift compiler missed the fact that stringFromDate did not return an optional and so it didn't warn me. I'm not sure why that is, it's certainly warned me before:
if let update = fromDate >>? self.dateFormatter.stringFromDate {
This fails, but not immediately
Unwrapping a non-optional value doesn't immediately result in a crash, error or anything apparently. I successfully used the date string to send and receive data from the server. I'm not certain why unwrapping a non-optional string would later result in a crash and that only when I attempted to set the instance variable that of the backing NSDate object the string was generated from. I think somehow, unwrapping a non-optional screwed up some pointers, but not necessarily the pointer to the unwrapped object. I think the crash itself has to do with Swift attempting to release or dealloc the original instance variable (lastUpdated) when a new one is set, but found the memory address messed up. I think only a Apple Swift engineer could tell me what actually happened, but perhaps there are clues here to how Swift works internally.
The NSMetadataItem class in the Cocoa framework under Swift contains the following function:
func valueForAttribute(key: String!) -> AnyObject!
I'm still learning the difference (and details) between forced unwrapping and optional chaining. In the above function, does this mean:
The key parameter must have a value, and
The return value is guaranteed to have a value?
My primary concern is with the exclamation point following the return value - once I have assigned the return value:
var isDownloadedVal = item.valueForAttribute(NSMetadataUbiquitousItemIsDownloadedKey)
Do I need to include an if let block when examining it, or am I guaranteed that it will have a value I can examine safely?
TLDR: Treat Foo! as if it were Foo.
Many Cocoa calls include implicitly unwrapped optionals, and their need for it is very likely the reason the feature even exists. Here's how I recommend thinking about it.
First, let's think about a simpler case that doesn't involve AnyObject. I think UIDevice makes a good example.
class func currentDevice() -> UIDevice!
What's going on here? Well, there is always a currentDevice. If this returned nil that would indicate some kind of deep error in the system. So if we were building this interface in Swift, this would likely just return UIDevice and be done with it. But we need to bridge to Objective-C, which returns UIDevice*. Now that should never be nil, but it syntactically could be nil. Now in ObjC, we typically ignore that fact and don't nil-check here (particularly because nil-messaging is typically safe).
So how would we express this situation in Swift? Well, technically it's an Optional<UIDevice>, and you'd wind up with:
class func currentDevice() -> UIDevice?
And you'd need to explicitly unwrap that every time you used it (ideally with an if let block). That would very quickly drive you insane, and for nothing. currentDevice() always returns a value. The Optional is an artifact of bridging to ObjC.
So they invented a hack to work around that (and I think it really is a hack; I can't imagine building this feature if ObjC weren't in the mix). That hack says, yes, it's an Optional, but you can pretend it's not, and we promise it's always going to be a value.
And that's !. For this kind of stuff, you basically ignore the ! and pretend that it's handing you back a UIDevice and roll along. If they lied to you and return nil, well, that's going to crash. They shouldn't have lied to you.
This suggests a rule: don't use ! unless you really need to (and you pretty much only need to when bridging to ObjC).
In your specific example, this works in both directions:
func valueForAttribute(key: String!) -> AnyObject!
Technically it takes an Optional<String>, but only because it's bridged to NSString*. You must pass non-nil here. And it technically returns you Optional<AnyObject>, but only because it's bridged to id. It promises that it won't be nil.
According to the Swift-eBook, which states the following
„Trying to use ! to access a non-existent optional value triggers a runtime error. Always make sure that an optional contains a non-nil value before using ! to force-unwrap its value.“
I would answer to your first two questions with Yes.
Do I need to include an if let block when examining it...
No, this is not necessary.