问题
I was was retrieving data in a .plist in Objective-C whenever my view loaded by the following method -
@interface ListTableViewController ()
@end
@implementation ListTableViewController
NSArray *dict;
-(void)viewDidAppear:(BOOL)animated
{
NSString *arr= [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
arr=[arr stringByAppendingPathComponent:@"datalist.plist"];
if ([[NSFileManager defaultManager]fileExistsAtPath:arr])
{
dict=[NSArray arrayWithContentsOfFile:arr];
NSLog(@"%@",dict);
}
[self.tableView reloadData];
}
Now for Swift I am doing the same thing as -
class ListTableViewController: UITableViewController{
var arr = NSArray()
var plistfinalpath = String()
override func viewDidAppear(animated: Bool){
let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, .UserDomainMask, true)[0]
plistfinalpath = path.stringByAppendingString("/login.plist")
print(plistfinalpath)
print("time of appearing\(arr)")
arr = NSArray(contentsOfFile: plistfinalpath)!
self.tableView.reloadData()
}
}
But since I have to unwrap the value in the line arr = NSArray(contentsOfFile: plistfinalpath)!
when the plist is empty when the app is launched for the first time the array is empty and since I am unwrapping it, it shows an error. So how can I achieve what I was doing in objective C?
回答1:
Just define your arr
variable as optional, like this :var arr:NSArray? = NSArray()
When a variable is defined as non optional, you cannot make it nil
. The NSArray
initializer you used is defined as public convenience init?(contentsOfFile path: String)
- the ?
means, that it can in fact return a nil
. When you used !
, you force unwrapped the value, which results in runtime crash when the value is nil
.
As always, you can read more in the docs
Alternatively, you can do something like this :
if let tempArr = NSArray(contentsOfFile: plistfinalpath) {
arr = tempArr
}
This way, you don't need to have your arr
defined as optional and will only assign to it, when the contents of file where not empty.
回答2:
A complete answer involves understanding the concept of "optional".
What is an optional?
In Swift you can declare a variable as optional appending a ?
after the type. When you do so, it means that the variable can hold a value or not at all. While when you declare a variable as non-optional, the compiler ensures that it holds a value at all times.
- Declaring a variable as optional:
var myOptionalVariable: NSArray?
- Declaring a variable as not optional:
var myVariable = NSArray()
Note how in the non optional var, we're forced by the compiler to give it a value (that in this case is just an empty array). This because the compiler grants that when a variable is not optional, it holds a value in every moment. Notice the difference: the first statement just declares the variable as an optional NSArray, the second statement initializes and assigns a new empty NSArray to the variable.
Why optional is useful?
With the introduction of optionals we have compile time safety about existence of the data contained on our variables. When not using an optional variable, we know for sure that a variable holds a value all the time. At the same time, when using an optional variable, we're forced to deal with the case where a value might be not there, instead of dealing with it silently (that is what happens with nil
in objective-c). Extracting value from an optional is called unwrapping
Unwrapping Optionals (i.e. extract value from optional)
To "extract" the value from an optional variable, you need to unwrap it. There are several ways to unwrap an optional, given myOptional
is an optional variable, here some examples:
if let variable = myOptional {
// I can safely use variable here
}
or even this other syntax:
variable = myOptional ?? defaultNonOptionalValue
// I can safely use variable here
Also, you can do a forced unwrap with
variable = myOptional!
// crash incoming?
You used this way to do the unwrap, but really you're saying to the compiler "Trust me, I know that myOptional
will never be nil" that in your case is not true, and it leads to a crash with the error Fatal error: unexpectedly found nil while unwrapping an Optional values
. You should always avoid forced unwrapped, unless you're really really sure about your data.
Long Story Short
You have 2 viable solutions:
- Do not use forced unwrap (see options above about unwrapping)
- Convert your variable
arr
to an optional (by declaring it in this way:var arr: NSArray?
) and then deal with a (non-forced) unwrap when you use it (i.e. in your tableView datasource methods)
Which one is more correct for your case, depends on the arr
meaning, if it makes sense to be optional, then go for it, otherwise, just leave it non optional and assign some default value.
回答3:
Just use the following code,
if let array = NSArray(contentsOfFile: plistfinalpath){
arr = array
}
In swift, (!) is used to forced unwrapping an optional values. You should use that when the optionals have a value, otherwise you should use the above code.
来源:https://stackoverflow.com/questions/34154199/objective-c-style-retrieving-from-plist-not-working-in-swift