1

我注意到,每当我重新启动应用程序并查看以下方法时,我发现返回的 beaconRegion 不为零。这如何精确地工作?

beaconRegion = [self.locationManager.monitoredRegions member:beaconRegion]; 
// for more context on how I use this please look at the code below

换句话说,iOS 如何处理CLLocationManager的分配?每次唤醒应用程序时都会反序列化它并以这种方式检索区域信息吗?


这是运行以下代码时 Xcode 调试器控制台的输出:

2015-11-11 09:44:13.718 RegionMonitoringTest[239:15121] AppDelegate: creating new location manager object
2015-11-11 09:44:13.722 RegionMonitoringTest[239:15121] BeaconMonitoring class: in startRangingForBeacons, startupdatinglocation, range for my beacon
2015-11-11 09:44:13.724 RegionMonitoringTest[239:15121] Region already in list
2015-11-11 09:44:13.732 RegionMonitoringTest[239:15121] AppDelegate: Application did became active.
2015-11-11 09:44:13.762 RegionMonitoringTest[239:15121] BeaconMonitoring -> LocationManager: didFailWithError | Error: Error Domain=kCLErrorDomain Code=0 "(null)"
2015-11-11 09:44:13.762 RegionMonitoringTest[239:15121] Requesting to start ranging for beacons again.
2015-11-11 09:44:13.762 RegionMonitoringTest[239:15121] BeaconMonitoring class: in startRangingForBeacons, startupdatinglocation, range for my beacon
2015-11-11 09:44:13.767 RegionMonitoringTest[239:15121] Region already in list

下面我粘贴我用来测试的源代码,它可能会有所帮助(它基于 Apple 提供的AirLocate示例):

#import "AppDelegate.h"
#define BEACON_REGION @"01020102-0102-0102-0102-010201020102"

@interface AppDelegate ()

@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSMutableArray * monitoredRegions;

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.

    if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
        NSLog(@"AppDelegate: being woken up after entering region");
    }
    else{
        NSLog(@"AppDelegate: creating new location manager object");
        self.locationManager = [[CLLocationManager alloc] init];
        self.locationManager.pausesLocationUpdatesAutomatically = false;
        self.locationManager.allowsBackgroundLocationUpdates = true;
        self.locationManager.delegate = self;

        if([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
            [self.locationManager requestAlwaysAuthorization];
        }

        self.monitoredRegions = [[NSMutableArray alloc] initWithCapacity:10];

        [self startRangingForBeacons];
     }

    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    NSLog(@"AppDelegate: will resign active");

}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

    NSLog(@"AppDelegate: did enter background");
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    //[self.bluetoothDataSync stopScanning];

    NSLog(@"AppDelegate: did enter foreground");
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    NSLog(@"AppDelegate: Application did became active.");
}

- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    //[[UIApplication sharedApplication] cancelAllLocalNotifications];
    NSLog(@"AppDelegate: App will terminate");
}

//////////////////////////////////////////////////

- (void) startRangingForBeacons{
    NSLog(@"BeaconMonitoring class: in startRangingForBeacons, startupdatinglocation, range for my beacon");
    [self startMonitoringForRegion:[[NSUUID alloc] initWithUUIDString:BEACON_REGION] :@"my-beaconregion"];
}


- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error {
    NSString * message = [NSString stringWithFormat:@"BeaconMonitoring -> LocationManager: monitoringDidFailForRegion | Error: %@, Region identifier: %@", error, region.identifier];

    NSLog(@"%@", message);
    NSLog(@"Requesting to start ranging for beacons again.");
    [self startRangingForBeacons];
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    NSString * message = [NSString stringWithFormat:@"BeaconMonitoring -> LocationManager: didFailWithError | Error: %@", error];
    NSLog(@"%@", message);
    NSLog(@"Requesting to start ranging for beacons again.");
    [self startRangingForBeacons];
}

- (void) startMonitoringForRegion:(NSUUID*)beaconUUID :(NSString*)regionIdentifier{
    CLBeaconRegion *beaconRegion = nil;

    beaconRegion = [self.locationManager.monitoredRegions member:beaconRegion];

    if(beaconRegion)
    {
        NSLog(@"Region already in list");
    }
    else{
        beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:beaconUUID identifier:regionIdentifier];

        beaconRegion.notifyEntryStateOnDisplay = YES;
        beaconRegion.notifyOnEntry = YES;
        beaconRegion.notifyOnExit  = YES;

        [self.locationManager startMonitoringForRegion:beaconRegion];
    }

    [self.locationManager startRangingBeaconsInRegion:beaconRegion];
    [self.locationManager startUpdatingLocation];
    [self.monitoredRegions addObject:beaconRegion];
}

- (void) stopRangingForbeacons{
    NSLog(@"BeaconMonitoring: stopRangingForbeacons - Stops updating location");
    [self.locationManager stopUpdatingLocation];

    for (int i=0; i < [self.monitoredRegions count]; i++) {
        NSObject * object = [self.monitoredRegions objectAtIndex:i];

        if ([object isKindOfClass:[CLBeaconRegion class]]) {
            CLBeaconRegion * region = (CLBeaconRegion*)object;
            [self.locationManager stopMonitoringForRegion:region];
            [self.locationManager stopRangingBeaconsInRegion:region];
        }
        else{
        NSLog(@"BeaconMonitoring: unrecongized object in beacon region list");
        }
    }
}

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    if (![CLLocationManager locationServicesEnabled]) {
        NSLog(@"Couldn't turn on ranging: Location services are not enabled.");
    }

    if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorized) {
        NSLog(@"Couldn't turn on monitoring: Location services not authorised.");
    }
}


#pragma CLLocationManagerDelegate

-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {

    NSLog(@"BeaconMonitoring: did enter region, will now start ranging beacons in this region");
    [manager startRangingBeaconsInRegion:(CLBeaconRegion*)region];
    [self.locationManager startUpdatingLocation];
}


-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
    NSLog(@"BeaconMonitoring: did exit region, will now: stop ranging beacons in this region; stop updating locations; stop scanning for BLE");

    [manager stopRangingBeaconsInRegion:(CLBeaconRegion*)region];
    [self.locationManager stopUpdatingLocation];

    NSDictionary * notificationData = @{ @"value" : @"exitedRegion"};
    [[NSNotificationCenter defaultCenter] postNotificationName:@"dataUpdate" object:nil userInfo:notificationData];
}

- (void) locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
    NSString * message = @"CLRegionState: ";

    switch (state) {
        case CLRegionStateInside:
            message = @"CLRegionState: state inside";
            break;
        case CLRegionStateOutside:
            message = @"CLRegionState: state outside";
            break;
        case CLRegionStateUnknown:
            message = @"CLRegionState: state unknown";
            break;
        default:
            message = @"CLRegionState: default case";
            break;
    }

    NSLog(@"%@", message);
}

-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {

    bool rangedBeacons = false;

    if ([beacons count]>0) {
        for (int i=0; i<[beacons count]; i++) {
            CLBeacon *beacon = [beacons objectAtIndex:i];
            if((beacon.major.intValue == 4) && (beacon.major.intValue == 8)){
                rangedBeacons = true;
            }
        }
    }

    if (rangedBeacons) {
        NSLog(@"Region identifier: %@", region.identifier);

        NSString * message = [NSString stringWithFormat:@"BeaconMonitoring: ranged a total of %lu, hence request scan start", (unsigned long)[beacons count]];
        NSLog(@"%@", message);
    }
}
4

1 回答 1

1

由于即使应用程序未运行(例如,由于内存压力而终止或在应用程序切换器处被用户杀死),位置管理器的监控仍然有效,因此 iOS 需要保留应用程序监控的区域列表以用于应用程序之外的某个位置,您可以将其monitoredRegions视为该列表的反映,而不是在对象被释放时消失的实例属性。

请注意,例如,如果您实例化多个位置管理器,则它们都共享相同的监视列表——该列表的直接结果是每个应用程序存储在操作系统级别的某个位置。如果您在一个位置管理器中开始/停止监视某个区域,它将影响所有其他区域。

所有这些都适用于CLBeaconRegion,但也适用于“常规”——CLCircularRegion因为它是监控的一个功能,而不是特定于信标的东西。

于 2015-11-11T10:26:02.740 回答