MST

星途 面试题库

面试题:Objective-C 异常处理与 NSError 在项目架构中的协作

假设你正在参与一个大型 iOS 项目的架构设计,阐述如何在整体架构层面合理地结合 Objective-C 的异常处理机制和 NSError 设计模式,以确保系统的健壮性和错误处理的有效性。
28.7万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

1. 理解两种错误处理机制

  • Objective - C 异常处理机制:使用@try@catch@finally块。@try块内放置可能抛出异常的代码,@catch捕获并处理异常,@finally块无论是否发生异常都会执行。例如:
@try {
    // 可能抛出异常的代码
    NSArray *array = @[@"1", @"2"];
    NSString *element = array[10]; // 越界访问,会抛出异常
} @catch (NSException *exception) {
    NSLog(@"捕获到异常: %@", exception);
} @finally {
    NSLog(@"无论是否有异常,都会执行这里");
}
  • NSError 设计模式:通过方法的NSError **参数返回错误信息。例如:
NSError *error = nil;
NSString *content = [NSString stringWithContentsOfFile:@"nonExistentFile.txt" encoding:NSUTF8StringEncoding error:&error];
if (!content) {
    NSLog(@"读取文件失败: %@", error);
}

2. 整体架构层面结合方式

2.1 业务逻辑层

  • 轻量级错误:对于业务逻辑中一些相对常见且不影响程序整体运行的错误,优先使用NSError。比如网络请求失败、数据解析错误等。例如,在一个网络请求的封装方法中:
- (void)fetchDataWithCompletion:(void(^)(id data, NSError *error))completion {
    NSURLSessionDataTask *task = [NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:@"http://example.com/api"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error) {
            completion(nil, error);
            return;
        }
        // 数据解析
        NSError *parseError = nil;
        id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
        if (parseError) {
            completion(nil, parseError);
            return;
        }
        completion(json, nil);
    }];
    [task resume];
}
  • 严重错误:对于那些会导致业务逻辑无法继续执行的严重错误,如资源严重不足、关键数据结构损坏等,使用 Objective - C 异常处理机制。例如,在初始化一个核心业务模块时,如果关键配置文件缺失或损坏,抛出异常:
- (instancetype)initWithConfigurationFile:(NSString *)filePath {
    @try {
        if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
            @throw [NSException exceptionWithName:@"ConfigurationFileMissingException" reason:@"关键配置文件缺失" userInfo:nil];
        }
        // 其他初始化逻辑
        self = [super init];
        return self;
    } @catch (NSException *exception) {
        NSLog(@"初始化失败: %@", exception);
        return nil;
    }
}

2.2 数据持久化层

  • 常规错误:数据持久化操作(如数据库插入、更新、删除)中,使用NSError处理常见错误,如数据库锁冲突、磁盘空间不足等。以 SQLite 数据库操作为例:
sqlite3_stmt *stmt;
const char *sql = "INSERT INTO users (name, age) VALUES (?,?)";
if (sqlite3_prepare_v2(database, sql, -1, &stmt, NULL) != SQLITE_OK) {
    NSError *error = [NSError errorWithDomain:@"SQLiteErrorDomain" code:sqlite3_errcode(database) userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithUTF8String:sqlite3_errmsg(database)]}];
    // 处理错误
    return NO;
}
// 绑定参数并执行
if (sqlite3_step(stmt) != SQLITE_DONE) {
    NSError *error = [NSError errorWithDomain:@"SQLiteErrorDomain" code:sqlite3_errcode(database) userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithUTF8String:sqlite3_errmsg(database)]}];
    // 处理错误
    sqlite3_finalize(stmt);
    return NO;
}
sqlite3_finalize(stmt);
return YES;
  • 灾难性错误:如果在数据持久化过程中发生灾难性错误,如数据库文件损坏且无法修复,使用异常处理机制。例如,在数据库迁移过程中,如果发现数据库结构严重损坏:
- (BOOL)migrateDatabase {
    @try {
        // 检查数据库结构
        if (/* 数据库结构严重损坏 */) {
            @throw [NSException exceptionWithName:@"DatabaseCorruptionException" reason:@"数据库结构严重损坏,无法迁移" userInfo:nil];
        }
        // 执行迁移逻辑
        return YES;
    } @catch (NSException *exception) {
        NSLog(@"数据库迁移失败: %@", exception);
        return NO;
    }
}

2.3 视图层

  • 视图加载与交互错误:在视图加载和用户交互相关的错误处理中,使用NSError。例如,加载视图控制器的 nib 文件失败:
NSString *nibName = @"MyViewController";
UINib *nib = [UINib nibWithName:nibName bundle:nil];
if (!nib) {
    NSError *error = [NSError errorWithDomain:@"ViewLoadingErrorDomain" code:1 userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"无法加载 %@ 的 nib 文件", nibName]}];
    // 处理错误,如显示错误提示给用户
    return;
}
  • 视图相关的严重异常:对于那些导致视图无法正常显示或交互的严重异常,如视图层次结构严重混乱,使用 Objective - C 异常处理机制。例如,在自定义视图的布局方法中,如果发现视图层次结构被严重破坏:
- (void)layoutSubviews {
    @try {
        if (/* 视图层次结构严重混乱 */) {
            @throw [NSException exceptionWithName:@"ViewHierarchyCorruptionException" reason:@"视图层次结构严重混乱,无法布局" userInfo:nil];
        }
        // 正常布局逻辑
    } @catch (NSException *exception) {
        NSLog(@"布局失败: %@", exception);
    }
}

3. 异常与 NSError 的传播

  • NSError 传播:在方法调用链中,将NSError从底层向上层传递,让上层调用者决定如何处理错误。例如,数据持久化层的方法返回NSError,业务逻辑层接收并可能进一步传递给视图层。
// 数据持久化层
- (BOOL)saveData:(id)data error:(NSError **)error {
    // 保存逻辑,返回错误
}
// 业务逻辑层
- (BOOL)processAndSaveData:(id)data error:(NSError **)error {
    if (![self.dataPersistence saveData:data error:error]) {
        return NO;
    }
    return YES;
}
// 视图层
- (void)saveButtonTapped {
    id data = [self getData];
    NSError *error = nil;
    if (![self.businessLogic processAndSaveData:data error:&error]) {
        // 显示错误信息给用户
    }
}
  • 异常传播:在方法调用链中,如果发生异常,异常会自动向上层传播,直到被@catch块捕获。例如,在业务逻辑层抛出的异常,如果在业务逻辑层没有被捕获,会传播到视图层的@try - @catch块中。
// 业务逻辑层
- (void)doBusinessLogic {
    @try {
        // 可能抛出异常的代码
        if (/* 满足异常条件 */) {
            @throw [NSException exceptionWithName:@"BusinessLogicException" reason:@"业务逻辑错误" userInfo:nil];
        }
    } @catch (NSException *exception) {
        // 也可以选择在这里处理异常,或者不处理,让异常继续传播
    }
}
// 视图层
- (void)viewDidLoad {
    @try {
        [self.businessLogic doBusinessLogic];
    } @catch (NSException *exception) {
        NSLog(@"视图层捕获到业务逻辑异常: %@", exception);
    }
}

4. 日志记录与监控

  • NSError 日志记录:在处理NSError时,记录详细的错误信息到日志中,包括错误域、错误码、错误描述等。可以使用NSLog或专业的日志框架(如 CocoaLumberjack)。例如:
NSError *error = nil;
// 执行可能产生错误的操作
if (error) {
    DDLogError(@"错误域: %@, 错误码: %ld, 错误描述: %@", error.domain, (long)error.code, error.localizedDescription);
}
  • 异常日志记录:在@catch块中,同样记录异常的详细信息,包括异常名称、原因、用户信息等。例如:
@try {
    // 可能抛出异常的代码
} @catch (NSException *exception) {
    DDLogError(@"异常名称: %@, 原因: %@, 用户信息: %@", exception.name, exception.reason, exception.userInfo);
}
  • 监控:可以使用第三方监控工具(如 Firebase Crashlytics、Bugly 等)来收集和分析异常与NSError信息,及时发现系统中的潜在问题。这些工具可以帮助定位错误发生的位置、频率等,以便及时修复。