设置UICollectionViewCell边框颜色时App触发EXC_BREAKPOINT崩溃
问题
我编写了一段代码,通过定时改变UICollectionViewCell的边框颜色实现闪烁效果。闪烁序列开始时会保存单元格的原始边框颜色,以便序列结束后恢复。但目前代码触发崩溃,报错信息为Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1831e1fd4),且调试现象不符合预期。
注:这段代码此前测试正常,不清楚为何突然崩溃。
相关代码如下:
@interface OrderCollectionViewCell : UICollectionViewCell . . . @property (strong, nonatomic) NSTimer *borderTimer; @property (nonatomic) BOOL blinkStatus; @property (nonatomic) NSInteger blinkTimes; . . . @implementation OrderCollectionViewCell { CGColorRef normalBorder; } -(void)flashBorder { // Save original color for later restore normalBorder = self.bg.layer.borderColor; if(self.borderTimer) { [self.borderTimer invalidate]; } self.borderTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(blink) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:self.borderTimer forMode:NSRunLoopCommonModes]; self.blinkTimes = 0; self.blinkStatus = NO; } -(void)blink { if (self.blinkTimes++ == 3) { // Sequence complete. Restore original color self.bg.layer.borderColor = normalBorder; // <-- This line crashes [self.borderTimer invalidate]; self.borderTimer = nil; return; } if(self.blinkStatus == NO){ self.bg.layer.borderColor = [UIColor whiteColor].CGColor; self.blinkStatus = YES; }else { self.bg.layer.borderColor = [UIColor orangeColor].CGColor; self.blinkStatus = NO; } }
奇怪的是,根据Apple文档,CGColorRef是结构体而非对象,但调试时self.bg.layer.borderColor和normalBorder显示为<CGColor 0x281b6d200>这类类似对象的地址格式。当恢复颜色时,normalBorder仅显示地址,无有效颜色数据。
请问是什么原因导致该崩溃?为何调试中CGColorRef表现得像对象而非结构体?
原因分析与解决方案
崩溃原因
CGColorRef属于Core Foundation(CF)对象,虽然文档将其定义为结构体,但本质是遵循CF引用计数规则的对象。你直接将self.bg.layer.borderColor赋值给normalBorder时,没有增加它的引用计数,后续系统可能会释放这个CGColor,导致normalBorder变成野指针,赋值时触发崩溃。此外,多次调用flashBorder时,旧的normalBorder未被释放,还会引发内存泄漏。
关于CGColorRef调试显示的问题
调试器对CF对象做了特殊处理,会以类似Objective-C对象的格式(<CGColor 地址>)展示,方便开发者查看,但它本质还是依赖引用计数管理的CF对象,并非纯结构体。
修复代码
修改代码,正确管理CGColorRef的引用计数:
@implementation OrderCollectionViewCell { CGColorRef normalBorder; } -(void)flashBorder { // 先释放旧的颜色,避免内存泄漏 if (normalBorder) { CFRelease(normalBorder); normalBorder = NULL; } // 保存原始颜色并增加引用计数 normalBorder = CGColorRetain(self.bg.layer.borderColor); if(self.borderTimer) { [self.borderTimer invalidate]; self.borderTimer = nil; } self.borderTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(blink) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:self.borderTimer forMode:NSRunLoopCommonModes]; self.blinkTimes = 0; self.blinkStatus = NO; } -(void)blink { if (self.blinkTimes++ == 3) { // 恢复原始颜色 self.bg.layer.borderColor = normalBorder; // 释放保存的颜色 CFRelease(normalBorder); normalBorder = NULL; [self.borderTimer invalidate]; self.borderTimer = nil; return; } if(!self.blinkStatus){ self.bg.layer.borderColor = [UIColor whiteColor].CGColor; self.blinkStatus = YES; }else { self.bg.layer.borderColor = [UIColor orangeColor].CGColor; self.blinkStatus = NO; } } // 销毁cell时清理资源 - (void)dealloc { if (normalBorder) { CFRelease(normalBorder); } if (self.borderTimer) { [self.borderTimer invalidate]; } }
额外优化建议
可以改用UIColor保存原始颜色,避开直接操作CGColorRef,更符合Objective-C的内存管理习惯:
// 将成员变量替换为UIColor类型 @property (nonatomic, strong) UIColor *normalBorderColor; -(void)flashBorder { self.normalBorderColor = [UIColor colorWithCGColor:self.bg.layer.borderColor]; // 其余代码不变 } -(void)blink { if (self.blinkTimes++ == 3) { self.bg.layer.borderColor = self.normalBorderColor.CGColor; // 其余代码不变 } // 其余代码不变 }
内容的提问来源于stack exchange,提问作者Agent Friday

