Otrzymano sygnał EXC_BAD_ACCESS

290

Podczas wdrażania aplikacji na urządzeniu program zostanie zamknięty po kilku cyklach z następującym błędem:

Program received signal: "EXC_BAD_ACCESS".

Program działa bez żadnych problemów na symulatorze iPhone'a, będzie także debugował i działał, o ile krok po kroku wykonam instrukcje. Jak tylko pozwolę, by znów zaczął działać, uderzę w EXC_BAD_ACCESSsygnał.

W tym konkretnym przypadku był to błąd w kodzie akcelerometru. Nie wykonałby się w symulatorze, dlatego nie wyrzucił żadnych błędów. Jednak wykona się po wdrożeniu na urządzeniu.

Większość odpowiedzi na to pytanie dotyczy EXC_BAD_ACCESSbłędu ogólnego , więc pozostawię to otwarte jako rozwiązanie problemu przerażającego błędu Bad Access.

EXC_BAD_ACCESSjest zwykle wyrzucany w wyniku nielegalnego dostępu do pamięci. Więcej informacji znajdziesz w odpowiedziach poniżej.

Czy napotkałeś już EXC_BAD_ACCESSwcześniej sygnał i jak sobie z nim poradziłeś?

Héctor Ramos
źródło

Odpowiedzi:

196

Z twojego opisu podejrzewam, że najbardziej prawdopodobne jest, że masz jakiś błąd w zarządzaniu pamięcią. Powiedziałeś, że pracujesz nad rozwojem iPhone'a od kilku tygodni, ale nie wiesz, czy masz ogólne doświadczenie z celem C. Jeśli pochodzisz z innego środowiska, może upłynąć trochę czasu, zanim naprawdę zinternalizujesz reguły zarządzania pamięcią - chyba że zrobisz to z tego powodu.

Pamiętaj, że wszystko, co otrzymujesz z funkcji alokacji (zwykle metoda alokacji statycznej, ale jest kilka innych) lub metoda kopiowania, również jesteś właścicielem pamięci i musisz ją zwolnić po zakończeniu.

Ale jeśli odzyskasz coś z prawie wszystkiego, w tym metod fabrycznych (np. [NSString stringWithFormat]), Otrzymasz referencję do automatycznego wydawania, co oznacza, że ​​może zostać wydana w przyszłości w innym kodzie - więc bardzo ważne jest, jeśli potrzebujesz aby utrzymać go poza bezpośrednią funkcją, którą zachowujesz. Jeśli tego nie zrobisz, pamięć może pozostać przydzielona podczas korzystania z niej lub zostać zwolniona, ale przypadkowo nadal ważna, podczas testowania emulatora, ale jest bardziej prawdopodobne, że zostanie zwolniona i pojawi się jako zły błąd dostępu podczas działania na urządzeniu.

Najlepszym sposobem na wyśledzenie tych rzeczy i dobrym pomysłem (nawet jeśli nie ma widocznych problemów) jest uruchomienie aplikacji w narzędziu Instruments, szczególnie z opcją Wycieki.

philsquared
źródło
2
Miałem trochę kodu próbkowania akcelerometru, który nie był istotny dla mojej aplikacji, co po usunięciu wyeliminowało błąd złego dostępu. Co ma sens, biorąc pod uwagę, że symulator nie ma akcelerometru. Dziwne wydaje mi się, że ten kod istniał nietknięty przez tydzień przed spowodowaniem tego błędu ...
Héctor Ramos
Jestem nowy w Objective-C, więc większość moich problemów powinna wynikać z zarządzania pamięcią. Po kilku latach w C ++ korzystam głównie z Javy przez ostatnie trzy lub cztery lata, więc zardzewiałem na zarządzaniu pamięcią. Dzięki za odpowiedź!
Héctor Ramos
4
Nie ma problemu - cieszę się, że to naprawiłeś. Zarządzanie pamięcią nie jest trudne do opanowania - musisz tylko nauczyć się zasad i nabrać dobrych nawyków.
philsquared
Wydaje mi się, że miałem problem z tym, że jestem zbyt agresywny w uwalnianiu napisów (i takich), które tworzę. Nadal nie jestem w 100% pewien, co i kiedy powinno się wydać, ale odpowiedź Phila z pewnością pomogła.
pluckyglen
Jeśli postępowałem zgodnie z tym, cmculloh, tak, to było właściwe. Nie jesteś właścicielem zwrotu obiektu z objectAtIndex.
philsquared
101

Główną przyczyną EXC_BAD_ACCESS jest próba dostępu do uwolnionych obiektów.

Aby dowiedzieć się, jak rozwiązać ten problem, przeczytaj ten dokument: DebuggingAutoReleasePool

Nawet jeśli nie uważasz, że „wypuszczasz automatycznie zwalniane obiekty”, dotyczy to ciebie.

Ta metoda działa wyjątkowo dobrze. Używam go cały czas z wielkim sukcesem !!

Podsumowując, wyjaśnia to, jak używać klasy debugowania NSZombie Cocoa i narzędzia wiersza polecenia „malloc_history”, aby znaleźć dokładnie to, co zostało zwolnione w twoim kodzie.

Dygresja:

Uruchamianie instrumentów i sprawdzanie wycieków nie rozwiąże problemu EXC_BAD_ACCESS. Jestem pewien, że wycieki pamięci nie mają nic wspólnego z EXC_BAD_ACCESS. Definicja wycieku jest obiektem, do którego nie masz już dostępu i dlatego nie możesz go nazwać.

AKTUALIZACJA: Teraz używam instrumentów do debugowania wycieków. W Xcode 4.2 wybierz Produkt-> Profil, a po uruchomieniu Instruments wybierz „Zombie”.

Bentford
źródło
18
Ta informacja jest bardzo ważna. Wycieki nie mogą powodować EXC_BAD_ACCESS (mają inne problemy). Napisałem to, aby wyjaśnić nieporozumienia dotyczące EXC_BAD_ACCESS loufranco.com/blog/files/Understanding-EXC_BAD_ACCESS.html
Lou Franco
4
To narzędzie zombie w Xcode jest genialne! Sprawca znalazłem w 3 minuty, a nie godziny.
Aram Kocharyan
1
powyższy link w odpowiedzi nie jest dostępny. Pokazuje błąd 404 nie znaleziony.
Tejas
12

Sygnał EXC_BAD_ACCESS jest wynikiem przekazania nieprawidłowego wskaźnika do wywołania systemowego. Dostałem je dzisiaj już wcześniej z programem testowym na OS X - przekazałem niezainicjowaną zmienną do pthread_join(), co było spowodowane wcześniejszą literówką.

Nie znam rozwoju iPhone'a, ale powinieneś dokładnie sprawdzić wszystkie wskaźniki bufora przekazywane do wywołań systemowych. Całkowicie podnieś poziom ostrzegawczy kompilatora (w gcc użyj opcji -Walli -Wextra). Włącz jak najwięcej diagnostyki na symulatorze / debuggerze.

Adam Rosenfield
źródło
8

Z mojego doświadczenia wynika, że ​​jest to na ogół spowodowane nielegalnym dostępem do pamięci. Sprawdź wszystkie wskaźniki, zwłaszcza wskaźniki obiektów, aby upewnić się, że zostały zainicjowane. Upewnij się, że plik MainWindow.xib, jeśli go używasz, jest poprawnie skonfigurowany i zawiera wszystkie niezbędne połączenia.

Jeśli żadne z tych sprawdzeń na papierze niczego nie wykryje, a nie stanie się to podczas wykonywania pojedynczych kroków, spróbuj zlokalizować błąd za pomocą instrukcji NSLog (): posyp je kodem, przesuwając je, dopóki nie wyodrębnisz linii, która powoduje błąd. Następnie ustaw punkt przerwania w tej linii i uruchom program. Kiedy osiągniesz punkt przerwania, sprawdź wszystkie zmienne i zawarte w nich obiekty, aby sprawdzić, czy coś nie wygląda tak, jak się spodziewasz. Zwłaszcza zwracam uwagę na zmienne, których klasy obiektów jest coś, czego się nie spodziewałeś. Jeśli zmienna ma zawierać UIWindow, ale zamiast tego zawiera NSNotification, ten sam podstawowy kod błędu może objawiać się w inny sposób, gdy debugger nie działa.

Brent Royal-Gordon
źródło
7

Właśnie spędziłem kilka godzin na śledzeniu EXC_BAD_ACCESS i odkryłem, że NSZombies i inne środowiska env nie wydają mi się nic powiedzieć.

Dla mnie była to głupia instrukcja NSLog ze specyfikatorami formatu, ale nie przekazano żadnych argumentów.

NSLog(@"Some silly log message %@-%@");

Naprawione przez

NSLog(@"Some silly log message %@-%@", someObj1, someObj2);
Scott Heaberlin
źródło
6

Filmy WWDC z 2010 r. Są dostępne dla wszystkich uczestników programu dla programistów Apple. Jest świetny film: „Sesja 311 - Zaawansowana analiza pamięci z instrumentami”, który pokazuje kilka przykładów używania zombie w instrumentach i debugowania innych problemów z pamięcią.

Aby uzyskać link do strony logowania, kliknij TUTAJ .

Jonasz
źródło
6

Nie jest to kompletna odpowiedź, ale jedna konkretna sytuacja, w której ją otrzymałem, dotyczy próby uzyskania dostępu do obiektu, który „zmarł”, ponieważ próbowałem użyć funkcji automatycznego uwalniania:

netObjectDefinedInMyHeader = [[[MyNetObject alloc] init] autorelease];

Tak więc na przykład przekazałem to jako obiekt do „powiadomienia” (zarejestrowałem go jako słuchacza, obserwatora, dowolny idiom, który ci się podoba), ale już umarł po wysłaniu powiadomienia i otrzymam EXC_BAD_ACCESS. Zmiana [[MyNetObject alloc] init]i późniejsze zwolnienie w razie potrzeby rozwiązało błąd.

Innym powodem może być na przykład przekazanie obiektu i próba jego zapisania:

myObjectDefinedInHeader = aParameterObjectPassedIn;

Później podczas próby uzyskania dostępu do myObjectDefinedInHeader możesz mieć problemy. Za pomocą:

myObjectDefinedInHeader = [aParameterObjectPassedIn retain];

może być tym, czego potrzebujesz. Oczywiście to tylko kilka przykładów tego, na co wpadłem i są też inne powody, ale mogą one okazać się nieuchwytne, więc o nich wspomnę. Powodzenia!

Obrabować
źródło
5

Aby dodać inną sytuację, w której może się to zdarzyć:

Miałem kod:

NSMutableString *string;
[string   appendWithFormat:@"foo"];

Oczywiście zapomniałem przydzielić pamięć dla ciągu:

NSMutableString *string = [[NSMutableString alloc] init];
[string   appendWithFormat:@"foo"];

naprawia problem.

gnuchu
źródło
Nie powoduje to dla mnie żadnego błędu, ponieważ łańcuch jest inicjowany na zero, a użycie wzorca obiektu zerowego wywołanie metody na zero nie robi nic.
Liron Yahdav
5

Inną metodą wychwytywania wyjątków EXC_BAD_ACCESS przed ich wystąpieniem jest analizator statyczny w XCode 4+.

Uruchom analizator statyczny za pomocą opcji Produkt> Analizuj (shift + cmd + B). Kliknięcie dowolnego komunikatu wygenerowanego przez analizator spowoduje nałożenie na źródło diagramu pokazującego sekwencję zatrzymań / zwolnień obiektu naruszającego prawo.

wprowadź opis zdjęcia tutaj

ericsoco
źródło
5

Uważam, że warto ustawić punkt przerwania na objc_exception_throw. W ten sposób debugger powinien się zepsuć, gdy pojawi się EXC_BAD_ACCESS.

Instrukcje można znaleźć tutaj Techniki debugowania

lyonanderson
źródło
4

Zastosuj prostą zasadę „jeśli nie przydzieliłeś go lub nie zatrzymałeś, nie zwalniaj”.

Punkt gamma
źródło
1
Rozwiń tę zasadę przez „... skopiuj ...” i wszystko powinno być w porządku.
Do
4

Jak debugować EXC_BAD_ACCESS

Sprawdź powyższy link i postępuj zgodnie z instrukcją .... Kilka szybkich instrukcji dotyczących używania NSZombies

Uruchom aplikację i po jej awarii (powinien wyświetlać „Przerwany” zamiast „EXC_BAD_ACCESS” ... sprawdź konsolę (Uruchom> konsolę) ... powinien pojawić się komunikat informujący, do którego obiektu próbuje uzyskać dostęp.

-Ben

Ben Call
źródło
3

Debugowałem i refaktoryzowałem kod, aby rozwiązać ten błąd przez ostatnie cztery godziny. Post powyżej pokazał mi problem:

Właściwość przed: startPoint = [[przydział DataPoint] init]; startPoint = [DataPointList objectAtIndex: 0];
. . . x = startPoint.x - 10; // EXC_BAD_ACCESS

Właściwość po: startPoint = [[przydział DataPoint] init]; startPoint = [[DataPointList objectAtIndex: 0] zachowaj];

Do widzenia EXC_BAD_ACCESS


źródło
Popełniłem podobny błąd. Zapomniałem zatrzymać instancję, która spowodowała awarię, i spędziłem godziny, żeby ją rozgryźć. Twoje doświadczenie pozwoliło mi poznać moje. Dzięki!
David.Chu.ca
3

Mam nadzieję, że po zakończeniu zwalniasz „sznurek”!


źródło
3

Zapomniałem zwrócić siebie w metodzie init ...;)

denbec
źródło
Powinno to być ostrzeżenie / błąd czasu kompilacji, a nie EXC_BAD_ACCESS podczas działania.
stigi
3

To jest doskonały wątek. Oto moje doświadczenie: pomyliłem się ze słowem kluczowym zachowaj / przypisaj w deklaracji właściwości. Powiedziałem:

@property (nonatomic, assign) IBOutlet UISegmentedControl *choicesControl;
@property (nonatomic, assign) IBOutlet UISwitch *africaSwitch;
@property (nonatomic, assign) IBOutlet UISwitch *asiaSwitch;

gdzie powinienem był powiedzieć

@property (nonatomic, retain) IBOutlet UISegmentedControl *choicesControl;
@property (nonatomic, retain) IBOutlet UISwitch *africaSwitch;
@property (nonatomic, retain) IBOutlet UISwitch *asiaSwitch;
fool4jesus
źródło
1
Dziwne, dlaczego miałbyś chcieć zatrzymać IBOutlet? Zarządzanie nimi odbywa się automatycznie.
jv42
3

EXC_BAD_ACCESS napotkałem na iPhonie tylko podczas próby wykonania metody C zawierającej dużą tablicę. Symulator był w stanie zapewnić mi wystarczającą ilość pamięci, aby uruchomić kod, ale nie urządzenie (tablica miała milion znaków, więc była odrobinę za duża!).

EXC_BAD_ACCESS wystąpił tuż po punkcie wejścia metody i przez pewien czas byłem zdezorientowany, ponieważ nie było go w pobliżu deklaracji tablicy.

Być może ktoś inny mógłby skorzystać z moich kilku godzin ciągnięcia włosów.

mblackwell8
źródło
3

Zapomniałem wyjąć z nieprzydzielonego wskaźnika dealloc. Otrzymywałem exc_bad_access na moim rootView UINavigationController, ale tylko czasami. Zakładałem, że problem tkwi w rootView, ponieważ ulegał awarii w połowie jego viewDidAppear {}. Okazało się, że stało się to dopiero po tym, jak opublikowałem widok złym wydaniem dealloc {} i to było to!

„EXC_BAD_ACCESS” [Przełączanie na proces 330] Brak dostępnej pamięci do zaprogramowania: niebezpieczne połączenie z centrum handlowym

Myślałem, że to problem, w którym próbowałem przydzielić ... a nie tam, gdzie próbowałem zwolnić non-przydział, D'oh!

Josh
źródło
3

Jak radzę sobie z EXC_BAD_ACCESS

Czasami czuję, że kiedy zostanie zgłoszony błąd EXC_BAD_ACCESS, xcode wyświetli błąd w klasie main.m, nie podając żadnych dodatkowych informacji o tym, gdzie dochodzi do awarii (czasami).

W tych czasach możemy ustawić wyjątkowy punkt przerwania w Xcode, aby po wychwyceniu wyjątku został umieszczony punkt przerwania i bezpośrednio poinformować użytkownika o awarii w tej linii

wprowadź opis zdjęcia tutaj

iPrabu
źródło
2

Wywołania NSAssert () w celu sprawdzenia parametrów metody są bardzo przydatne do śledzenia i unikania przekazywania zer.

Ryan Townshend
źródło
2

Właśnie miałem ten problem. Dla mnie powodem było usunięcie obiektu zarządzanego przez CoreData i próba odczytania go później z innego miejsca.

konryd
źródło
2

Debugowałem i refaktoryzowałem kod, aby rozwiązać ten błąd przez ostatnie cztery godziny. Post powyżej pokazał mi problem:

Nieruchomość przed:

startPoint = [[DataPoint alloc] init] ;
startPoint= [DataPointList objectAtIndex: 0];
x = startPoint.x - 10; // EXC_BAD_ACCESS

Nieruchomość po:

startPoint = [[DataPoint alloc] init] ;
startPoint = [[DataPointList objectAtIndex: 0] retain];

Do widzenia EXC_BAD_ACCESS

Dziękuję bardzo za odpowiedź. Walczę z tym problemem przez cały dzień. Jesteś niesamowity!


źródło
4
Czy nie zastępujesz od razu programu StartPoint? Nie sądzę, że w ogóle potrzebujesz tej pierwszej linii.
toxaq 24.09.10
2
Nie ma absolutnie żadnej potrzeby przydzielania i inicjowania zmiennej, jeśli natychmiast zamierzasz ją zastąpić inną. Po prostu wyciekasz z obiektu w pierwszym zadaniu.
dreamlax
2

Tylko dodać

Lynda.com ma fantastyczne DVD o nazwie

iPhone SDK Essential Training

a rozdział 6, lekcja 3 dotyczy EXEC_BAD_ACCESS i pracy z zombie.

Wspaniale było mi zrozumieć, nie tylko kod błędu, ale jak mogę użyć Zombie, aby uzyskać więcej informacji na temat uwolnionego obiektu.

balexandre
źródło
2

Aby sprawdzić, jaki może być błąd

Użyj NSZombieEnabled.

Aby aktywować funkcję NSZombieEnabled w aplikacji:

Wybierz Projekt> Edytuj aktywny plik wykonywalny, aby otworzyć okno informacji o pliku wykonywalnym. Kliknij Argumenty. Kliknij przycisk dodaj (+) w sekcji „Zmienne do ustawienia w środowisku”. Wpisz NSZombieEnabled w kolumnie Nazwa i YES w kolumnie Wartość. Upewnij się, że zaznaczono pole wyboru dla pozycji NSZombieEnabled.

Znalazłem tę odpowiedź na iPhoneSDK

zambono
źródło
2

Zdaję sobie sprawę, że o to pytano jakiś czas temu, ale po przeczytaniu tego wątku znalazłem rozwiązanie dla XCode 4.2: Produkt -> Edytuj schemat -> Karta diagnostyczna -> Włącz obiekty zombie

Pomógł mi znaleźć wiadomość wysyłaną do zwolnionego obiektu.

Sean Aitken
źródło
2

Jeśli masz nieskończoną rekurencję, myślę, że możesz również mieć ten błąd. To był przypadek dla mnie.

coolcool1994
źródło
1

Jeszcze inna możliwość: przy użyciu bloków w kolejkach może się łatwo zdarzyć, że spróbujesz uzyskać dostęp do obiektu w innej kolejce, który został już w tej chwili zwolniony. Zazwyczaj, gdy próbujesz wysłać coś do GUI. Jeśli punkt przerwania wyjątku jest ustawiony w dziwnym miejscu, może to być przyczyna.

mózg
źródło
1

Mam to, ponieważ nie używałem [self performSegueWithIdentifier:sender:]i -(void) prepareForSegue:(UIstoryboardSegue *)dobrze

DHShah01
źródło
1

Nie zapominaj o @symbolu podczas tworzenia ciągów, traktując to, C-stringsco NSStringsspowoduje EXC_BAD_ACCESS.

Użyj tego:

@"Some String"

Zamiast tego:

"Some String"

PS - zwykle przy zapełnianiu zawartości arraydużą ilością rekordów.

Artur
źródło