静态UIView添加到父视图后变为nil 调用dismiss无法移除重复新增实例
问题原因及修复方案
核心原因
以下是导致该问题的几种常见场景,按出现概率从高到低排列:
- 静态变量定义位置错误
static关键字的作用是限定变量作用域仅属于当前编译单元,如果你将static UIView *loadingView;定义在Loading.h头文件中,每一个引入了Loading.h的.m文件都会生成独立的loadingView副本。你在Loading.m的show方法中赋值的是当前编译单元的副本,其他操作如果访问到其他副本就会出现值为nil的情况,导致重复创建视图、dismiss无法匹配对应实例。 - MRC编译环境下未手动持有对象
如果项目未开启ARC,static UIView *loadingView默认为assign类型,不会持有对象。你创建的视图实例在自动释放池清空后会被释放,静态变量变为野指针,系统判定为nil,导致后续操作无法匹配已有实例。 - 非主线程执行UI操作
UIKit所有操作必须在主线程执行,如果你在子线程调用show或dismiss方法,会导致赋值、视图添加/移除操作未正确执行,出现变量状态异常。 - 代码语法错误或编译缓存异常
你提供的show方法中创建视图的代码存在语法错误:loadingView = [UIView alloc] init];缺少左括号,正确写法应为loadingView = [[UIView alloc] init];。如果实际代码存在该错误,或Xcode存在旧编译缓存,会导致赋值操作未正确执行。
修复方案
- 将静态变量移至
Loading.m文件顶部,不要放在头文件中:
// Loading.m 顶部定义,不要写入Loading.h static UIView *loadingView; @implementation Loading // 类方法实现写在这里 @end
- 确保UI操作都在主线程执行,优化
show和dismiss方法实现:
+ (void)show { dispatch_async(dispatch_get_main_queue(), ^{ if (loadingView == nil) { UIWindow *containerView = UIApplication.sharedApplication.keyWindow; NSParameterAssert(containerView); // 修正语法错误 loadingView = [[UIView alloc] init]; // 补充必要的布局配置,否则视图默认大小为0 loadingView.frame = containerView.bounds; loadingView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5]; [containerView addSubview:loadingView]; } }); } + (void)dismiss { dispatch_async(dispatch_get_main_queue(), ^{ if (loadingView != nil) { [loadingView removeFromSuperview]; loadingView = nil; } }); }
- 如果项目使用MRC编译,给静态变量赋值时添加
retain操作:
// MRC环境下的赋值写法 loadingView = [[[UIView alloc] init] retain];
- 清理Xcode编译缓存(快捷键
Shift + Cmd + K)后重新编译运行,排除缓存导致的异常。
内容的提问来源于stack exchange,提问作者Chan Tak Zee
相关产品推荐
相关产品推荐

