即使使用performSelectorOnMainThread和waitUntilDone,UI也会锁定:NO

时间:2013-06-23 11:48:09

标签: iphone ios objective-c

在我的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 个答案:

答案 0 :(得分:4)

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

看起来它会导致冻结。

尝试将其移出,这样当您想要在UI中显示的数据准备就绪时,您只需调用主线程。

答案 1 :(得分: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
}

答案 2 :(得分: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"]

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

每次都是一个很小的收获,但它们都会增加(运行成本和可读性/维护)。

答案 3 :(得分:2)

当调用计时器回调时,您处于主线程中。然后,当您使用“performSelectorOnMainThread”调用buttonUpdate方法时,您只需在主运行循环中对此消息进行排队并继续执行。然后,当执行buttonUpdate时,由于同步网络调用“dataWithContentsOfURL”在主线程内运行,它会阻止UI。 解决问题的最佳方法是立即调用“buttonUpdate”(不要使用performSelectorOnMainThread,因为您已经在主线程中)并从GCD异步或NSOperationQueue中调用initWithURL。在等待数据更新时,您可以在按钮中显示“更新消息”,一旦异步块或并发NSOperation终止,您最终可以将按钮更新到最终状态(不要忘记更新主线程。)