Swift 2: Cuộc gọi có thể ném, nhưng nó không được đánh dấu bằng 'thử' và lỗi không được xử lý


161

Sau khi tôi cài đặt Xcode 7 beta và chuyển đổi mã nhanh chóng của mình sang Swift 2, tôi đã gặp một số vấn đề với mã mà tôi không thể tìm ra. Tôi biết Swift 2 là mới vì vậy tôi tìm kiếm và tìm hiểu vì không có gì về nó, tôi nên viết một câu hỏi.

Đây là lỗi:

Cuộc gọi có thể ném, nhưng nó không được đánh dấu bằng 'thử' và lỗi không được xử lý

Mã số:

func deleteAccountDetail(){
        let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
        let request = NSFetchRequest()
        request.entity = entityDescription

        //The Line Below is where i expect the error
        let fetchedEntities = self.Context!.executeFetchRequest(request) as! [AccountDetail]

        for entity in fetchedEntities {
        self.Context!.deleteObject(entity)
        }

        do {
            try self.Context!.save()
        } catch _ {
        }

    }

Ảnh chụp nhanh: nhập mô tả hình ảnh ở đây

Câu trả lời:


168

Bạn phải bắt lỗi giống như bạn đang thực hiện save()cuộc gọi của mình và vì bạn đang xử lý nhiều lỗi ở đây, bạn có thể thực hiện trynhiều cuộc gọi liên tiếp trong một khối do-Catch duy nhất, như vậy:

func deleteAccountDetail() {
    let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
    let request = NSFetchRequest()
    request.entity = entityDescription

    do {
        let fetchedEntities = try self.Context!.executeFetchRequest(request) as! [AccountDetail]

        for entity in fetchedEntities {
            self.Context!.deleteObject(entity)
        }

        try self.Context!.save()
    } catch {
        print(error)
    }
}

Hoặc như @ bames53 đã chỉ ra trong các bình luận bên dưới, thông thường tốt hơn là không bắt lỗi ở nơi nó bị ném. Bạn có thể đánh dấu phương thức throwssau đó tryđể gọi phương thức. Ví dụ:

func deleteAccountDetail() throws {
    let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
    let request = NSFetchRequest()

    request.entity = entityDescription

    let fetchedEntities = try Context.executeFetchRequest(request) as! [AccountDetail]

    for entity in fetchedEntities {
        self.Context!.deleteObject(entity)
    }

    try self.Context!.save()
}

Điều này giúp tôi tìm ra nó, Cảm ơn bạn.
Farhad

5
Trên thực tế không bắt buộc phải có ngoại lệ ở đây. Có thể chỉ cần thêm trytừ khóa vào lệnh gọi hàm và khai báo hàm này là func deleteAccountDetail() throw. Hoặc nếu bạn đã đảm bảo rằng hàm sẽ không ném cho đầu vào đã cho, bạn có thể sử dụng try!.
bames53

4
Tôi không đưa vấn đề này lên nitpick, nhưng vì việc xử lý lỗi dựa trên ngoại lệ khá quan trọng mà hầu hết các nơi xảy ra ngoại lệ đều không bắt được ngoại lệ. Có ba loại nơi bắt ngoại lệ là phù hợp. Ở tất cả các nơi khác, mã không nên xử lý ngoại lệ một cách rõ ràng và nên dựa vào deinit()các cuộc gọi ngầm để dọn dẹp (ví dụ: RAII) hoặc đôi khi sử dụng deferđể thực hiện một số dọn dẹp ad hoc. Xem exceptionsafecode.com để biết thêm (Nó nói về C ++, nhưng các nguyên tắc cơ bản cũng áp dụng cho ngoại lệ Swift.)
bames53

Nhưng làm thế nào bạn sẽ chạy chức năng? Nếu tôi đi với cách @ bames53?
Farhad

1
@NickMoore Những gì các nhà phát triển Swift chọn để gọi họ không tạo ra sự khác biệt trong những gì họ thực sự là. Hệ thống xử lý lỗi mới của Swift là một triển khai của các trường hợp ngoại lệ vì thuật ngữ đó thường được sử dụng trong toàn bộ phần còn lại của ngành.
bames53

41

Khi gọi một hàm được khai báo bằng throwsSwift, bạn phải chú thích trang gọi hàm với tryhoặc try!. Ví dụ, đưa ra một chức năng ném:

func willOnlyThrowIfTrue(value: Bool) throws {
  if value { throw someError }
}

chức năng này có thể được gọi là:

func foo(value: Bool) throws {
  try willOnlyThrowIfTrue(value)
}

Ở đây chúng tôi chú thích cuộc gọi với try, gọi cho người đọc rằng hàm này có thể đưa ra một ngoại lệ và bất kỳ dòng mã nào sau đây có thể không được thực thi. Chúng ta cũng phải chú thích chức năng này throws, bởi vì chức năng này có thể ném một ngoại lệ (nghĩa là khi willOnlyThrowIfTrue()ném, sau đó foosẽ tự động bật lại ngoại lệ lên trên.

Nếu bạn muốn gọi một hàm được khai báo là có thể ném, nhưng cái mà bạn biết sẽ không ném trong trường hợp của bạn vì bạn đang cung cấp cho nó đầu vào chính xác, bạn có thể sử dụng try!.

func bar() {
  try! willOnlyThrowIfTrue(false)
}

Bằng cách này, khi bạn đảm bảo rằng mã sẽ không bị ném, bạn không cần phải thêm mã soạn sẵn để vô hiệu hóa lan truyền ngoại lệ.

try!được thi hành tại thời gian chạy: nếu bạn sử dụng try!và chức năng không kết thúc ném, sau đó thực hiện của chương trình của bạn sẽ được chấm dứt với một lỗi runtime.

Hầu hết các mã xử lý ngoại lệ sẽ trông giống như ở trên: hoặc bạn chỉ cần truyền các ngoại lệ lên trên khi chúng xảy ra hoặc bạn thiết lập các điều kiện sao cho các ngoại lệ có thể bị loại trừ. Bất kỳ việc dọn sạch các tài nguyên khác trong mã của bạn sẽ xảy ra thông qua việc hủy đối tượng (tức là deinit()) hoặc đôi khi qua defermã ed.

func baz(value: Bool) throws {

  var filePath = NSBundle.mainBundle().pathForResource("theFile", ofType:"txt")
  var data = NSData(contentsOfFile:filePath)

  try willOnlyThrowIfTrue(value)

  // data and filePath automatically cleaned up, even when an exception occurs.
}

Nếu vì bất kỳ lý do gì bạn đã dọn sạch mã cần chạy nhưng không có deinit()chức năng, bạn có thể sử dụng defer.

func qux(value: Bool) throws {
  defer {
    print("this code runs when the function exits, even when it exits by an exception")
  }

  try willOnlyThrowIfTrue(value)
}

Hầu hết các mã liên quan đến các ngoại lệ chỉ đơn giản là chúng truyền lên cho người gọi, dọn dẹp trên đường đi qua deinit()hoặc defer. Điều này là do hầu hết các mã không biết phải làm gì với lỗi; nó biết điều gì đã sai, nhưng nó không có đủ thông tin về những gì mã cấp cao hơn đang cố làm để biết phải làm gì về lỗi. Nó không biết nếu trình bày một hộp thoại cho người dùng là phù hợp, hoặc nếu nó nên thử lại, hoặc nếu một cái gì đó khác là phù hợp.

Mã cấp cao hơn, tuy nhiên, nên biết chính xác phải làm gì trong trường hợp có lỗi. Vì vậy, các trường hợp ngoại lệ cho phép các lỗi cụ thể nổi lên từ nơi chúng xảy ra ban đầu đến nơi chúng có thể được xử lý.

Xử lý các trường hợp ngoại lệ được thực hiện thông qua các catchbáo cáo.

func quux(value: Bool) {
  do {
    try willOnlyThrowIfTrue(value)
  } catch {
    // handle error
  }
}

Bạn có thể có nhiều câu lệnh bắt, mỗi câu bắt một loại ngoại lệ khác nhau.

  do {
    try someFunctionThatThowsDifferentExceptions()
  } catch MyErrorType.errorA {
    // handle errorA
  } catch MyErrorType.errorB {
    // handle errorB
  } catch {
    // handle other errors
  }

Để biết thêm chi tiết về các thực tiễn tốt nhất có ngoại lệ, hãy xem http://exceptionsafecode.com/ . Nó đặc biệt nhắm vào C ++, nhưng sau khi kiểm tra mô hình ngoại lệ Swift, tôi tin rằng những điều cơ bản cũng áp dụng cho Swift.

Để biết chi tiết về cú pháp Swift và mô hình xử lý lỗi, hãy xem cuốn sách Ngôn ngữ lập trình Swift (Phát hành trước Swift 2) .


Về cơ bản bắt chính nó có thể xử lý lỗi? hoặc chức năng nhập
Farhad

1
@BrianS Tôi không chắc chắn chính xác những gì bạn đang hỏi, đặc biệt là liên quan đến 'chức năng đầu vào', nhưng 'bắt' về cơ bản là một từ đồng nghĩa với 'xử lý' trong bối cảnh ngoại lệ. Điều đó có nghĩa là, bắt một ngoại lệ và xử lý một ngoại lệ là điều tương tự, theo như các ngôn ngữ lập trình có liên quan.
bames53

Tôi có một lỗi tôi không hiểu, bạn có thể giúp tôi không? Invalid conversion from throwing function of type '() throws -> _' to non-throwing function type '(NSData?, NSURLResponse?, NSError?) -> Void'
Farhad

@BrianS Có vẻ như bạn đang sử dụng một chức năng có chữ ký không chính xác ở đâu đó. Một cái gì đó hy vọng sẽ được cung cấp một hàm lấy NSData?, NSURLResponse?, NSError?làm đối số, nhưng bạn đang cung cấp cho nó một hàm không có bất kỳ đối số nào.
bames53

Hoặc một cái gì đó mong đợi một chức năng không được tuyên bố để ném ngoại lệ và bạn đang cung cấp cho nó một chức năng ném ngoại lệ.
bames53
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.