Swift 与 Objc Exception
一、背景
Swift具备完善的Error handle机制,对于纯Swift下面的Error,在编码的时候能够正确处理。
在使用try? 处理抛出Error的方法的时候,会忽略Error,直接返回nil
二、Swift 与 Objc下面的Exception
下面的代码是经常遇到的一种场景,Swift序列化
private func serialize(message: Message, pretty: Bool) -> String? { var result: String? do { let data = try JSONSerialization.data(withJSONObject: message, options: pretty ? .prettyPrinted : JSONSerialization.WritingOptions(rawValue: 0)) result = String(data: data, encoding: .utf8) } catch let error { log(error) } return result }
虽然这个序列化的API有标注throws标记,但是抛出的异常是objc的异常,这个swift中的try catch是catch不住的
/* Generate JSON data from a Foundation object.
If the object will not produce valid JSON then an exception will be thrown.
Setting the NSJSONWritingPrettyPrinted option will generate JSON with whitespace designed to make the output more readable.
If that option is not set, the most compact possible JSON will be generated.
If an error occurs, the error parameter will be set and the return value will be nil.
The resulting data is a encoded in UTF-8. */ open class func data(withJSONObject obj: Any, options opt: JSONSerialization.WritingOptions = []) throws -> Data
如果传递一个非可序列化的对象,那么将会导致下面的Crash
三、规避方案
Swift部分的方法存在throws的时候,需要特别注意是不是抛出的objc异常,如果是,那么需要特别处理,针对本例子当中,可以采用下面的方法判断一个对象是否可以序列化
/* Returns YES if the given object can be converted to JSON data, NO otherwise. The object must have the following properties: - Top level object is an NSArray or NSDictionary - All objects are NSString, NSNumber, NSArray, NSDictionary, or NSNull - All dictionary keys are NSStrings - NSNumbers are not NaN or infinity Other rules may apply. Calling this method or attempting a conversion are the definitive ways to tell if a given object can be converted to JSON data. */ open class func isValidJSONObject(_ obj: Any) -> Bool
修改之后的代码
guard JSONSerialization.isValidJSONObject(message) else { log("\(message) json object not valid") return nil }
除此之外,stackoverflow上面还提供一种方案,OC下面封装一个方法,这个方法内部catch所有NSException的异常信息,然后作为结果返回出来
比如:https://stackoverflow.com/questions/32758811/catching-nsexception-in-swift
NSError *tryCatch(void(^tryBlock)(), NSError *(^convertNSException)(NSException *)) { NSError *error = nil; @try { tryBlock(); } @catch (NSException *exception) { error = convertNSException(exception); } @finally { return error; } }