iOS:如何检测后台运行的另一ViewController中变量值的变化
嘿,我来帮你梳理下当前的问题和可行的解决方案~
首先得指出你目前代码里的两个核心问题:
- 阻塞主线程:你在FormViewController里用
[NSThread sleepForTimeInterval:1.0]加循环等待的方式,会直接卡死主线程——不仅用户界面完全无响应,甚至可能触发系统的Watchdog机制杀死APP。更糟的是,主线程被阻塞后,ViewController里设置isSyncAutoOnGoing = NO的主线程代码也没法执行,所以你才会一直看到YES的结果。 - 实例变量访问不规范:
isSyncAutoOnGoing是ViewController的私有实例变量,用valueForKey:访问属于非常规操作,不仅不安全,还可能因为KVC的规则导致获取不到正确的值。
下面是几种推荐的正确实现思路,按场景优先级排序:
方法一:NSNotificationCenter(通知中心)——最省心的跨页面通信
这是iOS里最常用的跨组件状态通知方式,适合不需要强耦合的场景。
步骤1:在ViewController里发送完成通知
当下载完成、设置isSyncAutoOnGoing = NO后,发送一个全局通知:
// 在主线程的代码块中,设置完isSyncAutoOnGoing=NO后添加 [[NSNotificationCenter defaultCenter] postNotificationName:@"SyncTaskCompleted" object:nil];
步骤2:在FormViewController里监听通知
在viewDidLoad中注册监听,实现回调方法:
- (void)viewDidLoad { [super viewDidLoad]; // 注册通知监听 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onSyncCompleted:) name:@"SyncTaskCompleted" object:nil]; } // 通知回调 - (void)onSyncCompleted:(NSNotification *)notification { NSLog(@"同步任务已完成,isSyncAutoOnGoing现在是NO"); // 这里写你需要执行的后续逻辑 } // 记得销毁时移除监听,避免野指针 - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; }
方法二:代理模式——适合明确的页面层级关系
如果FormViewController是从ViewController跳转过来的(比如push/present),用代理可以实现更严谨的耦合关系。
步骤1:定义代理协议
在ViewController的头文件里添加协议:
@protocol SyncStatusDelegate <NSObject> - (void)syncTaskDidFinish; @end @interface ViewController : UIViewController @property (nonatomic, weak) id<SyncStatusDelegate> syncDelegate; // 把原来的实例变量改成@property,规范访问 @property (nonatomic, assign) BOOL isSyncAutoOnGoing; @end
步骤2:ViewController里调用代理方法
下载完成时触发代理回调:
// 主线程代码块中,设置完isSyncAutoOnGoing=NO后 if ([self.syncDelegate respondsToSelector:@selector(syncTaskDidFinish)]) { [self.syncDelegate syncTaskDidFinish]; }
步骤3:FormViewController实现代理
// FormViewController.h #import "ViewController.h" @interface FormViewController : UIViewController <SyncStatusDelegate> @end // FormViewController.m - (void)viewDidLoad { [super viewDidLoad]; // 找到对应的ViewController实例,设置代理 NSArray *viewControllers = [[self navigationController] viewControllers]; for (id obj in viewControllers) { if ([obj isKindOfClass:[ViewController class]]) { ((ViewController *)obj).syncDelegate = self; break; } } } - (void)syncTaskDidFinish { NSLog(@"同步完成,isSyncAutoOnGoing变为NO"); // 执行你的后续逻辑 }
方法三:KVO(键值观察)——实时监听属性变化
如果需要实时追踪isSyncAutoOnGoing的每一次变化,KVO是合适的选择。
步骤1:将isSyncAutoOnGoing改为@property
首先在ViewController里把实例变量改成属性:
@interface ViewController () @property (nonatomic, assign) BOOL isSyncAutoOnGoing; @end
步骤2:FormViewController添加观察者
- (void)viewDidLoad { [super viewDidLoad]; NSArray *viewControllers = [[self navigationController] viewControllers]; for (id obj in viewControllers) { if ([obj isKindOfClass:[ViewController class]]) { [obj addObserver:self forKeyPath:@"isSyncAutoOnGoing" options:NSKeyValueObservingOptionNew context:nil]; break; } } } // KVO回调方法 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"isSyncAutoOnGoing"]) { BOOL newValue = [[change objectForKey:NSKeyValueChangeNewKey] boolValue]; if (!newValue) { NSLog(@"isSyncAutoOnGoing已变为NO,同步完成"); // 执行后续逻辑 } } } // 销毁时移除观察者 - (void)dealloc { NSArray *viewControllers = [[self navigationController] viewControllers]; for (id obj in viewControllers) { if ([obj isKindOfClass:[ViewController class]]) { [obj removeObserver:self forKeyPath:@"isSyncAutoOnGoing"]; break; } } }
方法四:单例管理全局状态——适合多页面关注同步状态
如果你的APP里有多个页面需要关注同步任务的状态,可以把状态放到单例类中统一管理。
步骤1:创建SyncManager单例
// SyncManager.h @interface SyncManager : NSObject @property (nonatomic, assign) BOOL isSyncAutoOnGoing; + (instancetype)sharedManager; @end // SyncManager.m @implementation SyncManager + (instancetype)sharedManager { static SyncManager *manager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ manager = [[SyncManager alloc] init]; }); return manager; } @end
步骤2:ViewController中使用单例状态
- (void)sync:(id)sender { [[SyncManager sharedManager] setIsSyncAutoOnGoing:YES]; dispatch_queue_t downloadQueue = dispatch_queue_create("downloader", NULL); dispatch_async(downloadQueue, ^{ // 耗时下载任务 ListOfValueSync *lovSync = [[ListOfValueSync alloc] init]; BOOL ret = [lovSync getAllListOfValueAll]; dispatch_async(dispatch_get_main_queue(), ^{ [spinner stopAnimating]; [spinner removeFromSuperview]; [[SyncManager sharedManager] setIsSyncAutoOnGoing:NO]; // 其他原有代码... }); }); }
步骤3:FormViewController监听单例状态
可以用通知、KVO或者直接读取单例属性,这里以KVO为例:
- (void)viewDidLoad { [super viewDidLoad]; [[SyncManager sharedManager] addObserver:self forKeyPath:@"isSyncAutoOnGoing" options:NSKeyValueObservingOptionNew context:nil]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"isSyncAutoOnGoing"]) { BOOL newValue = [[change objectForKey:NSKeyValueChangeNewKey] boolValue]; if (!newValue) { NSLog(@"全局同步任务已完成"); } } } - (void)dealloc { [[SyncManager sharedManager] removeObserver:self forKeyPath:@"isSyncAutoOnGoing"]; }
总结一下:
- 优先选择通知中心,实现简单且耦合度低;
- 如果页面层级明确,用代理模式更严谨;
- 需要实时追踪属性变化选KVO;
- 多页面关注状态用单例管理。
绝对要避免阻塞主线程的循环等待方式,这是iOS开发的大忌哦~
内容的提问来源于stack exchange,提问作者ΩlostA
相关产品推荐
相关产品推荐

