Kontroler wątku głównego: wywołanie interfejsu API w wątku w tle: - [UIApplication applicationState]

107

Używam map Google w Xcode 9 beta, iOS 11.

Otrzymuję następujący błąd w dzienniku:

Kontroler głównego wątku: UI API wywołane w wątku w tle: - [UIApplication applicationState] PID: 4442, TID: 837820, nazwa wątku: com.google.Maps.LabelingBehavior, nazwa kolejki: com.apple.root.default-qos.overcommit , QoS: 21

Dlaczego tak się dzieje, ponieważ jestem prawie pewien, że nie zmieniam żadnych elementów interfejsu z głównego wątku w moim kodzie.

 override func viewDidLoad() {

    let locationManager = CLLocationManager()


    locationManager.requestAlwaysAuthorization()


    locationManager.requestWhenInUseAuthorization()

        if CLLocationManager.locationServicesEnabled() {

            locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
            locationManager.startUpdatingLocation()
        }

      viewMap.delegate = self

     let camera = GMSCameraPosition.camera(withLatitude: 53.7931183329367, longitude: -1.53649874031544, zoom: 17.0)


        viewMap.animate(to: camera)


    }

    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        let locValue:CLLocationCoordinate2D = manager.location!.coordinate
        print("locations = \(locValue.latitude) \(locValue.longitude)")
    }

    func mapView(_ mapView: GMSMapView, willMove gesture: Bool) {


    }

    func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {

        if(moving > 1){
            moving = 1
        UIView.animate(withDuration: 0.5, delay: 0, animations: {

            self.topBarConstraint.constant = self.topBarConstraint.constant + (self.topBar.bounds.height / 2)

            self.bottomHalfConstraint.constant = self.bottomHalfConstraint.constant + (self.topBar.bounds.height / 2)

            self.view.layoutIfNeeded()
        }, completion: nil)
    }
         moving = 1
    }


    // Camera change Position this methods will call every time
    func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
        moving = moving + 1
        if(moving == 2){


            UIView.animate(withDuration: 0.5, delay: 0, animations: {


                self.topBarConstraint.constant = self.topBarConstraint.constant - (self.topBar.bounds.height / 2)

                self.bottomHalfConstraint.constant = self.bottomHalfConstraint.constant - (self.topBar.bounds.height / 2)


                self.view.layoutIfNeeded()
            }, completion: nil)
        }
        DispatchQueue.main.async {

            print("Moving: \(moving) Latitude: \(self.viewMap.camera.target.latitude)")
            print("Moving: \(moving)  Longitude: \(self.viewMap.camera.target.longitude)")
        }
    }
Czarny matowy
źródło
1
W mapView(_:didChange)jesteś wysłaniem printoświadczenia do głównej kolejki. Nie jesteś już w głównej kolejce? Jeśli nie, musisz również wysłać animatepołączenie do głównej kolejki. Sugerowałbym wstawienie kilku dispatchPrecondition(condition: .onQueue(.main))przed aktualizacjami interfejsu użytkownika, aby się upewnić.
Rob
Powiedziałeś: „Jestem prawie pewien, że nie zmieniam żadnych elementów interfejsu z głównego wątku w moim kodzie”. Zakładam, że miałeś na myśli „… z dowolnego wątku w tle”.
Rob
2
To nie twój problem. Myślę, że to już koniec. Zatrzymuje się w „com.google.Maps.LabelingBehavior”. Mam ten sam problem.
Tarvo Mäesepp
2
Cześć, tak, problem wydaje się leżeć w Google, mam nadzieję, że wkrótce wydadzą zaktualizowaną wersję
MattBlack
1
@MattBlack Spójrz na tę odpowiedź: stackoverflow.com/a/44392584/5912335
badhanganesh

Odpowiedzi:

54

Po pierwsze, upewnij się, że wywołania map google i zmiany interfejsu użytkownika są wywoływane z głównego wątku.

Możesz włączyć program Thread Sanitizer, aby znaleźć obraźliwe wiersze.

Możesz umieścić obraźliwe wiersze w głównym wątku za pomocą:

DispatchQueue.main.async {
    //Do UI Code here. 
    //Call Google maps methods.
}

Zaktualizuj również swoją aktualną wersję map Google. Mapy Google musiały dokonać kilku aktualizacji sprawdzania wątków.

Na pytanie: „Dlaczego tak się dzieje?” Myślę, że Apple dodał twierdzenie o przypadku krawędzi, dla którego Google musiał zaktualizować swój pod.

ScottyBlades
źródło
1
@thibautnoah, czy mówisz, że ponieważ nie sformułowałem tego jako „to (problem) występuje, ponieważ używasz xcode 9 beta w połączeniu z Google API?” Albo dlatego, że masz podobny błąd, którego nie rozwiązuje moja odpowiedź?
ScottyBlades
7
Xcode 9 zwrócił uwagę na pewne problemy z wątkami, których xcode 8 najwyraźniej nie wykrywa (czy jest to spowodowane ustawieniami xcode, czy też należy to ustalić). Powrót do xcode 8 jest równoznaczny z ignorowaniem problemów z wątkami, one nadal istnieją i nie są rozwiązane, więc nie jest to rozwiązanie, po prostu chowasz głowę w piasek i udajesz, że wszystko jest w porządku. Jeśli problem pochodzi z frameworka, prześlij go, aby można go było naprawić.
thibaut noah
Xcode 9 wykrywa problemy z wątkami, których xcode 8 nie jest odpowiedni do wykrycia, ORAZ Xcode 9 w połączeniu z interfejsem API Google prawdopodobnie powoduje ten błąd. Ten błąd jest zlokalizowany w debugerze w przypadku wielu awarii ORAZ awarie nie występują już po przełączeniu z powrotem do xcode 8.
ScottyBlades
Chodzi o to, że sprawdzanie wątków pokazuje symptom, a nie przyczynę. Mówię, że Xcode 9 beta jest zarówno przyczyną, jak i komunikatorem symptomów. „awarie nie występują już po przełączeniu z powrotem do Xcode 8”. Istnieje różnica między ostrzeżeniami XCode a rzeczywistymi awariami z odczytami debugera. Ten błąd może pojawić się jako ostrzeżenie LUB awaria. Awaria znika całkowicie po przełączeniu z powrotem do xcode 8. Xcode 9 beta nie bez powodu nazywa się beta.
ScottyBlades,
2
Tak ... Myślę, że wielu programistom iOS trudno jest zauważyć, że tylko dlatego, że coś wykazuje symptomy, nie oznacza, że ​​jest to gwarantowane, aby nie być przyczyną również. Myślę, że również trudno jest zaakceptować fakt, że Apple mogło kiedykolwiek popełnić błąd.
ScottyBlades
157

Trudno jest znaleźć kod UI, który czasami nie jest wykonywany w głównym wątku. Możesz użyć poniższej sztuczki, aby go zlokalizować i naprawić.

  1. Wybierz Edit Scheme -> Diagnostics, zaznacz Main Thread Checker.

    Xcode 11.4.1

    Kliknij małą strzałkę obok głównego kontrolera wątków, aby utworzyć punkt przerwania głównego kontrolera wątków. wprowadź opis obrazu tutaj

    Poprzedni Xcode

    Zaznacz opcję Wstrzymaj w przypadku problemów. wprowadź opis obrazu tutaj

  2. Uruchom aplikację na iOS, aby odtworzyć ten problem. (Xcode powinien zatrzymać się przy pierwszym numerze). wprowadź opis obrazu tutaj

  3. Zawiń kod, który modyfikuje interfejs użytkownika w DispatchQueue.main.async {} wprowadź opis obrazu tutaj

UnchartedWorks
źródło
4
Dzięki, zaoszczędź mi może 30 minut.
Pomarańczowy
10
Z jakiegoś powodu, kiedy robię to w Xcode 10.1, otrzymuję tylko stos wywołań, który jest bezradny i nie mogę odnieść się do linii kodu: 2018-12-29 19: 46: 56.500629 + 0100 BedtimePrototype [1553: 834478] [raporty ] Kontroler głównego wątku: UI API wywołane w wątku w tle: - [UIApplication applicationState] PID: 1553, TID: 834478, nazwa wątku: com.apple.CoreMotion.MotionThread, nazwa kolejki: com.apple.root.default-qos. overcommit, QoS: 0
Vilmir
1
Mam też stos rozmów, który wygląda na bezradnego. Po lewej stronie zatrzymał się "com.apple.CoreMotion.MotionThread(23)", potem sprawdzam też wszystkie inne wątki. W Thread 1czymś zwróciło moją uwagę: jest to SVProgressHUD (biblioteka widoku postępu, z której korzystałem). Więc wiem, że to wywołanie SVProgressHUD.show()gdzieś w docelowym kontrolerze widoku. Następnie zawiń każdy wygląd DispatchQueue.main.asynclub po prostu skomentuj, przetestuj ponownie i odkryłem, który z nich jest problematyczny.
John Pang,
2
Usunąłem również ostrzeżenie, komentując SVProgressHUD.show. Zawinięcie tego wywołania w DispatchQueue.main.async nie usunęło ostrzeżenia. Używam kapsuły w wersji 2.2.5 Ten wątek może pomóc lepiej zrozumieć problem: github.com/SVProgressHUD/SVProgressHUD/issues/950
Vilmir
1
W xcode 11.4.1 nie ma pola wyboru „Wstrzymaj w przypadku problemów”. Edycja: znalazłem małą strzałkę obok głównego kontrolera wątków. Kliknięcie dodaje punkt przerwania.
ChrisO
47

Zawiń wiersze kodu, które modyfikują interfejs użytkownika DispatchQueue.main.async {}, aby upewnić się, że są wykonywane w głównym wątku. W przeciwnym razie możesz wywoływać je z wątku w tle, w którym modyfikacje interfejsu użytkownika są niedozwolone. Wszystkie takie wiersze kodu muszą być wykonywane z głównego wątku.

Toma
źródło
4
tak, użyłem już tego, ale ostrzeżenie jest nadal obecne
MattBlack
@Pang, jest używany do drukowania rzeczy, a nie do głównego kodu interfejsu użytkownika.
Toma
4
@MattBlack spróbuj zawinąć wszystkie rzeczy, które modyfikują interfejs użytkownikaDispatchQueue.main.async {}
Toma
@ user6603599-Działa jak urok
Sree
3

Skorzystaj z linku https://developer.apple.com/documentation/code_diagnostics/main_thread_checker

U mnie to zadziałało, gdy zadzwoniłem z bloku.

Krishna Chaitanya Bandaru
źródło
1
Jeśli używasz Swift 4+, to najlepsze rozwiązanie działa idealnie, dziękuję za pomoc w zaoszczędzeniu czasu. Grałem z edytorem schematów, ale przestrzeganie rad Apple jest teraz najlepszą opcją i idziemy naprzód.
AbuTaareq
-34

Wybierz schemat -> Diagnotics, usuń główny kontroler wątków, a ostrzeżenie zniknie. edytor schematów

L.Peng
źródło
18
Myślę, że to nie jest dobry pomysł. Występuje tutaj problem, który wykrył kontroler wątków. Wyłączenie narzędzia do sprawdzania wątków pozwoli na to, aby ten i przyszłe problemy pozostały nieodkryte i spowodował powstanie przyszłego długu technicznego w celu rozwiązania problemów.
ablarg
1
Właściwie chodzi mi o to, że możemy to zrobić, gdy używamy renderowania OpenGL es, ponieważ nie możemy renderować bufora ramki w głównym wątku. dobrze?
L.Peng,
1
Ach, więc jeśli wojna się nie pojawia, to problem nie istnieje?
testy turinga
11
„Dlaczego mój czujnik dymu ciągle się wyłącza?” „Po prostu wyjmij baterię, problem rozwiązany”.
John Montgomery
Jeśli ostrzeżenie się nie pojawia, czy problem nie istnieje?
S. Gissel