Eu não li muito sobre o Swift, mas uma coisa que notei é que não há exceções. Então, como eles lidam com erros no Swift? Alguém encontrou algo relacionado ao tratamento de erros?
Eu não li muito sobre o Swift, mas uma coisa que notei é que não há exceções. Então, como eles lidam com erros no Swift? Alguém encontrou algo relacionado ao tratamento de erros?
Respostas:
As coisas mudaram um pouco no Swift 2, pois há um novo mecanismo de tratamento de erros, que é um pouco mais semelhante às exceções, mas diferente em detalhes.
Se a função / método quiser indicar que pode gerar um erro, deve conter uma throws
palavra-chave como esta
func summonDefaultDragon() throws -> Dragon
Nota: não há especificação para o tipo de erro que a função realmente pode gerar. Essa declaração simplesmente declara que a função pode lançar uma instância de qualquer tipo implementando ErrorType ou não está lançando.
Para chamar a função, você precisa usar a palavra-chave try, como esta
try summonDefaultDragon()
esta linha normalmente deve estar presente com bloco de captura e captura como este
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
}
Nota: a cláusula catch usa todos os recursos avançados da correspondência de padrões Swift, para que você seja muito flexível aqui.
Você pode propagar o erro, se estiver chamando uma função de lançamento de uma função que é marcada com throws
palavra-chave:
func fulfill(quest: Quest) throws {
let dragon = try summonDefaultDragon()
quest.ride(dragon)
}
Como alternativa, você pode chamar a função de arremesso usando try?
:
let dragonOrNil = try? summonDefaultDragon()
Dessa forma, você obtém o valor de retorno ou nulo, se ocorrer algum erro. Usando esse caminho, você não obtém o objeto de erro.
O que significa que você também pode combinar try?
com declarações úteis como:
if let dragon = try? summonDefaultDragon()
ou
guard let dragon = try? summonDefaultDragon() else { ... }
Por fim, você pode decidir que sabe que o erro não ocorrerá (por exemplo, porque você já verificou os pré-requisitos) e usar a try!
palavra-chave:
let dragon = try! summonDefaultDragon()
Se a função realmente gerar um erro, você receberá um erro de tempo de execução no aplicativo e o aplicativo será encerrado.
Para gerar um erro, use a palavra-chave throw como esta
throw DragonError.dragonIsMissing
Você pode lançar qualquer coisa que esteja em conformidade com o ErrorType
protocolo. Para iniciantes, em NSError
conformidade com esse protocolo, mas você provavelmente gostaria de usar o enum, ErrorType
que permite agrupar vários erros relacionados, potencialmente com dados adicionais, como este
enum DragonError: ErrorType {
case dragonIsMissing
case notEnoughMana(requiredMana: Int)
...
}
As principais diferenças entre o novo mecanismo de erro Swift 2 e 3 e as exceções no estilo Java / C # / C ++ são as seguintes:
do-catch
+ try
+ defer
vs tradicionaltry-catch-finally
sintaxe .do-catch
bloco não capturará nenhum NSException e vice-versa, para isso você deve usar o ObjC.NSError
convenções do método Cocoa de retornar false
(para Bool
retornar funções) ou nil
(para AnyObject
retornar funções) e passar NSErrorPointer
com detalhes de erro.Como um açúcar sintático extra para facilitar o tratamento de erros, existem mais dois conceitos
defer
palavra-chave) que permitem obter o mesmo efeito dos blocos finalmente em Java / C # / etcguard
palavra-chave), que permite escrever um código se / else menos que no código normal de verificação / sinalização de erros.Erros de tempo de execução:
Como Leandros sugere para lidar com erros de tempo de execução (como problemas de conectividade de rede, análise de dados, arquivo de abertura, etc.), você deve usar NSError
como no ObjC, porque o Foundation, AppKit, UIKit, etc. relatam seus erros dessa maneira. Portanto, é mais uma coisa de estrutura do que de linguagem.
Outro padrão frequente que está sendo usado são os blocos de sucesso / falha do separador, como no 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")
})
Ainda assim, o bloco de falha recebeu a NSError
instância frequentemente , descrevendo o erro.
Erros do programador:
Para erros do programador (como acesso fora do limite do elemento da matriz, argumentos inválidos passados para uma chamada de função, etc.), você usou exceções no ObjC. Linguagem Swift não parece ter qualquer suporte ao idioma para exceções (como throw
, catch
, etc palavra-chave). No entanto, como a documentação sugere, ele está sendo executado no mesmo tempo de execução que o ObjC e, portanto, você ainda pode jogar NSExceptions
assim:
NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()
Você simplesmente não pode capturá-los no Swift puro, embora possa optar por capturar exceções no código ObjC.
A questão é se você deve lançar exceções para erros de programadores ou usar afirmações, como a Apple sugere no guia de idiomas.
fatalError(...)
é o mesmo também.
Atualização 9 de junho de 2015 - Muito importante
Swift 2.0 vem com try
, throw
e catch
palavras-chave e o mais interessante é:
O Swift converte automaticamente os métodos Objective-C que produzem erros em métodos que geram um erro de acordo com a funcionalidade nativa de manipulação de erros do Swift.
Nota: Os métodos que consomem erros, como delegar métodos ou métodos que usam um manipulador de conclusão com um argumento de objeto NSError, não se tornam métodos lançados quando importados pelo Swift.
Trecho de: Apple Inc. “Usando o Swift com cacau e o Objective-C (pré-lançamento do Swift 2).” iBooks.
Exemplo: (do livro)
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);
}
O equivalente em swift será:
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)")
}
Lançando um erro:
*errorPtr = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo: nil]
Será propagado automaticamente para o chamador:
throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)
Nos livros da Apple, parece que a linguagem de programação Swift parece que os erros devem ser tratados usando enum.
Aqui está um exemplo do livro.
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)"
}
De: Apple Inc. “A linguagem de programação Swift”. iBooks. https://itun.es/br/jEUH0.l
Atualizar
Dos jornais da Apple, "Usando Swift com cacau e Objective-C". As exceções de tempo de execução não ocorrem usando idiomas rápidos, é por isso que você não tem o try-catch. Em vez disso, você usa o encadeamento opcional .
Aqui está um trecho do livro:
Por exemplo, na lista de códigos abaixo, a primeira e a segunda linhas não são executadas porque a propriedade length e o characterAtIndex: method não existem em um objeto NSDate. A constante myLength é inferida como um Int opcional e é definida como nil. Você também pode usar uma instrução if-let para desembrulhar condicionalmente o resultado de um método ao qual o objeto pode não responder, conforme mostrado na linha três
let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
println("Found \(fifthCharacter) at index 5")
}
Trecho de: Apple Inc. "Usando Swift com cacau e Objective-C". iBooks. https://itun.es/br/1u3-0.l
E os livros também incentivam você a usar o padrão de erro de cacau do Objective-C (NSError Object)
O relatório de erros no Swift segue o mesmo padrão do Objective-C, com o benefício adicional de oferecer valores de retorno opcionais. No caso mais simples, você retorna um valor Bool da função para indicar se ele foi ou não bem-sucedido. Quando você precisar relatar o motivo do erro, poderá adicionar à função um parâmetro de saída NSError do tipo NSErrorPointer. Esse tipo é aproximadamente equivalente ao NSError do Objective-C **, com segurança adicional na memória e digitação opcional. Você pode usar o prefixo & operator para passar uma referência a um tipo NSError opcional como um objeto NSErrorPointer, conforme mostrado na lista de códigos abaixo.
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)")
}
}
Trecho de: Apple Inc. "Usando Swift com cacau e Objective-C". iBooks. https://itun.es/br/1u3-0.l
Não há exceções no Swift, semelhante à abordagem do Objective-C.
No desenvolvimento, você pode usar assert
para capturar os erros que possam aparecer e precisam ser corrigidos antes de ir para a produção.
A NSError
abordagem clássica não é alterada, você envia um NSErrorPointer
, que é preenchido.
Breve exemplo:
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)")
}
f();g();
torna-se f(&err);if(err) return;g(&err);if(err) return;
para o primeiro mês, então ele acaba de se tornarf(nil);g(nil);hopeToGetHereAlive();
O 'Swift Way' recomendado é:
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
}
No entanto, prefiro try / catch, pois acho mais fácil seguir porque move o tratamento de erros para um bloco separado no final, esse arranjo às vezes é chamado de "Caminho Dourado". Sorte que você pode fazer isso com fechamentos:
TryBool {
write("~/Error2")(error: $0) // The code to try
}.catch {
println("write failure 2: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}
Também é fácil adicionar um recurso de nova tentativa:
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
}
A lista para o TryBool é:
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)
}
}
Você pode escrever uma classe semelhante para testar um valor retornado opcional em vez de valor 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)
}
}
A versão TryOptional aplica um tipo de retorno não opcional que facilita a programação subseqüente, por exemplo, '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
Usando o 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
}
Observe o desempacotamento automático.
Editar: Embora esta resposta funcione, é pouco mais que o Objective-C transliterado para o Swift. Foi tornado obsoleto pelas alterações no Swift 2.0. A resposta de Guilherme Torres Castro acima é uma introdução muito boa à maneira preferida de lidar com erros no Swift. VOS
Demorou um pouco para descobrir isso, mas acho que já o sussurrei. Parece feio embora. Nada além de uma capa fina sobre a versão Objective-C.
Chamando uma função com um parâmetro 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
}`
Escrevendo a função que recebe um parâmetro de erro ...
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...
}
Wrapper básico em torno do objetivo C que fornece o recurso try catch. https://github.com/williamFalcon/SwiftTryCatch
Use como:
SwiftTryCatch.try({ () -> Void in
//try something
}, catch: { (error) -> Void in
//handle error
}, finally: { () -> Void in
//close resources
})
Esta é uma resposta de atualização para o Swift 2.0. Estou ansioso por um modelo de manipulação de erros rico em recursos, como em java. Finalmente, eles anunciaram as boas novas. aqui
Modelo de tratamento de erros: o novo modelo de tratamento de erros no Swift 2.0 parecerá instantaneamente natural, com palavras-chave familiares de tentativa, lançamento e captura . O melhor de tudo foi que ele foi projetado para funcionar perfeitamente com os SDKs da Apple e o NSError. De fato, o NSError está em conformidade com o ErrorType de um Swift. Você definitivamente vai querer assistir à sessão da WWDC no What's New in Swift para ouvir mais sobre isso.
por exemplo :
func loadData() throws { }
func test() {
do {
try loadData()
} catch {
print(error)
}}
Como Guilherme Torres Castro disse, em Swift 2,0, try
, catch
,do
pode ser utilizado na programação.
Por exemplo, em CoreData buscar método de dados, em vez de colocar &error
como um parâmetro para o managedContext.executeFetchRequest(fetchRequest, error: &error)
, agora só precisa usar uso managedContext.executeFetchRequest(fetchRequest)
e, em seguida, manipular o erro com try
, catch
( a Apple Document Link )
do {
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
if let results = fetchedResults{
people = results
}
} catch {
print("Could not fetch")
}
Se você já baixou o xcode7 Beta. Tente pesquisar lançando erros em Documentations and API Reference e escolha o primeiro resultado exibido, pois fornece uma idéia básica do que pode ser feito para essa nova sintaxe. No entanto, a documentação completa ainda não está publicada para muitas APIs.
Técnicas mais sofisticadas de tratamento de erros podem ser encontradas em
O que há de novo no Swift (2015 Sessão 106 28m30s)
A manipulação de erros é um novo recurso do Swift 2.0. Ele usa os try
, throw
e catch
palavras-chave.
Veja o anúncio do Apple Swift 2.0 no blog oficial do Apple Swift
Lib agradável e simples para lidar com a exceção: TryCatchFinally-Swift
Como alguns outros, ele envolve os recursos de exceção do objetivo C.
Use-o assim:
try {
println(" try")
}.catch { e in
println(" catch")
}.finally {
println(" finally")
}
Começando com o Swift 2, como outros já mencionaram, a manipulação de erros é melhor realizada através do uso de enums do / try / catch e ErrorType. Isso funciona muito bem para métodos síncronos, mas é necessária um pouco de inteligência para o tratamento de erros assíncronos.
Este artigo possui uma ótima abordagem para esse problema:
https://jeremywsherman.com/blog/2015/06/17/using-swift-throws-with-completion-callbacks/
Para resumir:
// 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()
}
então, a chamada para o método acima seria a seguinte:
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
}
})
Isso parece um pouco mais limpo do que ter um retorno de chamada errorHandler separado passado para a função assíncrona, que era como isso seria tratado antes do Swift 2.
O que eu vi é que, devido à natureza do dispositivo, você não deseja lançar um monte de mensagens enigmáticas de manipulação de erros para o usuário. É por isso que a maioria das funções retorna valores opcionais, então você apenas codifica para ignorar o opcional. Se uma função retornar nula, significando que falhou, você poderá exibir uma mensagem ou o que for.