Xử lý lỗi trong ngôn ngữ Swift


190

Tôi chưa đọc quá nhiều về Swift nhưng có một điều tôi nhận thấy là không có ngoại lệ. Vậy làm thế nào để họ xử lý lỗi trong Swift? Có ai tìm thấy bất cứ điều gì liên quan đến xử lý lỗi?


1
Tôi đã tìm thấy các thông báo lỗi giống như với Obj-C: o
Arbitur

13
@Arbitur cách segfault cũ tốt?
peko

Tạo một NSTimer trong Swift và khi tôi viết sai chức năng thì nó bị lỗi và báo lỗi cho tôi rằng nó không thể tìm thấy phương thức :)
Arbitur

3
Bạn có thể thêm hỗ trợ thử bắt cho Swift bằng cách làm theo các hướng dẫn trong bài viết này: Medium.com/@_willfalcon/adding-try-catch-to-swift-71ab27bcb5b8
William Falcon

@peko Làm thế nào để bạn xử lý một segfault trong Swift? Tôi không nghĩ rằng điều đó có thể xảy ra vào lúc này, điều đáng buồn là một số lỗi không thể sửa chữa được
Orlin Georgiev

Câu trả lời:


148

Swift 2 & 3

Mọi thứ đã thay đổi một chút trong Swift 2, vì có một cơ chế xử lý lỗi mới, có phần giống với ngoại lệ hơn nhưng khác về chi tiết.

1. Chỉ ra khả năng lỗi

Nếu hàm / phương thức muốn chỉ ra rằng nó có thể gây ra lỗi, thì nó nên chứa throwstừ khóa như thế này

func summonDefaultDragon() throws -> Dragon

Lưu ý: không có thông số kỹ thuật cho loại lỗi mà hàm thực sự có thể ném. Tuyên bố này chỉ đơn giản nói rằng hàm có thể ném một thể hiện của bất kỳ loại ErrorType nào hoặc hoàn toàn không ném.

2. Gọi hàm có thể ném lỗi

Để gọi hàm, bạn cần sử dụng từ khóa thử, như thế này

try summonDefaultDragon()

dòng này thường có mặt khối bắt-bắt như thế này

do {
    let dragon = try summonDefaultDragon() 
} catch DragonError.dragonIsMissing {
    // Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
    // Other specific-case error-handlng
} catch {
    // Catch all error-handling
}

Lưu ý: mệnh đề bắt sử dụng tất cả các tính năng mạnh mẽ của khớp mẫu Swift để bạn rất linh hoạt ở đây.

Bạn có thể quyết định tuyên truyền lỗi, nếu bạn đang gọi một hàm ném từ một hàm được đánh dấu bằng throwstừ khóa:

func fulfill(quest: Quest) throws {
    let dragon = try summonDefaultDragon()
    quest.ride(dragon)
} 

Ngoài ra, bạn có thể gọi chức năng ném bằng cách sử dụng try?:

let dragonOrNil = try? summonDefaultDragon()

Bằng cách này, bạn có thể nhận được giá trị trả về hoặc không, nếu có lỗi xảy ra. Sử dụng theo cách này bạn không nhận được đối tượng lỗi.

Điều đó có nghĩa là bạn cũng có thể kết hợp try?với các tuyên bố hữu ích như:

if let dragon = try? summonDefaultDragon()

hoặc là

guard let dragon = try? summonDefaultDragon() else { ... }

Cuối cùng, bạn có thể quyết định rằng bạn biết rằng lỗi sẽ không thực sự xảy ra (ví dụ: vì bạn đã kiểm tra là điều kiện tiên quyết) và sử dụng try!từ khóa:

let dragon = try! summonDefaultDragon()

Nếu chức năng thực sự gây ra lỗi, thì bạn sẽ gặp lỗi thời gian chạy trong ứng dụng của mình và ứng dụng sẽ chấm dứt.

3. Ném lỗi

Để ném lỗi, bạn sử dụng từ khóa throw như thế này

throw DragonError.dragonIsMissing

Bạn có thể ném bất cứ thứ gì phù hợp với ErrorTypegiao thức. Đối với những người mới bắt đầu NSErrortuân thủ giao thức này, nhưng bạn có thể muốn sử dụng enum dựa trên enum ErrorTypecho phép bạn nhóm nhiều lỗi liên quan, có khả năng với các phần dữ liệu bổ sung, như thế này

enum DragonError: ErrorType {
    case dragonIsMissing
    case notEnoughMana(requiredMana: Int)
    ...
}

Sự khác biệt chính giữa cơ chế lỗi Swift 2 & 3 mới và các ngoại lệ kiểu Java / C # / C ++ như sau:

  • Cú pháp hơi khác một chút: do-catch+ try+ deferso với truyền thốngtry-catch-finally cú pháp .
  • Xử lý ngoại lệ thường phát sinh thời gian thực hiện cao hơn nhiều trong đường dẫn ngoại lệ so với đường dẫn thành công. Đây không phải là trường hợp với lỗi Swift 2.0, trong đó đường dẫn thành công và đường dẫn lỗi có giá gần như nhau.
  • Tất cả các mã ném lỗi phải được khai báo, trong khi các ngoại lệ có thể đã bị ném từ bất cứ đâu. Tất cả các lỗi là "ngoại lệ được kiểm tra" trong danh pháp Java. Tuy nhiên, trái ngược với Java, bạn không chỉ định các lỗi có khả năng bị ném.
  • Ngoại lệ Swift không tương thích với ngoại lệ ObjC. do-catchKhối của bạn sẽ không bắt được bất kỳ NSException nào và ngược lại, bạn phải sử dụng ObjC.
  • Các ngoại lệ Swift tương thích với NSErrorcác quy ước của phương thức Ca cao về việc trả về false(cho các Boolhàm trả về) hoặc nil(cho các AnyObjecthàm trả về) và chuyển NSErrorPointervới các chi tiết lỗi.

Là một đường tổng hợp thêm để giảm bớt xử lý lỗi, có hai khái niệm nữa

  • hành động hoãn lại (sử dụng defertừ khóa) cho phép bạn đạt được hiệu quả tương tự như các khối cuối cùng trong Java / C # / etc
  • tuyên bố bảo vệ (sử dụng guardtừ khóa) cho phép bạn viết ít hơn mã if / khác so với mã kiểm tra / báo hiệu lỗi thông thường.

Swift 1

Lỗi thời gian chạy:

Như Leandros đề xuất để xử lý các lỗi thời gian chạy (như các sự cố kết nối mạng, phân tích dữ liệu, mở tệp, v.v.), bạn nên sử dụng NSErrornhư bạn đã làm trong ObjC, vì Foundation, AppKit, UIKit, v.v ... báo cáo lỗi của họ theo cách này. Vì vậy, đó là điều khuôn khổ hơn điều ngôn ngữ.

Một mô hình thường xuyên khác đang được sử dụng là các khối thành công / thất bại như trong AFNetworking:

var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
    success: { (NSURLSessionDataTask) -> Void in
        println("Success")
    },
    failure:{ (NSURLSessionDataTask, NSError) -> Void in
        println("Failure")
    })

Vẫn là khối lỗi thường xuyên nhận được NSError, mô tả lỗi.

Lỗi lập trình viên:

Đối với các lỗi lập trình viên (như truy cập ngoài giới hạn của phần tử mảng, các đối số không hợp lệ được truyền cho một lệnh gọi hàm, v.v.) bạn đã sử dụng các ngoại lệ trong ObjC. Swift ngôn ngữ dường như không có bất kỳ hỗ trợ ngôn ngữ cho trường hợp ngoại lệ (như throw, catch, vv từ khóa). Tuy nhiên, như tài liệu cho thấy nó đang chạy trên cùng thời gian chạy với ObjC, và do đó bạn vẫn có thể ném NSExceptionsnhư thế này:

NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()

Bạn không thể bắt chúng trong Swift thuần túy, mặc dù bạn có thể chọn bắt ngoại lệ trong mã ObjC.

Các câu hỏi là liệu bạn có nên đưa ra ngoại lệ cho lỗi lập trình viên hay sử dụng các xác nhận như Apple gợi ý trong hướng dẫn ngôn ngữ.


20
"Các sự cố kết nối mạng" và "mở tệp" bằng cách sử dụng API ca cao (NSFileHandle) có thể đưa ra các ngoại lệ cần phải bắt. Không có ngoại lệ trong Swift, bạn cần triển khai phần này trong chương trình của mình trong Objective-C hoặc thực hiện tất cả công việc của mình bằng API BSD C (cả hai đều là công việc kém). Xem tài liệu về NSFileHandle.writeData để biết thêm ... developer.apple.com/l Library / ios / documentation / Coloa / Reference / Lỗi :
Matt Gallagher

5
Một lần nữa, không xử lý ngoại lệ có nghĩa là xây dựng đối tượng hai giai đoạn với tất cả các vấn đề cố hữu của nó. Xem stroustrup.com/except.pdf .
Phil

2
các fatalError(...)giống là tốt.
holex

8
Nhiều như tôi thích Swift, tôi nghĩ rằng đây là một sự lựa chọn thảm khốc và đã nếm trải một số hậu quả, họ đang chơi với lửa với sự thiếu sót này ...
Rob

2
Có, các ngoại lệ được kiểm tra hiện được sử dụng một cách tiết kiệm, vì người ta thấy rằng buộc các lập trình viên phải bắt ngoại lệ, họ có rất ít hy vọng phục hồi từ mã gây ô nhiễm theo các cách phá vỡ nguyên tắc trách nhiệm duy nhất. Một lớp cấp miền không muốn phải xử lý các ngoại lệ của lớp cơ sở hạ tầng. Vì vậy, bây giờ, các ngoại lệ không được kiểm tra có xu hướng được ưa chuộng và bên quan tâm có thể bắt chúng, nếu thích hợp. . Đã kiểm tra = chắc chắn có thể phục hồi. Bỏ chọn = không / có khả năng phục hồi.
Jasper Blues

69

Cập nhật ngày 9 tháng 6 năm 2015 - Rất quan trọng

Swift 2.0 đi kèm với try, throwcatchtừ khóa và thú vị nhất là:

Swift tự động dịch các phương thức Objective-C tạo ra lỗi thành các phương thức đưa ra lỗi theo chức năng xử lý lỗi gốc của Swift.

Lưu ý: Các phương thức sử dụng lỗi, chẳng hạn như các phương thức ủy nhiệm hoặc phương thức nhận xử lý hoàn thành với đối số NSError, không trở thành phương thức ném khi được nhập bởi Swift.

Trích từ: Apple Inc., Sử dụng Swift với Ca cao và Objective-C (Phát hành trước Swift 2). Sách điện tử.

Ví dụ: (từ cuốn sách)

NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:@"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success && error){
    NSLog(@"Error: %@", error.domain);
}

Tương đương trong swift sẽ là:

let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("path/to/file")
do {
    try fileManager.removeItemAtURL(URL)
} catch let error as NSError {
    print ("Error: \(error.domain)")
}

Ném một lỗi:

*errorPtr = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo: nil]

Sẽ được tự động truyền tới người gọi:

throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)

Từ sách của Apple, Ngôn ngữ lập trình Swift có vẻ như lỗi nên được xử lý bằng enum.

Dưới đây là một ví dụ từ cuốn sách.

enum ServerResponse {
    case Result(String, String)
    case Error(String)
}

let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")

switch success {
case let .Result(sunrise, sunset):
    let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
    let serverResponse = "Failure...  \(error)"
}

Từ: Apple Inc., Ngôn ngữ lập trình Swift. Sách điện tử. https://itun.es/br/jEUH0.l

Cập nhật

Từ sách tin tức của Apple, "Sử dụng Swift với Ca cao và Mục tiêu-C". Ngoại lệ thời gian chạy không xảy ra bằng cách sử dụng ngôn ngữ nhanh, vì vậy đó là lý do tại sao bạn không thử bắt. Thay vào đó bạn sử dụng Chaining tùy chọn .

Đây là một đoạn trong cuốn sách:

Ví dụ, trong danh sách mã bên dưới, các dòng đầu tiên và thứ hai không được thực thi vì thuộc tính độ dài và phương thức characterAtIndex: không tồn tại trên một đối tượng NSDate. Hằng số myLpm được suy ra là một Int tùy chọn và được đặt thành không. Bạn cũng có thể sử dụng câu lệnh if let let để hủy kết quả một cách có điều kiện kết quả của một phương thức mà đối tượng có thể không đáp ứng, như được hiển thị trên dòng ba

let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
    println("Found \(fifthCharacter) at index 5")
}

Trích từ: Apple Inc., sử dụng Swift với ca cao và khách quan-C. Sách điện tử. https://itun.es/br/1u3-0.l


Và các cuốn sách cũng khuyến khích bạn sử dụng mẫu lỗi ca cao từ Objective-C (NSError Object)

Báo cáo lỗi trong Swift tuân theo cùng một mẫu trong Objective-C, với lợi ích bổ sung là cung cấp các giá trị trả về tùy chọn. Trong trường hợp đơn giản nhất, bạn trả về giá trị Bool từ hàm để cho biết liệu nó có thành công hay không. Khi bạn cần báo cáo lý do lỗi, bạn có thể thêm vào hàm một tham số NSError ngoài loại NSErrorPulum. Loại này gần tương đương với NSError của Objective-C, với sự an toàn bộ nhớ bổ sung và gõ tùy chọn. Bạn có thể sử dụng tiền tố & toán tử để chuyển tham chiếu đến loại NSError tùy chọn dưới dạng đối tượng NSErrorPulum, như được hiển thị trong danh sách mã bên dưới.

var writeError : NSError?
let written = myString.writeToFile(path, atomically: false,
    encoding: NSUTF8StringEncoding,
    error: &writeError)
if !written {
    if let error = writeError {
        println("write failure: \(error.localizedDescription)")
    }
}

Trích từ: Apple Inc., sử dụng Swift với ca cao và khách quan-C. Sách điện tử. https://itun.es/br/1u3-0.l


Đối với câu lệnh cuối cùng, nó phải là: do {try myString.writeToFile (đường dẫn, nguyên tử: true, mã hóa: NSUTF8StringEncoding)} bắt lỗi khi NSError {print (error)}
Jacky

1
@Jacky Vâng, điều đó đúng với swift 2.0 mặc dù câu trả lời này đã được viết trước khi phát hành swift 2.0, tôi đã cập nhật câu trả lời để hiển thị cách xử lý lỗi mới trong swift 2.0. Tôi đã suy nghĩ theo cách này để tham khảo, nhưng tôi sẽ xem xét cập nhật toàn bộ câu trả lời để chỉ sử dụng swift 2.0
Guilherme Torres Castro

12

Không có trường hợp ngoại lệ nào trong Swift, tương tự như cách tiếp cận của Objective-C.

Trong quá trình phát triển, bạn có thể sử dụng assertđể bắt bất kỳ lỗi nào có thể xuất hiện và cần được sửa trước khi đi vào sản xuất.

Cách NSErrortiếp cận cổ điển không bị thay đổi, bạn gửi một cái NSErrorPointer, được phổ biến.

Ví dụ ngắn gọn:

var error: NSError?
var contents = NSFileManager.defaultManager().contentsOfDirectoryAtPath("/Users/leandros", error: &error)
if let error = error {
    println("An error occurred \(error)")
} else {
    println("Contents: \(contents)")
}

6
Điều này đặt ra hai câu hỏi: điều gì xảy ra khi mã ObjC mà chúng ta gọi từ Swift thực sự ném ra một ngoại lệ và liệu NSError có phải là đối tượng lỗi phổ biến của chúng ta như trong ObjC không?
MDJ

1
Có phải thực tế cuộc sống với Swift là người khởi tạo không hoặc không thể thất bại?
Phil

11
Việc xử lý ngoại lệ có vẻ khá bẩn
Tash Pemhiwa

27
Vâng, ai cần ngoại lệ khi bạn có thể gặp sự cố? Hoặc đặt NSError ** làm đối số trong tất cả các hàm bạn khai báo? để mọi thứ f();g();trở thành f(&err);if(err) return;g(&err);if(err) return;trong tháng đầu tiên, sau đó nó sẽ trở thànhf(nil);g(nil);hopeToGetHereAlive();
hariseldon78

2
Câu trả lời này đã lỗi thời (Swift hiện hỗ trợ các ngoại lệ) và sai (Objective-C không hỗ trợ các ngoại lệ.
Rog

11

'Swift Way' được đề xuất là:

func write(path: String)(#error: NSErrorPointer) -> Bool { // Useful to curry error parameter for retrying (see below)!
    return "Hello!".writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: error)
}

var writeError: NSError?
let written = write("~/Error1")(error: &writeError)
if !written {
    println("write failure 1: \(writeError!.localizedDescription)")
    // assert(false) // Terminate program
}

Tuy nhiên, tôi thích thử / bắt hơn vì tôi thấy dễ theo dõi hơn vì nó chuyển việc xử lý lỗi sang một khối riêng ở cuối, sự sắp xếp này đôi khi được gọi là "Đường dẫn vàng". May mắn bạn có thể làm điều này với đóng cửa:

TryBool {
    write("~/Error2")(error: $0) // The code to try
}.catch {
    println("write failure 2: \($0!.localizedDescription)") // Report failure
    // assert(false) // Terminate program
}

Ngoài ra, thật dễ dàng để thêm một cơ sở thử lại:

TryBool {
    write("~/Error3")(error: $0) // The code to try
}.retry {
    println("write failure 3 on try \($1 + 1): \($0!.localizedDescription)")
    return write("~/Error3r")  // The code to retry
}.catch {
    println("write failure 3 catch: \($0!.localizedDescription)") // Report failure
    // assert(false) // Terminate program
}

Danh sách cho TryBool là:

class TryBool {
    typealias Tryee = NSErrorPointer -> Bool
    typealias Catchee = NSError? -> ()
    typealias Retryee = (NSError?, UInt) -> Tryee

    private var tryee: Tryee
    private var retries: UInt = 0
    private var retryee: Retryee?

    init(tryee: Tryee) {
        self.tryee = tryee
    }

    func retry(retries: UInt, retryee: Retryee) -> Self {
        self.retries = retries
        self.retryee = retryee
        return self
    }
    func retry(retryee: Retryee) -> Self {
        return self.retry(1, retryee)
    }
    func retry(retries: UInt) -> Self {
        // For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
        self.retries = retries
        retryee = nil
        return self
    }
    func retry() -> Self {
        return retry(1)
    }

    func catch(catchee: Catchee) {
        var error: NSError?
        for numRetries in 0...retries { // First try is retry 0
            error = nil
            let result = tryee(&error)
            if result {
                return
            } else if numRetries != retries {
                if let r = retryee {
                    tryee = r(error, numRetries)
                }
            }
        }
        catchee(error)
    }
}

Bạn có thể viết một lớp tương tự để kiểm tra giá trị trả về Tùy chọn thay vì giá trị Bool:

class TryOptional<T> {
    typealias Tryee = NSErrorPointer -> T?
    typealias Catchee = NSError? -> T
    typealias Retryee = (NSError?, UInt) -> Tryee

    private var tryee: Tryee
    private var retries: UInt = 0
    private var retryee: Retryee?

    init(tryee: Tryee) {
        self.tryee = tryee
    }

    func retry(retries: UInt, retryee: Retryee) -> Self {
        self.retries = retries
        self.retryee = retryee
        return self
    }
    func retry(retryee: Retryee) -> Self {
        return retry(1, retryee)
    }
    func retry(retries: UInt) -> Self {
        // For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
        self.retries = retries
        retryee = nil
        return self
    }
    func retry() -> Self {
        return retry(1)
    }

    func catch(catchee: Catchee) -> T {
        var error: NSError?
        for numRetries in 0...retries {
            error = nil
            let result = tryee(&error)
            if let r = result {
                return r
            } else if numRetries != retries {
                if let r = retryee {
                    tryee = r(error, numRetries)
                }
            }
        }
        return catchee(error)
    }
}

Phiên bản TryOptional thực thi loại trả về không tùy chọn giúp cho việc lập trình tiếp theo dễ dàng hơn, ví dụ: 'Swift Way:

struct FailableInitializer {
    init?(_ id: Int, error: NSErrorPointer) {
        // Always fails in example
        if error != nil {
            error.memory = NSError(domain: "", code: id, userInfo: [:])
        }
        return nil
    }
    private init() {
        // Empty in example
    }
    static let fallback = FailableInitializer()
}

func failableInitializer(id: Int)(#error: NSErrorPointer) -> FailableInitializer? { // Curry for retry
    return FailableInitializer(id, error: error)
}

var failError: NSError?
var failure1Temp = failableInitializer(1)(error: &failError)
if failure1Temp == nil {
    println("failableInitializer failure code: \(failError!.code)")
    failure1Temp = FailableInitializer.fallback
}
let failure1 = failure1Temp! // Unwrap

Sử dụng TryOptional:

let failure2 = TryOptional {
    failableInitializer(2)(error: $0)
}.catch {
    println("failableInitializer failure code: \($0!.code)")
    return FailableInitializer.fallback
}

let failure3 = TryOptional {
    failableInitializer(3)(error: $0)
}.retry {
    println("failableInitializer failure, on try \($1 + 1), code: \($0!.code)")
    return failableInitializer(31)
}.catch {
    println("failableInitializer failure code: \($0!.code)")
    return FailableInitializer.fallback
}

Lưu ý tự động mở khóa.


7

Chỉnh sửa: Mặc dù câu trả lời này hoạt động, nhưng nó ít hơn so với Objective-C được phiên âm thành Swift. Nó đã bị lỗi thời bởi những thay đổi trong Swift 2.0. Câu trả lời của Guilherme Torres Castro ở trên là phần giới thiệu rất hay về cách xử lý lỗi ưa thích trong Swift. VOS

Phải mất một chút để tìm ra nó nhưng tôi nghĩ rằng tôi đã nói nó. Có vẻ như xấu xí mặc dù. Không có gì hơn một lớp da mỏng so với phiên bản Objective-C.

Gọi một hàm với tham số NSError ...

var fooError : NSError ? = nil

let someObject = foo(aParam, error:&fooError)

// Check something was returned and look for an error if it wasn't.
if !someObject {
   if let error = fooError {
      // Handle error
      NSLog("This happened: \(error.localizedDescription)")
   }
} else {
   // Handle success
}`

Viết hàm có tham số lỗi ...

func foo(param:ParamObject, error: NSErrorPointer) -> SomeObject {

   // Do stuff...

   if somethingBadHasHappened {
      if error {
         error.memory = NSError(domain: domain, code: code, userInfo: [:])
      }
      return nil
   }

   // Do more stuff...
}


5

Trình bao bọc cơ bản xung quanh mục tiêu C cung cấp cho bạn tính năng thử bắt. https://github.com/williamFalcon/SwiftTryCatch

Sử dụng như:

SwiftTryCatch.try({ () -> Void in
        //try something
     }, catch: { (error) -> Void in
        //handle error
     }, finally: { () -> Void in
        //close resources
})

Ý tưởng tốt. Nhưng ai quyết định sử dụng điều này phải ghi nhớ rằng các đối tượng được phân bổ trong khối thử không bị xử lý khi ném ngoại lệ. Điều này có thể gây ra sự cố của các đối tượng zombie và mọi hoạt động sử dụng RAII đều bị xâm phạm (tự động mở khóa, tự động sql-commit, auto-sql-rollback ...). Có lẽ c ++ có thể giúp chúng ta với một số dạng "runAtExit"?
hariseldon78

Cập nhật: tôi chỉ thấy rằng có một lá cờ trong tiếng kêu để cho phép phát hành các đối tượng ở ngoại lệ ném: -fobjc-arc-exceptions. Tôi phải thử nếu nó vẫn hoạt động với phiên bản được bao bọc (tôi nghĩ là nó nên)
hariseldon78

Nếu bạn sử dụng tùy chọn này, hãy lưu ý rằng kích thước mã tăng lên do trình biên dịch phải tạo mã bán ngoại lệ an toàn. Ngoài ra: Dựa vào tính năng biên dịch như vậy có thể không phải là ý tưởng tốt nhất. Các ngoại lệ chỉ dành cho các lỗi lập trình viên vì vậy việc sử dụng tùy chọn trình biên dịch đó chỉ để tiết kiệm một chút bộ nhớ trong quá trình phát triển là không đáng. Nếu bạn có ngoại lệ trong mã sản xuất, bạn nên xử lý vấn đề gây ra những ngoại lệ đó ngay từ đầu.
Christian Kienle

1
Có thể có những tình huống ngoài tầm kiểm soát của bạn. Ví dụ, phân tích cú pháp json ở định dạng sai.
William Falcon

3

Đây là một câu trả lời cập nhật cho swift 2.0. Tôi đang mong đợi mô hình xử lý lỗi phong phú như trong java. Cuối cùng, họ đã thông báo tin tốt. đây

Mô hình xử lý lỗi: Mô hình xử lý lỗi mới trong Swift 2.0 sẽ ngay lập tức cảm thấy tự nhiên, với các từ khóa thử, ném và bắt quen thuộc . Trên hết, nó được thiết kế để hoạt động hoàn hảo với SDK Apple và NSError. Trên thực tế, NSError tuân thủ Loại Lỗi của Swift. Bạn chắc chắn sẽ muốn xem phiên WWDC trên What's New Swift để nghe thêm về nó.

ví dụ :

func loadData() throws { }
func test() {
do {
    try loadData()
} catch {
    print(error)
}}

3

Như Guilherme Torres Castro cho biết, trong Swift 2.0, try, catch, docó thể được sử dụng trong chương trình này.

Ví dụ: Trong phương thức tìm nạp dữ liệu CoreData, thay vì đặt &errorlàm tham số vào managedContext.executeFetchRequest(fetchRequest, error: &error), bây giờ chúng ta chỉ cần sử dụng managedContext.executeFetchRequest(fetchRequest)và sau đó xử lý lỗi với try, catch( Liên kết tài liệu của Apple )

do {
   let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
   if let results = fetchedResults{
      people = results
   }
} catch {
   print("Could not fetch")
}

Nếu bạn đã tải xuống xcode7 Beta. Hãy thử tìm kiếm lỗi ném trong Tài liệu và Tham chiếu API và chọn kết quả hiển thị đầu tiên, nó đưa ra ý tưởng cơ bản về những gì có thể được thực hiện cho cú pháp mới này. Tuy nhiên, tài liệu đầy đủ chưa được đăng cho nhiều API.

Các kỹ thuật xử lý lỗi lạ mắt hơn có thể được tìm thấy trong

Có gì mới trong Swift (Phiên bản 2015 106 28m30s)



1

Lib đẹp và đơn giản để xử lý ngoại lệ: TryCatchFinally-Swift

Giống như một số khác, nó bao quanh các tính năng ngoại lệ C khách quan.

Sử dụng nó như thế này:

try {
    println("  try")
}.catch { e in
    println("  catch")
}.finally {
    println("  finally")
}

Tôi đã thêm một mẫu :)
Morten Holmgaard

Có lẽ đáng để đề cập đến ý kiến ​​của các tác giả: "Cảnh báo: Đây là một hack cho vui và xấu. Chống lại sự cám dỗ để sử dụng nó."
jbat100

1

Bắt đầu với Swift 2, như những người khác đã đề cập, việc xử lý lỗi được thực hiện tốt nhất thông qua việc sử dụng các enum do / try / Catch và ErrorType. Điều này hoạt động khá tốt cho các phương thức đồng bộ, nhưng cần một chút thông minh để xử lý lỗi không đồng bộ.

Bài viết này có một cách tiếp cận tuyệt vời cho vấn đề này:

https://jeremywsherman.com/blog/2015/06/17/USE-swift-throws-with-completion-callbacks/

Để tóm tắt:

// create a typealias used in completion blocks, for cleaner code
typealias LoadDataResult = () throws -> NSData

// notice the reference to the typealias in the completionHandler
func loadData(someID: String, completionHandler: LoadDataResult -> Void)
    {
    completionHandler()
    }

sau đó, cuộc gọi đến phương thức trên sẽ như sau:

self.loadData("someString",
    completionHandler:     
        { result: LoadDataResult in
        do
            {
            let data = try result()
            // success - go ahead and work with the data
            }
        catch
            {
            // failure - look at the error code and handle accordingly
            }
        })

Điều này có vẻ sạch hơn một chút so với việc có một cuộc gọi lại errorHandler riêng được chuyển đến chức năng không đồng bộ, đó là cách điều này sẽ được xử lý trước Swift 2.


0

Những gì tôi đã thấy là do bản chất của thiết bị mà bạn không muốn ném một loạt các thông báo xử lý lỗi khó hiểu vào người dùng. Đó là lý do tại sao hầu hết các hàm trả về các giá trị tùy chọn sau đó bạn chỉ cần mã để bỏ qua tùy chọn. Nếu một chức năng trở lại không có nghĩa là nó thất bại, bạn có thể bật một tin nhắn hoặc bất cứ điều gì.


1
Trả về một con số không trả về không có thông tin về bản chất của lỗi. Nếu một đối tượng lỗi được trả về khi xảy ra lỗi, tùy thuộc vào lỗi, lập trình viên có thể chọn bỏ qua nó, xử lý nó, để nó nổi bong bóng hoặc "bật một thông báo hoặc bất cứ điều gì". Kiên thức là sức mạnh.
Vince O'Sullivan
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.