Swift 3:从数组中解包可选值的最安全方法是什么?

4

首先,我初始化变量以保存股票数据。

var applePrice: String?
var googlePrice: String?
var twitterPrice: String?
var teslaPrice: String?
var samsungPrice: String?
var stockPrices = [String]()

我从YQL获取当前股票价格,并将这些值放入数组中。

func stockFetcher() {

    Alamofire.request(stockUrl).responseJSON { (responseData) -> Void in
        if((responseData.result.value) != nil) {
            let json = JSON(responseData.result.value!)
            if let applePrice = json["query"]["results"]["quote"][0]["Ask"].string {
                print(applePrice)
                self.applePrice = applePrice
                self.tableView.reloadData()
            }
            if let googlePrice = json["query"]["results"]["quote"][1]["Ask"].string {
                print(googlePrice)
                self.googlePrice = googlePrice
                self.tableView.reloadData()
            }
            if let twitterPrice = json["query"]["results"]["quote"][2]["Ask"].string {
                print(twitterPrice)
                self.twitterPrice = twitterPrice
                self.tableView.reloadData()
            }
            if let teslaPrice = json["query"]["results"]["quote"][3]["Ask"].string {
                print(teslaPrice)
                self.teslaPrice = teslaPrice
                self.tableView.reloadData()
            }
            if let samsungPrice = json["query"]["results"]["quote"][4]["Ask"].string {
                print(samsungPrice)
                self.samsungPrice = samsungPrice
                self.tableView.reloadData()
            }
            let stockPrices = ["\(self.applePrice)", "\(self.googlePrice)", "\(self.twitterPrice)", "\(self.teslaPrice)", "\(self.samsungPrice)"]
            self.stockPrices = stockPrices
            print(json)
        }
    }
}

在“cellForRowAt indexPath”函数中,我将文本输出到标签中。
    if self.stockPrices.count > indexPath.row + 1 {
        cell.detailTextLabel?.text = "Current Stock Price: \(self.stockPrices[indexPath.row])" ?? "Fetching stock prices..."
    } else {
        cell.detailTextLabel?.text = "No data found"
    }

我遇到了一个问题,需要打印当前股票价格:Optional("stock price"),其中包含单词optional。我猜测这是因为我给它提供了一个可选值的数组,但实际上我不知道是否会从YQL获取数据,其中5只股票中可能有一只是nil,而其他股票则有数据。从阅读其他类似的问题中,我可以看出解决方案是使用!来解包值,但当它是一个可能为空的数据数组时,我不太确定如何实现该解决方案,而不仅仅是一个Int或其他数据类型。
如何安全地解包并消除Optional这个单词?

不要使用字符串插值将数据添加到数组中;直接添加字符串即可(例如 [self.applePrice, self.googlePrice,...]),但 Duncan 的方法是正确的。 - Paulw11
3个回答

3

首先:

任何时候,当你多次重复相同的代码块,且只是将一个值从0增加到某个最大值时,这是一种代码异味(code smell)。你应该考虑使用不同的方式来处理它。

你应该使用一个数组来进行处理。

那么,用一组枚举变量作为索引如何呢:

enum companyIndexes: Int {
  case apple
  case google
  case twitter
  case tesla
  //etc...
}

现在您可以使用循环遍历数组,并更加清晰地安装您的值:
var stockPrices = [String?]()
Alamofire.request(stockUrl).responseJSON { (responseData) -> Void in
    if((responseData.result.value) != nil) {
        let json = JSON(responseData.result.value!)
        let pricesArray = json["query"]["results"]["quote"]
        for aPriceEntry in pricesArray {
           let priceString = aPriceEntry["ask"].string
           stockPrices.append(priceString)
        }
   }
}

从数组中获取价格:

let applePrice = stockPrices[companyIndexes.apple.rawValue]

这将导致一个可选项。

您可以使用空合运算符 (??) 将 nil 值替换为字符串,例如 "无价格可用。":

let applePrice = stockPrices[companyIndexes.apple.rawValue] ?? "No price available"

或者如其他答案所示:
if let applePrice = stockPrices[companyIndexes.apple.rawValue] {
   //we got a valid price
} else
   //We don't have a price for that entry
}

您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - KingTim
这是Swift 101的基础内容。将枚举定义放在文件顶部。如果你只需要从单个类访问价格数组,请将其放在该类的定义内部。如果你需要从多个类访问价格,请将其放在类定义之前。 - Duncan C
将我发布的代码放在你的Alamofire.request()函数的完成处理程序中,就像你的一样。这是一种更清晰的处理股票数组价格解析的方法,而不是反复重复相同的代码。 - Duncan C
我的帖子旨在提供一种更好的做事方式的通用指南,而不是一个完整的解决方案。你需要根据自己的需求进行调整。 - Duncan C
我刚开始接触编程,这是一个练习项目,所以我非常感谢任何指导。我已经按照应该的方式实现了代码,但是在letPriceString = aPriceEntry这一行仍然出现错误“类型string/json没有下标成员”。 - KingTim
所以你需要将它转换为正确的类型。你可以使用 let priceString = (aPriceEntry as! [String:Double])["ask"].string(我不确定 "ask" 的值是什么。由于你使用 .string 将其转换为字符串,我假设它是像 Double 这样的数字类型...) - Duncan C

0

我正在 Xcode 之外编写这篇文章(所以可能会有错别字),但这种逻辑应该是可行的。

if self.stockPrices.count > indexPath.row + 1 {
    var txt = "Fetching stock prices..."
    if let price = self.stockPrices[indexPath.row] {
        txt = price
    }
    cell.detailTextLabel?.text = txt
} else {
    cell.detailTextLabel?.text = "No data found"
}

0

为了安全地解包,请使用以下代码:

if let currentStockPrice = self.stockPrices[indexPath.row]
{
    // currentStockPrice available there
}
// currentStockPrice unavailable

如果您需要在一个if语句后面解包多个变量,可能会导致代码难以阅读。在这种情况下,请使用以下模式。
guard let currentStockPrice = self.stockPrices[indexPath.row]
else
{
    // currentStockPrice is unavailable there
    // must escape via return, continue, break etc.
}
// currentStockPrice is available

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接