从catch中的do块引用变量

时间:2019-03-20 02:02:14

标签: swift try-catch

我需要访问do语句中的变量。在您不使用if语句之外的变量的意义上,它的行为是否像else-if一样?

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {
    override func loadView() {

        do {
            //let url = URL?("https://www.hackingwithswift.com")
            let TECIONEXContent = try String(contentsOf: URL("https://www.hackingwithswift.com"))

        } catch { print("error")}

            //I need to access TECIONEXContent variable outside the do statement

            // Error: Use of unresolved identifier 'TECIONEXContent'
            var TECGrid = TECIONEXContent.components(separatedBy: "\n")
    }
}

错误在最后一行,“未解决的标识符”。

3 个答案:

答案 0 :(得分:2)

  

在您不使用if语句之外的变量的意义上,它的行为类似于else-if语句吗?

是的。但是就像if-else语句一样,您可以在do-catch之前定义变量:

例如在if-else语句中:

let foo: String

if bar > 1 {
    foo = "bigger than one"
} else {
    foo = "one or smaller"
}

或者,就您而言:

let url = URL(string: "https://www.hackingwithswift.com")!

let contents: String

do {
    contents = try String(contentsOf: url)
} catch { 
    print(error)
    return
}

let grid = contents.components(separatedBy: "\n")

或者,您实际上并没有对错误消息做任何事情,可以完全消除do-catch

guard let contents = try? String(contentsOf: url) else {
    print("error")
    return
}

let grid = contents.components(separatedBy: "\n")

坦率地说,使用String(contentsOf:)可能不是最好的模式,因为它执行同步的网络请求,如果操作系统的“看门狗”进程毫不客气地杀死了您的应用程序,则可能会冒着危险。主线程被阻塞;即使没有发生,在网络请求进行过程中冻结应用程序也不是一种良好的用户体验。通常我们会使用URLSession

let url = URL(string: "https://www.hackingwithswift.com")!

URLSession.shared.dataTask(with: url) { data, response, error in
    guard
        let data = data,
        let httpResponse = response as? HTTPURLResponse,
        let string = String(data: data, encoding: .utf8) else {
            print(error ?? "Unknown error")
            return
    }

    guard 200 ..< 300 ~= httpResponse.statusCode else {
        print("Expected 2xx response, but got \(httpResponse.statusCode)")
        return
    }

    let grid = string.components(separatedBy: "\n")

    DispatchQueue.main.async {
        // use `grid` here
    }
}.resume()

无关,但是:

  • 约定是用小写字母开头的变量名。

  • 您实现了loadView。我们很少这样做,而是执行viewDidLoad,并确保也调用super.viewDidLoad()

  • 如果您正在操场上这样做,显然还应该设置needsIndefiniteExecution(如果尚未设置)。

因此:

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        performRequest()
    }

    func performRequest() {
        let url = URL(string: "https://www.hackingwithswift.com")!

        URLSession.shared.dataTask(with: url) { data, response, error in
            guard
                let data = data,
                let httpResponse = response as? HTTPURLResponse,
                let string = String(data: data, encoding: .utf8) else {
                    print(error ?? "Unknown error")
                    return
            }

            guard 200 ..< 300 ~= httpResponse.statusCode else {
                print("Expected 2xx response, but got \(httpResponse.statusCode)")
                return
            }

            let grid = string.components(separatedBy: "\n")

            DispatchQueue.main.async {
                print(grid)
                // use `grid` here
            }
        }.resume()
    }
}

PlaygroundPage.current.liveView = ViewController()
PlaygroundPage.current.needsIndefiniteExecution = true

答案 1 :(得分:1)

您的代码中有四个(!)非常糟糕的做法。

  1. 从不从远程URL像String(contentsOf这样的API同步加载数据。使用URLSession之类的异步API。
  2. 从不catch块中打印无意义的文字字符串。打印error实例。
  3. 根据命名约定,变量名称应为 lowerCamelCased
  4. do - catch块中,始终将所有 good 代码放在do范围内。这样可以解决您的问题。

    do {
        let tecionexContent = try String(contentsOf: URL("https://www.hackingwithswift.com")!)
        let tecGrid = tecionexContent.components(separatedBy: "\n")
    } catch { print(error) }
    

    推荐

    URLSession.shared.dataTask(with: URL("https://www.hackingwithswift.com")!) { data, _ , error in 
       if let error = error { print(error); return }
       let tecionexContent = String(data: data!, encoding: .utf8)!
       let tecGrid = tecionexContent.components(separatedBy: "\n")
    
    }.resume()
    

答案 2 :(得分:0)

问题

代码的问题是,您正在块/闭包(这是局部作用域)中定义变量。您的变量应位于两个块都可以看到的范围内。这意味着在catch块中,变量不存在。具体来说,您尝试从实例化其闭合的闭包外部引用名为TECIONEXContent的字符串变量。

关于某些常规样式要点:请坚持使用变量的快速命名约定(即驼峰式大小写),而类等应大写。 (出于在操场上运行以下代码的目的,我使用了任意函数​​名称,但您可以从生命周期方法中使用它。)

基本游乐场演示代码

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {

    private var tecGrid: [String]? {
        didSet {
            // Update some UI Here (insuring your on the main thread)
            print(self.tecGrid)
        }
    }

     func test() {
        do {
             var texionicContent = try String(contentsOf:URL(string: "https://www.hackingwithswift.com")!)
             tecGrid = texionicContent.components(separatedBy: "\n")
        }
        catch let error {
            print("Catch the error")
        }
    }
}


let play = MyViewController(nibName: nil, bundle: nil)
play.test()