如何在Objective-C中实现类似Swift的枚举式错误处理?
嘿,刚从Swift切换到Objective-C,找不到像Swift枚举那样优雅处理错误的方式?太懂这种落差了!Swift的Error枚举确实把错误场景归类得清清楚楚,Objective-C里虽然没有完全一致的语法,但我们可以用它的特性实现类似的效果,下面给你几种实用的方案:
1. 用NS_ENUM定义错误码+NSError传递错误信息
这是Objective-C里最常用的错误处理方式,相当于把Swift枚举的case映射成错误码,再通过NSError **指针把错误对象传递出来,还能像Swift的LocalizedError那样添加本地化描述。
先定义你的错误枚举(用NS_ENUM):
typedef NS_ENUM(NSInteger, MyAppErrorCode) { MyAppErrorCodeInvalidURL, MyAppErrorCodeJSONStringConversionFailed, MyAppErrorCodeJSONStringifyFailed, MyAppErrorCodeHashArrayCastFailed };
然后封装一个创建自定义NSError的工具方法,类似Swift枚举的localizedDescription:
@interface NSError (MyAppErrors) + (instancetype)myAppErrorWithCode:(MyAppErrorCode)code userInfo:(NSDictionary *)userInfo; @end @implementation NSError (MyAppErrors) + (instancetype)myAppErrorWithCode:(MyAppErrorCode)code userInfo:(NSDictionary *)userInfo { NSString *domain = @"com.yourcompany.yourapp.error"; NSString *localizedDescription; switch (code) { case MyAppErrorCodeInvalidURL: localizedDescription = [NSString stringWithFormat:@"无效的URL:%@", userInfo[@"URLString"]]; break; case MyAppErrorCodeJSONStringConversionFailed: localizedDescription = [NSString stringWithFormat:@"JSON字符串转换失败:%@", userInfo[@"FailedString"]]; break; case MyAppErrorCodeJSONStringifyFailed: localizedDescription = @"JSON序列化失败"; break; case MyAppErrorCodeHashArrayCastFailed: localizedDescription = [NSString stringWithFormat:@"无法将对象转换为哈希数组:%@", userInfo[@"InvalidObject"]]; break; default: localizedDescription = @"未知错误"; break; } NSMutableDictionary *info = [NSMutableDictionary dictionaryWithDictionary:userInfo]; info[NSLocalizedDescriptionKey] = localizedDescription; return [NSError errorWithDomain:domain code:code userInfo:info]; } @end
然后在方法里使用它,通过指针传递错误:
- (BOOL)convertToJSONString:(id)object error:(NSError **)error { if (!object) { if (error) { *error = [NSError myAppErrorWithCode:MyAppErrorCodeJSONStringifyFailed userInfo:@{@"InvalidObject": object ?: @"nil"}]; } return NO; } // 尝试序列化逻辑... NSError *serializationError; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object options:0 error:&serializationError]; if (!jsonData) { if (error) { *error = [NSError myAppErrorWithCode:MyAppErrorCodeJSONStringifyFailed userInfo:@{@"OriginalError": serializationError}]; } return NO; } return YES; }
调用的时候就像这样:
NSError *error; BOOL success = [self convertToJSONString:someObject error:&error]; if (!success) { NSLog(@"错误:%@", error.localizedDescription); // 根据错误码做不同处理 if (error.code == MyAppErrorCodeInvalidURL) { // 处理无效URL的情况 } }
2. 结合Block回调传递错误
如果你的方法是异步的,用Block回调传递错误会更自然,这也和Swift的Result类型有点像(OC里可以自定义类似Result的Block结构):
先定义一个包含结果和错误的Block类型:
typedef void(^MyAppCompletionBlock)(id _Nullable result, NSError * _Nullable error);
然后在异步方法里使用:
- (void)fetchDataFromURLString:(NSString *)urlString completion:(MyAppCompletionBlock)completion { NSURL *url = [NSURL URLWithString:urlString]; if (!url) { NSError *error = [NSError myAppErrorWithCode:MyAppErrorCodeInvalidURL userInfo:@{@"URLString": urlString}]; completion(nil, error); return; } // 发起网络请求... NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error) { completion(nil, error); return; } // 解析数据... id parsedData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; if (!parsedData) { NSError *conversionError = [NSError myAppErrorWithCode:MyAppErrorCodeJSONStringConversionFailed userInfo:@{@"OriginalError": error}]; completion(nil, conversionError); return; } completion(parsedData, nil); }]; [task resume]; }
调用的时候:
[self fetchDataFromURLString:@"invalid-url" completion:^(id _Nullable result, NSError * _Nullable error) { if (error) { NSLog(@"请求失败:%@", error.localizedDescription); if (error.code == MyAppErrorCodeInvalidURL) { // 提示用户输入正确的URL } } else { // 处理返回的数据 } }];
3. 额外提示:Objective-C的异常不是用来处理业务错误的
别想着用@try/@catch来处理业务错误哦!OC里的异常一般是用来处理致命错误(比如数组越界、未识别的选择器),而不是像Swift那样处理业务逻辑里的错误场景,苹果官方也不推荐用异常来处理预期内的错误,所以还是老老实实用NSError的方式更规范。
这样下来,虽然语法上不如Swift枚举简洁,但也能实现类似的错误分类、信息传递和本地化描述的效果,完全能覆盖你之前用Swift枚举处理错误的场景啦!
内容的提问来源于stack exchange,提问作者Pavel Bogart

