0

在我的viewController中,我有一个每 20 秒触发一次并调用位置更新的计时器。然后,当位置更新时,我通过performSelectorOnMainThread. 出于某种原因,即使我已经包含waitUntilDone:NO,我的用户界面在执行检查时也会一直锁定。有谁知道我可以如何改进这一点,以使屏幕不会每 20 秒冻结一次?谢谢!

-(void)viewDidLoad {

    NSTimer* myTimer = [NSTimer scheduledTimerWithTimeInterval: 20.0 target: self 
    selector: @selector(callAfterSixtySecond:) userInfo: nil repeats: YES];

    //other code
}

-(void) callAfterSixtySecond:(NSTimer*) t {

    locationManagerProfile.delegate = self;
    locationManagerProfile.desiredAccuracy = kCLLocationAccuracyBest; 
    [locationManagerProfile startUpdatingLocation];  
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation 
*)newLocation fromLocation:(CLLocation *)oldLocation {

    CLLocation *currentLocation = newLocation;

    if (currentLocation != nil) {

        [self performSelectorOnMainThread:@selector(buttonUpdate) withObject:nil 
        waitUntilDone:NO];   
    }
}

最后,我的界面是用这个方法更新的:

-(void) buttonUpdate {

    [locationManagerProfile stopUpdatingLocation];
    NSString *userLatitude =[(PDCAppDelegate *)[UIApplication sharedApplication].delegate 
    getUserLatitude];
    NSString *userLongitude =[(PDCAppDelegate *)[UIApplication sharedApplication].delegate 
    getUserLongitude];

    NSString *placeLatitude = [[NSUserDefaults standardUserDefaults]
    stringForKey:@"savedLatitude"];

    NSString *placeLongitude = [[NSUserDefaults standardUserDefaults]
    stringForKey:@"savedLongitude"];

    NSString *distanceURL = [NSString stringWithFormat:@"http://www.website.com/page.php?
    lat1=%@&lon1=%@&lat2=%@&lon2=%@",userLatitude, userLongitude, placeLatitude, 
    placeLongitude];

    NSData *distanceURLResult = [NSData dataWithContentsOfURL:[NSURL 
    URLWithString:distanceURL]];

    NSString *distanceInFeet = [[NSString alloc] initWithData:distanceURLResult 
    encoding:NSUTF8StringEncoding];

    if ([distanceInFeet isEqualToString:@"1"]) {

        UIBarButtonItem *btnGo = [[UIBarButtonItem alloc] initWithTitle:@"Button A" 
        style:UIBarButtonItemStyleBordered target:self action:@selector(actionA)];
        self.navigationItem.rightBarButtonItem = btnGo;
        [self.navigationItem.rightBarButtonItem setTintColor:[UIColor 
        colorWithRed:44.0/255.0 green:160.0/255.0 blue:65.0/255.0 alpha:1.0]];  
        UIBarButtonItem *btnGoTwo = [[UIBarButtonItem alloc] initWithTitle:@"Button B" 
        style:UIBarButtonItemStyleBordered target:self action:@selector(actionB)];
        self.navigationItem.rightBarButtonItem = btnGoTwo; 
        self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:btnGo, 
        btnGoTwo, nil];
    }
    if ([distanceInFeet isEqualToString:@"0"]) {
        UIBarButtonItem *btnGo = [[UIBarButtonItem alloc] initWithTitle:@"Button C" 
        style:UIBarButtonItemStyleBordered target:self action:@selector(actionC)];
        self.navigationItem.rightBarButtonItem = btnGo;
        [self.navigationItem.rightBarButtonItem setTintColor:[UIColor 
        colorWithRed:44.0/255.0 green:160.0/255.0 blue:65.0/255.0 alpha:1.0]];        
        UIBarButtonItem *btnGoTwo = [[UIBarButtonItem alloc] initWithTitle:@"Button B" 
        style:UIBarButtonItemStyleBordered target:self action:@selector(actionB)];
        self.navigationItem.rightBarButtonItem = btnGoTwo;   
        self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:btnGo, 
        btnGoTwo, nil];
    }       
}
4

4 回答 4

4

您似乎dataWithContentsOfURL:在主线程上使用 while 从 URL 中获取一些数据。

看起来它会为冻结负责。

尝试将其移出,以便仅在要在 UI 中显示的数据准备好时才调用主线程。

于 2013-06-23T12:01:37.627 回答
3

viewDidLoad在主线程上调用,您在此处添加计时器,以便在主线程上调用选择器。你不在后台。有很多方法可以让方法同时执行,例如你可以在其中调用dispatch_async

-(void) callAfterSixtySecond:(NSTimer*) t {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ^{
        locationManagerProfile.delegate = self;
        locationManagerProfile.desiredAccuracy = kCLLocationAccuracyBest; 
        [locationManagerProfile startUpdatingLocation];  
    });
}

或者异步添加定时器:

-(void)viewDidLoad {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ^{
        NSTimer* myTimer = [NSTimer scheduledTimerWithTimeInterval: 20.0 target: self 
        selector: @selector(callAfterSixtySecond:) userInfo: nil repeats: YES];
    });

    //other code
}
于 2013-06-23T11:56:56.103 回答
3

想法CLLocationManager是,当您的应用程序对位置感兴趣时,您创建、配置和启动它一次。然后,根据您设置的配置,无论何时更改位置,它都会给您回电(locationManager:didUpdateToLocation:fromLocation:或者,如果可以的话,更好)。locationManager:didUpdateLocations:反复启动和停止位置管理器效率低下,并且不太可能为您提供准确的位置信息。

一旦你解决了,你可能会没事的。

之后,您需要重新处理线程,因为计时器将在主线程上触发,因此切换回主线程并没有真正改变任何东西(只是增加了一个短暂的延迟)。

另外,不要写那么多代码。特别是如果它一遍又一遍地使用相同的代码。例如,如果您想从用户默认值中获取多个内容,请不要这样做:

[[NSUserDefaults standardUserDefaults] stringForKey:@"savedLatitude"]
[[NSUserDefaults standardUserDefaults] stringForKey:@"savedLongitude"]

更好的做法:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]
[defaults stringForKey:@"savedLatitude"]
[defaults stringForKey:@"savedLongitude"]

(用户默认值只是一个示例,这适用于您无缘无故多次进行的任何方法调用......)

每次都是很小的收获,但所有这些都加起来(运行时成本和可读性/维护)。

于 2013-06-23T12:00:15.243 回答
2

当定时器回调被调用时,你在主线程中。然后,当您使用“performSelectorOnMainThread”调用 buttonUpdate 方法时,您只需将此消息排入主运行循环并继续执行。然后当 buttonUpdate 被执行时,由于同步网络调用“dataWithContentsOfURL”在主线程中运行,它阻塞了 UI。解决问题的最佳方法是立即调用“buttonUpdate”(不要使用 performSelectorOnMainThread,因为您已经在主线程中)并从 GCD 异步或 NSOperationQueue 中调用您的 initWithURL。在等待数据更新时,您可以显示“更新消息”

于 2013-06-23T12:10:22.930 回答