iOS 性能优化面试题 - 启动/卡顿/耗电/崩溃/包瘦身/死锁
崩溃-治理
本文详细介绍常见崩溃类型的修复方法、防崩溃保护机制以及线上监控体系。 崩溃治理是 APM 稳定性子系的核心模块。完整的监控建设、多类型稳定性问题(Watchdog/OOM/CPU/IO)的统一治理与 Zombie/Coredump/MemoryGraph 等高级归因能力,请参考 APM 系列:指标体系、业界方案。 崩溃治理流程 flowchart TB subgraph S1[1. 崩溃发现] A1[线上监控报警] A2[用户反馈] A3[测试发现] end subgraph S2[2. 崩溃分析] B1[符号化堆栈] B2[问题定位] B3[根因分析] end subgraph S3[3. 问题修复] C1[代码修复] C2[防护措施] C3[回归测试] end subgraph S4[4. 效果验证] D1[灰度发布] D2[监控崩溃率] D3[持续跟踪] end A1 --> S2 A2 --> S2 A3 --> S2 S2 --> S3 S3 --> S4 常见崩溃类型及修复 1. 数组越界 // 崩溃示例 NSArray *array = @[@1, @2, @3]; id obj = array[10]; // NSRangeException // 修复方案1:边界检查 - (id)safeObjectAtIndex:(NSUInteger)index fromArray:(NSArray *)array { if (index < array.count) { return array[index]; } return nil; } // 修复方案2:分类安全方法 @implementation NSArray (Safe) - (id)safeObjectAtIndex:(NSUInteger)index { if (index < self.count) { return [self objectAtIndex:index]; } return nil; } @end // 修复方案3:Swift可选绑定 extension Array { subscript(safe index: Index) -> Element? { return indices.contains(index) ? self[index] : nil } } // 使用 let value = array[safe: 10] // 返回nil而不是崩溃 2. 字典插入nil // 崩溃示例 NSMutableDictionary *dict = [NSMutableDictionary dictionary]; NSString *value = nil; [dict setObject:value forKey:@"key"]; // NSInvalidArgumentException // 修复方案1:nil检查 - (void)setObject:(id)object forKey:(id)key inDict:(NSMutableDictionary *)dict { if (object && key) { dict[key] = object; } } // 修复方案2:分类安全方法 @implementation NSMutableDictionary (Safe) - (void)safeSetObject:(id)object forKey:(id)key { if (object && key) { [self setObject:object forKey:key]; } } @end // 修复方案3:使用NSNull dict[@"key"] = value ?: [NSNull null]; 3. Unrecognized Selector // 崩溃示例 NSString *str = @"hello"; [str performSelector:@selector(count)]; // unrecognized selector // 修复方案1:respondsToSelector检查 if ([obj respondsToSelector:@selector(someMethod)]) { [obj performSelector:@selector(someMethod)]; } // 修复方案2:消息转发防护 @implementation NSObject (CrashProtection) + (void)load { // Hook forwardingTargetForSelector: Method original = class_getInstanceMethod(self, @selector(forwardingTargetForSelector:)); Method swizzled = class_getInstanceMethod(self, @selector(safe_forwardingTargetForSelector:)); method_exchangeImplementations(original, swizzled); } - (id)safe_forwardingTargetForSelector:(SEL)aSelector { // 如果找不到方法,返回一个空实现的对象 id target = [self safe_forwardingTargetForSelector:aSelector]; if (target) return target; // 记录错误 NSLog(@"Unrecognized selector: %@ on %@", NSStringFromSelector(aSelector), self); // 返回一个能处理任何消息的对象 return [CrashStub shared]; } @end 4. KVO崩溃 // 崩溃场景1:重复移除观察者 [obj removeObserver:self forKeyPath:@"value"]; [obj removeObserver:self forKeyPath:@"value"]; // 崩溃 // 崩溃场景2:未移除观察者就释放 - (void)dealloc { // 忘记移除观察者 } // 修复方案:安全的KVO封装 @interface SafeKVOProxy : NSObject @property (nonatomic, weak) id observer; @property (nonatomic, weak) id target; @property (nonatomic, copy) NSString *keyPath; @property (nonatomic, copy) void (^callback)(id newValue); @end @implementation SafeKVOProxy - (instancetype)initWithTarget:(id)target keyPath:(NSString *)keyPath observer:(id)observer callback:(void (^)(id))callback { self = [super init]; if (self) { _target = target; _keyPath = keyPath; _observer = observer; _callback = callback; [target addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL]; } return self; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (self.callback) { self.callback(change[NSKeyValueChangeNewKey]); } } - (void)dealloc { [_target removeObserver:self forKeyPath:_keyPath]; } @end 5. 多线程崩溃 // 崩溃场景:非线程安全的集合操作 // 线程1 [mutableArray addObject:obj1]; // 线程2 [mutableArray removeObjectAtIndex:0]; // 可能崩溃 // 修复方案1:加锁 @property (nonatomic, strong) NSLock *arrayLock; - (void)safeAddObject:(id)object { [self.arrayLock lock]; [self.mutableArray addObject:object]; [self.arrayLock unlock]; } // 修复方案2:使用GCD串行队列 @property (nonatomic, strong) dispatch_queue_t arrayQueue; - (void)safeAddObject:(id)object { dispatch_sync(self.arrayQueue, ^{ [self.mutableArray addObject:object]; }); } // 修复方案3:使用Actor(Swift Concurrency) actor SafeStorage { private var items: [Any] = [] func append(_ item: Any) { items.append(item) } func remove(at index: Int) { guard index < items.count else { return } items.remove(at: index) } func getAll() -> [Any] { return items } } let storage = SafeStorage() await storage.append(obj1) 6. 野指针崩溃 // 崩溃场景 __unsafe_unretained id unsafeRef = obj; obj = nil; [unsafeRef description]; // EXC_BAD_ACCESS // 修复方案1:使用weak而非unsafe_unretained __weak id weakRef = obj; obj = nil; [weakRef description]; // 安全,weakRef为nil // 修复方案2:Zombie Objects检测(调试用) // 在Scheme中启用Zombie Objects // 修复方案3:野指针检测工具 // 使用Address Sanitizer // Product -> Scheme -> Edit Scheme -> Diagnostics -> Address Sanitizer 7. Swift强制解包崩溃 // 崩溃示例 let value: String? = nil print(value!) // Fatal error: Unexpectedly found nil // 修复方案1:可选绑定 if let value = optionalValue { print(value) } // 修复方案2:空合运算符 let value = optionalValue ?? "default" // 修复方案3:guard语句 guard let value = optionalValue else { return } print(value) // 修复方案4:可选链 let length = optionalString?.count // 返回Int? 8. OOM(Out Of Memory)崩溃 OOM 是 iOS 最难治理的崩溃类型之一:系统因进程占用内存超过限制而将其强杀,不会产生标准崩溃日志,App 也没有机会在崩溃现场同步写下详细堆栈。因此 OOM 治理必须在事故前就把现场数据预埋下来,分为三个时段: ...