質問

Currently my location code is all part of a singleton. I am curious if this is what is causing my problems. I am using the location singleton as part of both geofence regions and iBeacons. I have multiple iBeacons in the location I am testing and it seems that sometimes a enter of a beacon region interrupts the exit of a previous region. I am curious if this is happening because this is a singleton and if I should change this to a standard class that the app delegate initializes to start the monitoring and the app will handle the calls backs for each call back that occurs. I think that since it is a singleton the second call back that occurs is stopping the first call back if it hasn't completed.

You can see from the log below that I exited the basement and it started a timed event to make sure I truly exited the basement (software filter to get rid of the rapid exit/enter that occurs with iBeacons). Then I walked into my living room which fired another region event but we never get the message didExitRegion: Basement logged out.

2014-04-25 15:35:22.757 [2761:707] Just Requested Background Time
2014-04-25 15:35:22.761 [2761:707] Basement
2014-04-25 15:35:22.767 [2761:707] Start Timed Event To See If Its A False Exit
2014-04-25 15:35:22.777 [2761:707] Just Requested Background Time
2014-04-25 15:35:22.778 [2761:707] Basement
2014-04-25 15:35:22.780 [2761:707] Start Timed Event To See If Its A False Exit
2014-04-25 15:35:23.751 [2761:707] Just Requested Background Time
2014-04-25 15:35:23.753 [2761:707] didEnterRegion: Living Room
2014-04-25 15:35:23.772 [2761:707] Just Requested Background Time
2014-04-25 15:35:23.774 [2761:707] didEnterRegion: Living Room
2014-04-25 15:35:24.270 [2761:707] Job ID Value
2014-04-25 15:35:24.272 [2761:707] 3208

New log after changes made to nstimer:

2014-04-25 17:13:06.873 [3243:707] Just Requested Background Time
2014-04-25 17:13:06.875 [3243:707] Kitchen
2014-04-25 17:13:06.877 [3243:707] Start Timed Event To See If Its A False Exit
2014-04-25 17:13:06.884 [3243:707] Just Requested Background Time
2014-04-25 17:13:06.885 [3243:707] Kitchen
2014-04-25 17:13:06.886 [3243:707] Start Timed Event To See If Its A False Exit
2014-04-25 17:13:09.146 [3243:707] didExitRegion Basement
2014-04-25 17:13:09.149 [3243:707] Cancelled Exit Of Region: Basement
2014-04-25 17:13:09.881 [3243:707] Job ID Value
2014-04-25 17:13:09.883 [3243:707] 4040
2014-04-25 17:13:09.884 [3243:707] Connection Successful 
2014-04-25 17:13:10.878 [3243:707] Cancelled Exit Of Region: Kitchen
2014-04-25 17:13:10.889 [3243:707] Cancelled Exit Of Region: Kitchen

Code Updated:

- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
    currentRegion = region;
    GeofenceObject *gObject = [[GeofenceObject alloc] init];
    gObject = [self getObjectForGeofence:region];

    //stop multiple notification deliveries
    if (notificationDelivered && ![gObject getIsBeacon])
    {
        notificationDelivered = NO;
        return;
    }

    BOOL alwaysNotify = [[NSUserDefaults standardUserDefaults] boolForKey:@"alwaysNotifyLocation"];
    if (![self checkWifi] && ![self checkWWLAN] && ![gObject getIsBeacon])
    {
        [self notifyNoNetwork:gObject forState:[NSNumber numberWithInt:1]];
        return;
    }

    if ((alwaysNotify && ![gObject getIsBeacon]))
    {
        [self notifyAlways:gObject forState:[NSNumber numberWithInt:1]];
        return;
    }

    BOOL isInBackground = NO;
    if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)
    {
        isInBackground = YES;
    }

    NSTimer *timer = (NSTimer *)[self.timers objectForKey:[gObject getGeofenceName]];
    if (timer != nil)
    {
        [timer invalidate];
        [self.timers removeObjectForKey:[gObject getGeofenceName]];
    }

    if (isInBackground && [gObject getIsBeacon])
    {
        [self beginBackgroundTask];
        NSLog(@"%@", [@"didEnterRegion: " stringByAppendingString:[gObject getGeofenceName]]);
        [self didEnterRegion:region forObject:gObject];
        return;
    }

    if (isInBackground)
    {
        [self beginBackgroundTask];
        NSLog(@"%@",[@"didEnterRegion " stringByAppendingString:[gObject getGeofenceName]]);
        [self didEnterRegion:region forObject:gObject];
        return;
    }

    if ([gObject getIsBeacon])
    {
        NSLog(@"%@", [@"didEnterRegion: " stringByAppendingString:[gObject getGeofenceName]]);
        [self didEnterRegion:region forObject:gObject];
    }

    NSLog(@"%@",[@"didEnterRegion " stringByAppendingString:[gObject getGeofenceName]]);
    [self didEnterRegion:region forObject:gObject];
}

- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
    currentRegion = region;
    GeofenceObject *gObject = [[GeofenceObject alloc] init];
    gObject = [self getObjectForGeofence:region];

    //stop multiple notification deliveries
    if (notificationDelivered && ![gObject getIsBeacon])
    {
        notificationDelivered = NO;
        return;
    }

    BOOL alwaysNotify = [[NSUserDefaults standardUserDefaults] boolForKey:@"alwaysNotifyLocation"];
    if (![self checkWifi] && ![self checkWWLAN] && ![gObject getIsBeacon])
    {
        [self notifyNoNetwork:gObject forState:[NSNumber numberWithInt:0]];
        return;
    }

    if ((alwaysNotify && ![gObject getIsBeacon]))
    {
        [self notifyAlways:gObject forState:[NSNumber numberWithInt:0]];
        return;
    }

    BOOL isInBackground = NO;
    if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)
    {
        isInBackground = YES;
    }

    if (isInBackground && [gObject getIsBeacon])
    {
        [self beginBackgroundTask];
        [gObject setJustExitedRegion:YES];
        [self replaceObjectWithUpdate:gObject];
        NSDictionary *info = [NSDictionary dictionaryWithObject:region forKey:@"region"];
        NSLog(@"%@", [gObject getGeofenceName]);
        NSLog(@"%@", @"Start Timed Event To See If Its A False Exit");
        [self.timers setObject:[NSTimer scheduledTimerWithTimeInterval:4.0f target:self selector:@selector(checkBackOnExit:) userInfo:info repeats:NO] forKey:[gObject getGeofenceName]];
        return;
    }

    if (isInBackground)
    {
        [self beginBackgroundTask];
        NSLog(@"%@",[@"didExitRegion " stringByAppendingString:[gObject getGeofenceName]]);
        [self didExitRegion:region forObject:gObject];
        return;
    }

    if ([gObject getIsBeacon])
    {
        [gObject setJustExitedRegion:YES];
        [self replaceObjectWithUpdate:gObject];
        NSDictionary *info = [NSDictionary dictionaryWithObject:region forKey:@"region"];
        NSLog(@"%@", [gObject getGeofenceName]);
        NSLog(@"%@", @"Start Timed Event To See If Its A False Exit");
        [self.timers setObject:[NSTimer scheduledTimerWithTimeInterval:4.0f target:self selector:@selector(checkBackOnExit:) userInfo:info repeats:NO] forKey:[gObject getGeofenceName]];
        return;
    }

    NSLog(@"%@",[@"didExitRegion " stringByAppendingString:[gObject getGeofenceName]]);
    [self didExitRegion:region forObject:gObject];
}

- (void)beginBackgroundTask
{
    NSLog(@"%@", @"Just Requested Background Time");
    bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        NSLog(@"%@", @"Just Cancelled Background Time");
        [[UIApplication sharedApplication] endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
}

- (void)checkBackOnExit:(NSTimer *)timer
{
    NSDictionary *info = [timer userInfo];
    CLRegion *region = [info objectForKey:@"region"];
    GeofenceObject *gObject = [[GeofenceObject alloc] init];
    gObject = [self getObjectForGeofence:region];
    if ([gObject getJustExitedRegion])
    {
        [self didExitRegion:region forObject:gObject];
        NSLog(@"%@",[@"didExitRegion " stringByAppendingString:[gObject getGeofenceName]]);
        [gObject setJustExitedRegion:NO];
        [self replaceObjectWithUpdate:gObject];
        [timer invalidate];
        [self.timers removeObjectForKey:[gObject getGeofenceName]];
        info = nil;
    }else
    {
        [timer invalidate];
        [self.timers removeObjectForKey:[gObject getGeofenceName]];
        NSLog(@"%@", [@"Cancelled Exit Of Region: " stringByAppendingString:[gObject getGeofenceName]]);
        info = nil;
        return;
    }
}
役に立ちましたか?

解決

Your problem is this line -

[NSTimer scheduledTimerWithTimeInterval:4.0f target:self selector:@selector(checkBackOnExit:) userInfo:info repeats:NO];

You don't store the new NSTimer that is returned from scheduledTimerWithTimeInterval anywhere so as soon as the method exits it will be deallocated. You need to add the NSTimer to a property. As you can possibly have multiple timers running concurrently I would suggest an NSMutableDictionary.

@property (retain,nonatomic)  NSMutableDictionary *timers;

Initialise this wherever appropriate (such as init)

self.timers=[[NSMutableDictionary alloc]init];

then in didExitRegion:

[self.timers addObject:[NSTimer scheduledTimerWithTimeInterval:4.0f target:self selector:@selector(checkBackOnExit:) userInfo:info repeats:NO] forKey:gObject.geoFenceName];

In didEnterRegion:

NSTimer *timer=(NSTimer *)[self.timers objectForKey:gObject.geoFenceName]
if (timer != nil) {
    [timer invalidate];
    [self.timers removeObjectForKey:gObject.geoFenceName];
}

And also in your checkBackOnExit: method

[timer invalidate];
[self.timers removeObjectForKey:gObject.geoFenceName];
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top