Swift Programming Language book doesn't discuss this but should all object variables be optional in Swift? Because technically speaking, an object creation can fail and return nil (as in the case of Objective-C). So should all object variables in Swift for Swift classes (or at least for all Foundation classes) be declared as optional?
let obj:NSData? = NSData()
This might be opinion based but I think that usually you want variables to be non-optional.
There are not many objects initializers that can actually return a nil.
In Obj-C you don't check whether all initialisers return nil.
NSArray *array = [[NSArray alloc] init];
if (array != nil) { //would you test this?
}
In the same way, don't let all object variables be optional in Swift. Only the ones when you actually want to check for nil values.
By the way, in pure Swift the Object initializers can't return a nil because they don't actually have a return value. In Swift, the object initialization can't fail, so we are speaking only about Obj-C interoperability here.
One example to answer the comments:
NSData *data = [[NSData alloc] initWithContentsOfFile:#"some_file"];
in Swift:
var data = NSData(contentsOfFile: "some_file")
If the file doesn't exist, in both languages we get a nil. In Swift, we have an implicitly unwrapped optional, so the assignment itself won't fail and we can still test for nil if we want to.
If we expected data could be nil, the behavior would be the same in both languages, we would solve the problem somehow. If we haven't expected it, it's a bug because everything else is undefined behavior.
In Swift, the application will crash early - the first time you try to use data.
In Obj-C, the result will be absolutely random - note that we never expected data to be nil, so our next statement could be:
[array addObject:data];
crashing the application.
However, because of the nature of Obj-C, thousands of statements could happen before the bug actually reveals itself and it can reveal itself very strangely - for example: data parsing could fail, UI layout will fail because items will be missing when we expected them and so on. We could also get a late crash. Nevertheless, the application behavior will be undefined since the nil return because we didn't account for that possibility:
...things may not be executed, but it certainly won't crash the program or leave things in an unstable state...
Both statements from the comment are false.
The late manifestation of errors is one of the biggest problems when debugging Obj-C applications, making them unsafe. Sometimes they don't crash but that doesn't mean they are working correctly. Swift considers and early crash a much better alternative than a hidden nil object propagating itself through the application and crashing the app hours later.
Related
There are cases where you forgot to set a value (so it's actually a bug), and running the program with forced unwrapping can crash the problem, and that can allow you to track down the bug where you forgot to set the value that you should have set.
From posts talking about avoiding forced unwrapping, it's always brought up that forced unwrapping can crash the program therefore it's a bad thing. What's so bad about crashing a problem when it actually has a bug?
Please give examples where forced unwrapping can be bad.
(I'm not saying forced unwrapping is suitable for everything.)
Forced unwrapping (and I'm going to include force-casting as well) should only be used when you, as the programmer, know for a fact that an optional will never actually ever be nil unless that nil represents a clear bug in your code during development (and then you want it to crash).
There are many examples where this type of forced unwrapping is appropriate. Examples include:
Getting the path to a known file in your app bundle (nil means you forgot to target the file during development).
Force casting a call to UITableView dequeueReusableCell (nil means you have a mistake in your storyboard).
Getting a specific component from DateComponents when you just specially asked Calendar for that component (nil mean you have a typo).
There are obviously many other cases where forced-unwrapping is appropriate but you must have a clear understanding of those cases.
But there are just as many runtime decisions that result in optionals that you can't guarantee and such cases should not be forced unwrapped.
Examples include:
Dealing with any user input. Never assume a user enters valid data. Never assume a value can be converted as expected. Always check for nil results.
Parsing JSON results. Never assume the data you get matches some expected format even if that format is clearly documented and always seems to work. Things change over time. Gracefully handle such unexpected data instead of just assuming a value will always be there and of the assumed data type.
Dealing with any API that can throw or return optional results. Things can go wrong. Errors happen. Never assume you will get back a valid answer. Code defensively.
In the end, a developer with the proper experience and understanding of how optionals work, what they mean, and when a value may or may not ever actually be nil is in a position to safely use forced unwrapping when appropriate. Use it wisely.
Never use forced-unwrapping just because Xcode suggested it to make the compiler happy.
Forced unwrapping is bad because your program is not guaranteed to be accessing an actual variable at the time of execution. When this happens your program might be attempting to perform a mathematical calculation on a number that doesn't exist, and your app would crash. Your point of in the development phase if it crashes you would be able to narrow down why the crash happened and fix the issue of it being nil at runtime for your development phase, but what about in production?
For example, if you were retrieving some sort of number from a web service you may want to compare this number to something local, maybe a version number:
if let json = try? JSONSerialization.jsonObject(with: responseData, options: .allowFragments) as? [String: Any],
let serverAPIVersion:NSNumber = json["API_Version_Number"] as? NSNumber {
if self.currentAPIVersion.uintValue < serverAPIVersion.uintValue {
self.updateVersionWith(number: serverAPIVersion)
}
}
In the code above we are safely unwrapping the "API_Version_Number" from the JSON we get from the server. We are safely unwrapping because if there weren't a value for "API_Version_Number" then when we would try to do the comparison to the current version, the program would crash.
// This will crash if the server does not include "API_Version_Number in json response data
let serverAPIVersion:NSNumber = json["API_Version_Number"] as! NSNumber
And in production there are things out of your control (many times a server side issue) that may lead to unpopulated variables. That is why it is best to conditionally unwrap to gain access to values safely in your code to keep things from crashing at execution time.
There's nothing wrong with crashing a program if it actually has a bug...if you can not work your way around the crash
But as #phillip-mills says in his comment:
...asked by people who use forced unwrapping without understanding that's what's causing the crash
We often see examples where people force unwrap optionals that, for some unexpected reason, isn't there, and in that case it makes sense to try to unwrap the optional "gracefully.
An Example of Bad Forced Unwrapping
Lets say you have a backend service that gives you some JSON which you parse into a model object and present in a view.
Lets say you have a model like this one:
struct Person {
let firstName: String?
let lastName: String?
}
And in a UITableView you populate some cells with person objects. Something along the lines of:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//dequeue your cell
//get your person element from some array
//populate
cell.firstNameLabel.text = person.firstName!
cell.lastNameLabel.text = person.lastName!
return cell
}
At some point you probably will end up with a Person without a firstName (you can say that that is a problem with the backend and so on and so forth but....it'll probably happen :) ) If you force unwrap, your app will crash where it shouldn't be necessary. In this case you could have gotten away with a graceful unwrap
if let firstName = person.firstName {
cell.firstNameLabel.text = firstName
}
Or you could have used the Nil Coalescing Operator
let firstName = person.firstName ?? ""
Finally, as #rmaddy says in his answer
Forced unwrapping is bad because your program is not guaranteed to be accessing an actual variable at the time of execution
You can not always be sure that the data is what you expect it to be.
And when you have the possibility to actually only do some operation if you're absolutely sure that the data you're operating on is valid, that is, if you've unwrapped it safely...it'd be stupid not to use that option :)
Hope that helps you.
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.
Say I have a method that takes a CLLocationCoordinate2D. I can't pass nil directly to that in code; the compiler complains. However, the following compiles. So what happens at runtime?
CLLocation* loc = nil;
[self passMeCoordinates:loc.coordinate];
This is an interesting question. I assume your earlier code was:
[self passMeCoordinates:nil];
The compiler complains about this because CLLocationCoordinate2D is not an object. It's a C-style struct. So, you can't pass an object/pointer (nil) where a struct is expected. (They're not the same size, etc.)
If I slightly paraphrase your code to:
CLLocation* loc = nil;
CLLocationCoordinate2D coord = loc.coordinate;
[self passMeCoordinates:coord];
The question comes down to "what value does coord have". As you may know, the rule in Objective-C is that if you send a message to nil (as we do here -- loc.coordinate is equivalent to [loc coordinate]), then you get back 0. But what if we're expecting a struct? As I just mentioned, it's not the same size as an integer. Well, it turns out that the result depends on what compiler you're using:
LLVM 3.0+ (Xcode 4.2+): returns all zeros, so it's equivalent to a coordinate of (0,0): Can I rely on nil UIView returning CGRectZero for its frame?
LLVM Earlier/GCC: a struct can be filled with garbage/undefined contents, so it could be anything.
Loc.coordinate is a structure so you cannot pass nil for that.
And method that return C structure are not safe to be call on nil.
So your warning is not about passing nil as argument.
As of your question about nil as argument. Well it depends on the method, some handle nil gracefully others don't. Refer to the documentation of those method it's usually said if you can or cannot pass nil as argument.
As others have pointed out, you're dealing with a struct property of your location object, and therefore nil will be problematic. You should only be using nil with objects.
But more generally, you should never assume that any method will accept nil as a parameter. It all varies from method to method. For the built-in classes, it will generally specify in the documentation when a nil parameter is acceptable. If it doesn't say that nil is permissible, it's wise to assume that it's not.
Whether your own code accepts a nil parameter is entirely dependent upon what you do with that parameter and if you're doing anything that requires it to not be nil, whether your code is checking for non-nil values and handling appropriately, etc.
As a completely random example, if you have a method that does:
NSMutableArray *array = [NSMutableArray array];
[array addObject:text];
This will generate a NSInvalidArgumentException exception if text is nil.
Bottom line, it all varies, method to method. Refer to the documentation for the methods in question to see if nil is permissible or not.
The one general exception to the "don't use nil unless the documentation says you can" rule is that when sending messages to a nil object (i.e. invoking a method on an object that is nil). That is permissible (but obviously does nothing). As the documentation says:
In Objective-C, you can often send a message to nil with no ill effects. Return values from messages sent to nil are guaranteed to work as long as what is returned is typed as an object.
At runtime nil will get passed to your method. The compiler does not do runtime checking , it only does compile time type checking.
for example:
NSString *myString = [[NSString alloc] init];
if (nil == myString) {
return;
}
Need I do this??Thank you!
No, you don't need to do that, and I can't say I've ever seen code that made pervasive use of that pattern.
Also, the if statement can be shortened to:
if (! myString) {
return;
}
...which is equivalent though no less superfluous. Checking for nil can be useful, but is typically not done immediately after object instantiation. Instead the typical case is to do it to ensure that an object is not over-released, for instance using a pattern like:
if (myObj) {
[myObj release];
myObj = nil;
}
Note that calling any method on nil is allowed in Objective-C, so less harm is caused by an unexpected nil value floating around than in languages like Java where attempting to do anything with a null reference throws an exception.
Objective-C allows to call on nil pointers, that differs the language from many others and allows to skip some checks. Of course, it's still wise to check for == nil in some particular cases, however, you don't have to check every step in the code. Take a look at Apple's documentation and samples and try to follow their style.
Frankly, most iPhone app code produced these days assumes that an out-of-heap condition (the only logical reason for such a simple instantiation operation to fail) never occurs in normal operation. Only when creating large (eg, image) objects might one check for allocation failure.
Of course, init routines can fail for a host of reasons, so checking for nil following a complex instantiation operation may be warranted (depending on the object type). (And remember that many other methods of some objects can return nil under some circumstances as well, so you need to read the specs and code accordingly.)
Does anyone know if the following code could be problematic:
NSString *addchar = nil;
if (case1)
addChar = [[firstname substringToIndex:1] capitalizedString];
else
addChar = [[fullname substringToIndex:1] capitalizedString];
Assume that firstname and fullname aren't null or empty. Does initializing the NSString object and setting it to 'nil' cause some possible problem? It seems to cause my app to freeze, but only for a very few users, and only those users, but it doesn't have anything to do with different input strings or empty strings. So I'm trying to isolate the problem, but I don't know the difference between
NSString *addChar;
and
NSString *addChar = nil;
Thanks.
Either form is perfectly acceptable. The problem you're having is elsewhere. I recommend doing some code profiling with Instruments to figure out where this issue is occurring.
Without the nil initializer, in some cases your variable may be initialized with garbage (whatever was in the memory space previously). There are specific rules regarding which types of variables (scope-based, static storage, etc.) are nil-initialized automatically for you, but I've always found that it's easier to explicitly initialize all variables instead of memorizing those rules.
That said, because both branches of your if statement clobber any previous value of addChar, there should not be any case in which you can see an invalid value. But it's certainly not hurting anything to explicitly initialize to nil, and if a future maintainer comes along and changes the code paths you might find that the initializer saves you!
You should always initialize to nil if variable is not initialized otherwise.
You can send messages to nil they will be ignored.
NSString * str = nil;
NSLog(#"%#", [str description]);
Outputs:
2009-12-15 08:59:03.352 x[11775] (nil)
Of course I don't need to call description explicitly, I'm just demonstrating call to nil.
There is no difference here since you don't read addChar in your code. Moreover, the compiler may sometimes initialize addChar to nil for you if you don't do it explicitly (depending on the scope of addChar's declaration).
See related question at this place.
From now on, when you use ARC, the strong, weak, and autoreleasing stack variables are initialized to nil:
https://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226-CH1-SW5
Additionally, since dawn of times, in ObjC the instance variables are initialized to zero:
Are instance variables set to nil by default in Objective-C?