6

私は完全に困惑しています、これが状況です:

私のアプリは Core Location フレームワークを使用してユーザーの現在の場所を取得し、TrailBehindでサーバーに ping を送信して近くの興味深い場所を探し、それらをリストとして表示します。問題はありません。

バッテリーを節約するために、サーバーからデータを取得した後、GPS サービスをオフにします。ユーザーがアプリの使用中に移動し、新しいリストが必要な場合、ナビゲーション コントローラーの [更新] をクリックすると、CLLocation サービスが再びアクティブになり、サーバーから新しいデータのバッチが取得され、テーブルが再描画されます。

アプリがサーバーからデータを取得している間、「読み込み中です。お待​​ちください」という地球儀が回転する読み込み画面を読み込み、「戻る」ボタンを押さないようにナビゲーション バーを非表示にします。

そのため、サーバーからの最初のデータ取得は問題なく行われます。

最初に更新を押したときに、すべてのコードが実行されて新しい場所が取得され、サーバーに再度 ping が実行されて新しいデータ リストが取得され、セルが更新されます。ただし、テーブル ビューをロードする代わりに、テーブル ビューのナビゲーション コントローラー バーを復元しますが、メイン ウィンドウにロード ビューを表示します。これはデバイスでのみ当てはまり、シミュレーターではすべてが正常に機能します。

リフレッシュ機能を2回目に押すと、機能が正常に機能します。

上記のように更新を3回目にすると失敗します。

リフレッシュを4回目にすると、正常に動作します。

上記のように更新を5回目にすると失敗します。

などなど、偶数のリフレッシュは成功し、奇数のリフレッシュは失敗します。すべてのコードを 1 行ずつ調べてみたところ、すべてが正常に実行されているようです。私は実際にコア命令をステップオーバーし続け、「ステップオーバー」を大量にクリックした後、CFRunLoopRunSpecificのある時点でテーブルビューが実際に画面に表示されることがわかりましたが、「続行」をクリックするとロードビューが引き継がれましたスクリーン。

私は絶対に困惑しています。助けてください!!あなたの洞察を前もって感謝します。

奇妙な動作のビデオ:

関連コード:

RootViewControllerMethods (これは、この TableView プロジェクトのベース ビューです)

- (void)viewDidLoad {
    //Start the Current Location controller as soon as the program starts.  The Controller calls delegate methods
    //that will update the list and refresh
    [MyCLController sharedInstance].delegate = self;
    [[MyCLController sharedInstance].locationManager startUpdatingLocation];
    lv = [[LoadingViewController alloc] initWithNibName:@"Loading" bundle:nil];
    [self.navigationController pushViewController:lv animated:YES];
    [super viewDidLoad];
}



- (void)updateClicked {
    //When the location is successfully updated the UpdateCells method will stop the CL manager from updating, so when we want to update the location
    //all we have to do is start it up again.  I hope.
    [[MyCLController sharedInstance].locationManager startUpdatingLocation];
    [self.navigationController pushViewController:lv animated:YES];
    //LV is a class object which is of type UIViewController and contains my spinning globe/loading view.
}



-(void)updateCells {
    //When the Core Location controller has updated its location it calls this metod.  The method sends a request for a JSON dictionary
    //to trailbehind and stores the response in the class variable jsonArray.  reloadData is then called which causes the table to
    //re-initialize the table with the new data in jsonArray and display it on the screen.  

    [[MyCLController sharedInstance].locationManager stopUpdatingLocation];

    if(self.navigationController.visibleViewController != self) {
        self.urlString = [NSString stringWithFormat:@"http://www.trailbehind.com/iphone/nodes/%@/%@/2/10",self.lat,self.lon];
        NSURL *jsonURL = [NSURL URLWithString:self.urlString];
        NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL];
        NSLog(@"JsonData = %@ \n", jsonURL);
        self.jsonArray = [jsonData JSONValue];
        [self.tableView reloadData];
        [self.navigationController popToRootViewControllerAnimated:YES];
        [jsonData release];
    }
}

CLController メソッド: 基本的に、すべてのデータを直接 RootViewController に送り返すだけです。

// Called when the location is updated
- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation
{
    NSLog(@"New Location: %@ \n", newLocation);
    NSLog(@"Old Location: %@ \n", oldLocation);
    @synchronized(self) {
        NSNumber *lat = [[[NSNumber alloc] init] autorelease];
        NSNumber *lon = [[[NSNumber alloc] init] autorelease];
        lat = [NSNumber numberWithFloat:newLocation.coordinate.latitude];
        lon = [NSNumber numberWithFloat:newLocation.coordinate.longitude];
        [self.delegate noteLat:lat];
        [self.delegate noteLon:lon];
        [self.delegate noteNewLocation:newLocation];
        [self.delegate updateCells];
    }
}
4

5 に答える 5

1

最初に考えられるのは、読み込み中のビューをプッシュするまで、startUpdatingLocation を CLLocationManager に送信したくないということです。多くの場合、最初の -locationManager:didUpdateToLocation:fromLocation: メッセージは、キャッシュされた GPS データとともに即座に表示されます。これが問題になるのは、すべてのメッセージを処理していて、こちらのサンプル コードに示すように GPS データをフィルター処理していない場合のみです。ただし、これはあなたが説明した状況を引き起こすことはありません.ロード画面が動かなくなる可能性があります.

別のタブに切り替えたときにルートビューコントローラーにポップしようとしていて、呼び出しが正しい場所で行われていないという別の状況で、このような同様の奇妙な動作を経験しました。popToRootViewController が 2 回呼び出されたと思います。私の疑いでは、読み込み中のビューが 2 回プッシュされているか、2 回ポップされている可能性があります。

-viewWillAppear:、-viewDidAppear:、-viewWillDisappear:、および -viewDidDisappear: を LoadingViewController に最小限のログで実装することをお勧めします。

- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"[%@ viewWillAppear:%d]", [self class], animated);
    [super viewWillAppear:animated];
}

- (void)viewDidAppear:(BOOL)animated {
    NSLog(@"[%@ viewDidAppear:%d]", [self class], animated);
    [super viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated {
    NSLog(@"[%@ viewWillDisappear:%d]", [self class], animated);
    [super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated {
    NSLog(@"[%@ viewDidDisappear:%d]", [self class], animated);
    [super viewDidDisappear:animated];
}

次に、デバイスでテストを実行して、それらが常にビュー コントローラーに送信されているかどうか、およびその頻度を確認します。-updateClicked にログを追加して、ダブルタップを明らかにすることができます。

別の考えとして、 @synchronized ブロックは良いアイデアですが、最初のスレッドがブロックを終了するまで、他のスレッドがこれらのステートメントを実行するのを保留するだけです。-stopUpdatingLocation メッセージを @synchronized ブロック内の最初のステートメントに移動することをお勧めします。そうすれば、新しい GPS データを処理することに決めたら、すぐに CLLocationManager に新しいデータの送信を停止するように指示します。

于 2009-05-09T21:59:34.213 に答える
0

アプリケーションをデバッグして、updateCellsを呼び出すときにコントロールがどこに行くかを確認できますか?アプリに明らかに問題はないようです。

LoadingViewControllerクラスにいる間は、メモリの警告がないことを確認してください。メモリ警告があり、RootViewControllerのビューが解放されている場合、RootViewControllerにポップを実行すると、viewDidLoadが再度呼び出されます。

viewDidLoadとupdateCellsにブレークポイントを保持します。他の場所でLoadingViewControllerを呼び出していませんか?

于 2009-05-04T06:27:07.053 に答える
0

元のコードを見ると、このブロックは非常に疑わしいです。

- (void)viewDidLoad {
    ...
    lv = [[LoadingViewController alloc] initWithNibName:@"Loading" bundle:nil];
    [self.navigationController pushViewController:lv animated:YES];
    [super viewDidLoad];
 }

viewDidLoadNIBがロードされるたびに呼び出されます。これは、特にメモリが不足している場合に複数回発生する可能性があります(デバイスでのみ発生するというあなたの意見を踏まえると、このように思われます)。を実装し-didReciveMemoryWarning、superを呼び出した後、少なくともログを印刷して、それが自分に起こっているかどうかを確認することをお勧めします。

上記のコードについて私を悩ませているのは、ほぼ確実にリークしているということです。つまり、走り回るlv回数が増えている可能性があります。LoadingViewControllersあなたはそれがクラス変数だと言います。本当にそれがインスタンス変数だということですか?ivarsは常にアクセサーを使用する必要があります(self.lvまたは[self lv]ではなくlv)。それらに直接割り当てないでください。あなたはほとんどいつもそれを間違ってするでしょう(あなたはおそらくここでドンであるため)。

于 2009-05-24T01:36:49.257 に答える
0

私はまったく同じ問題を探しているときにこれに遭遇したので、あなたはすでにあなたの問題を解決していると確信していますが、誰かがそれに遭遇した場合に備えて私の解決策を投稿すると思いました...

このエラーは、InterfaceBuilderで2つのIBActionを同じUIButtonに割り当てた場合に発生するようです。ビューコントローラーをスタックにプッシュするために使用したボタンは2つのIBActionに割り当てられており、それぞれが異なるコントローラーをnavigationControllerのスタックにプッシュしていたことが判明しました(ただし、そのうちの1つだけが表示されることになります-おそらく最後の呼び出されるもの)。とにかく、一番上のビューの戻るボタンを押しても、実際にはそれが閉じられるわけではなく(または、2番目の見えないコントローラーが閉じられる可能性があります)、戻るには2回押す必要があります。

とにかく、ボタンをチェックして、それらが単一のIBActionにのみ割り当てられていることを確認してください。それは私のためにそれを修正しました。

于 2011-12-06T13:26:16.693 に答える
0

だから、私はこれを機能させることはありませんでした。ナビゲーションコントローラーのデフォルトの戻るボタンでポップを実行できるようにするのではなく、プログラムで popViewController を呼び出すたびにのみ、デバイスでこの動作を観察します。

私の回避策は、カスタムの読み込みビューを作成し、インターネットへのアクセスによる遅延が発生するたびに画面をそのビューに切り替えることでした。私のメソッドは、yes または no のブール変数を取ります。yes は読み込み画面に切り替わり、no は通常のビューに戻ります。コードは次のとおりです。

- (void)switchViewsToLoading:(BOOL)loading {
// Start the Animation Block  
CGContextRef context = UIGraphicsGetCurrentContext();  
[UIView beginAnimations:nil context:context];  
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft forView:self.tableView cache:YES];  
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];  
[UIView setAnimationDuration:.75];  

// Animations  
if(loading) { 
    if (lv == nil) { lv = [[LoadingViewController alloc] initWithNibName:@"Loading" bundle:nil]; }
    [self.view addSubview:lv.view];
    [self.view sendSubviewToBack:self.tableView];
    self.title = @"TrailBehind";
}
else {
    [lv.view removeFromSuperview];

}
// Commit Animation Block  
[UIView commitAnimations];  
//It looks kind of dumb to animate the nav bar buttons, so set those here
if(loading) {
    self.navigationItem.rightBarButtonItem = nil;
    self.navigationItem.leftBarButtonItem = nil;
    self.title = @"TrailBehind";
}
else {
    UIBarButtonItem *feedback = [[UIBarButtonItem alloc] initWithTitle:@"Feedback" style:UIBarButtonItemStylePlain target:self action:@selector(feedbackClicked)];
    self.navigationItem.rightBarButtonItem = feedback;
    UIBarButtonItem *update = [[UIBarButtonItem alloc] initWithTitle:@"Move Me" style:UIBarButtonItemStylePlain target:self action:@selector(updateClicked)];
    self.navigationItem.leftBarButtonItem = update;
    [feedback release];
    [update release];
}

}

于 2009-05-24T00:59:35.230 に答える