You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS平台Unity应用后台采集指南针数据的问题咨询

问题描述

我Objective-C和Swift基础薄弱,耗时编写了一款iOS原生插件,用于Unity应用进入后台时通过后台线程采集指南针数据。插件能编译运行,但仅能持续约2秒就被系统终止。我希望实现长时间采集指南针数据,或者至少获取带时间戳的指南针数据,想请教代码存在的问题、可行实现方案及相关参考方向。


相关代码

iOS原生插件代码(MyPlugin.m)

extern "C" {
#import <CoreLocation/CoreLocation.h>
#import "MyPlugin.h"

@implementation MyPlugin

NSThread *_magneticHeadThread;
BOOL _shouldStop;
NSMutableArray *readings;

- (void)startBackgroundThread {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        NSLog(@"Background thread started!");
        readings = [NSMutableArray array];
        _magneticHeadThread = [[NSThread alloc] initWithTarget:self selector:@selector(retrieveMagneticHeadPosition) object:nil];
        [_magneticHeadThread start];
    });
}

- (void)applicationDidEnterBackground:(NSNotification *)notification {
    _shouldStop = NO;
    [self startBackgroundThread];
}

- (void)applicationWillEnterForeground:(NSNotification *)notification {
    _shouldStop = YES;
    [_magneticHeadThread cancel];
    NSLog(@"Magnetic head position: %lu", [readings count]);
}

+ (instancetype)sharedInstance {
    static MyPlugin *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyPlugin alloc] init];
    });
    return sharedInstance;
}

- (void)retrieveMagneticHeadPosition {
    CLLocationManager *locationManager = [[CLLocationManager alloc] init];
    
    locationManager.headingFilter = kCLHeadingFilterNone;
    [locationManager startUpdatingHeading];
    int c = 0;
    while (!_shouldStop) {
        
        NSLog(@"Magnetic head position: %i", c);
        c++;
        [readings addObject:@(locationManager.heading.magneticHeading)];
        [NSThread sleepForTimeInterval:0.1];
    }
    
    [locationManager stopUpdatingHeading];
}

@end


void UnityMyPlugin_StartBackgroundThread() {
    [[NSNotificationCenter defaultCenter] addObserver:[MyPlugin sharedInstance] selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:[MyPlugin sharedInstance] selector:@selector(applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}
}

Unity端调用代码

void Start()
{
    Application.runInBackground = true;
#if UNITY_IPHONE && !UNITY_EDITOR
    UnityMyPlugin_StartBackgroundThread();
#endif
}

代码存在的问题

  1. iOS后台执行限制未处理:iOS应用进入后台后,默认仅保留2-10秒的后台执行窗口,你的代码未申请对应后台权限,系统会直接终止后台线程。
  2. CLLocationManager使用错误:
    • 采用主动轮询locationManager.heading的方式,违背了CLLocationManager通过代理回调获取数据的设计逻辑,无法准确捕获实时更新。
    • locationManager是局部变量,方法执行完毕后可能被提前释放,导致后续无法获取数据。
  3. 线程安全隐患:全局变量_shouldStop未加锁保护,多线程环境下可能出现读写不一致的问题。
  4. 线程嵌套冗余:在全局队列异步后再启动新NSThread,属于不必要的线程嵌套,增加了代码复杂度。

可行实现方案

方案一:申请iOS后台定位权限(实现长时间采集)

iOS允许开启后台定位更新的应用在后台持续获取航向数据,步骤如下:

  1. 在Info.plist中添加权限描述:
    • NSLocationWhenInUseUsageDescription:前台使用定位的说明文本
    • NSLocationAlwaysAndWhenInUseUsageDescription:前后台使用定位的说明文本
  2. 修改插件代码,改用代理回调并配置后台权限:
    @interface MyPlugin () <CLLocationManagerDelegate>
    @property (nonatomic, strong) CLLocationManager *locationManager;
    @property (nonatomic, assign) UIBackgroundTaskIdentifier bgTask;
    @end
    
    @implementation MyPlugin
    // 单例等原有代码保留
    
    - (void)applicationDidEnterBackground:(NSNotification *)notification {
        _shouldStop = NO;
        // 启动后台任务延长执行窗口(配合定位权限)
        self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"CompassBackgroundTask" expirationHandler:^{
            [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
            self.bgTask = UIBackgroundTaskInvalid;
        }];
    
        // 初始化定位管理器并配置后台参数
        if (!self.locationManager) {
            self.locationManager = [[CLLocationManager alloc] init];
            self.locationManager.delegate = self;
            self.locationManager.headingFilter = kCLHeadingFilterNone;
            self.locationManager.allowsBackgroundLocationUpdates = YES;
            self.locationManager.pausesLocationUpdatesAutomatically = NO;
        }
        // 请求始终授权
        [self.locationManager requestAlwaysAuthorization];
        [self.locationManager startUpdatingHeading];
    }
    
    - (void)applicationWillEnterForeground:(NSNotification *)notification {
        _shouldStop = YES;
        [self.locationManager stopUpdatingHeading];
        if (self.bgTask != UIBackgroundTaskInvalid) {
            [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
            self.bgTask = UIBackgroundTaskInvalid;
        }
        NSLog(@"Magnetic head position count: %lu", [readings count]);
    }
    
    // 航向更新代理回调,自动获取带时间戳的数据
    - (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
        if (_shouldStop) return;
        NSDictionary *reading = @{
            @"magneticHeading": @(newHeading.magneticHeading),
            @"timestamp": newHeading.timestamp
        };
        [readings addObject:reading];
    }
    
    @end
    
  3. Unity端代码无需修改,保持原调用逻辑即可。

方案二:利用后台任务延长执行时间(短期采集)

若无需长时间采集,仅需延长后台执行时间(最长约180秒),可使用iOS后台任务API:

  1. 在applicationDidEnterBackground中启动后台任务:
    - (void)applicationDidEnterBackground:(NSNotification *)notification {
        _shouldStop = NO;
        self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"CompassShortTask" expirationHandler:^{
            _shouldStop = YES;
            [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
            self.bgTask = UIBackgroundTaskInvalid;
        }];
        // 启动指南针采集(同样改用代理回调方式)
        [self setupCompassWithDelegate];
    }
    
  2. 替换原轮询逻辑为CLLocationManager代理回调,确保数据准确获取。

相关参考方向

  • 掌握CLLocationManager的官方使用规范,重点理解后台定位配置和代理回调机制
  • 学习iOS后台执行模型,区分后台任务、后台权限(定位/音频等)的适用场景
  • 熟悉Objective-C内存管理规则,避免局部变量提前释放导致的功能异常
  • 了解线程安全的基本处理方式,比如用@synchronized或串行队列保护共享变量读写

内容的提问来源于stack exchange,提问作者Daniele

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 07:14:57