Debug (tryb debugowania) — to konfiguracja kompilacji aplikacji mobilnej, w której kompilator dołącza informację symboliczną, wyłącza optymalizację kodu i podłącza debugger do analizy krok po kroku. Według Android Developers, kompilacja Debug zawiera symbole debugowania, nie kompresuje zasobów i umożliwia podłączenie inspektora baz danych i zapytań sieciowych. Tryb Debug przeciwstawia się kompilacji Release: w Debug programista poświęca wydajność na rzecz przejrzystości wykonania kodu.
Najważniejsze
Debug (debugowanie) — to nie tylko flaga kompilatora, ale cały zestaw ustawień, które czynią aplikację przejrzystą dla programisty. W trybie Debug kompilator dodaje do pliku wykonywalnego tablicę nazw symbolicznych (DWARF), która łączy kod maszynowy z oryginalnymi liniami kodu źródłowego. Bez tej tablicy debugger nie może pokazać, która linia kodu jest aktualnie wykonywana.
Debugger — program, który uruchamia twoją aplikację w kontrolowanym środowisku. Możesz wstrzymać wykonanie na dowolnej linii (breakpoint), wyświetlić wartości wszystkich zmiennych w bieżącym zakresie widoczności, zmienić je w locie i kontynuować wykonanie. Dla platform mobilnych standardowym debuggerem jest LLDB — komponent LLVM, używany zarówno w Xcode, jak i w Android Studio.
Tryb Debug włącza również dodatkowe sprawdzenia, które są wyłączone w Release: asercje (assertions), sprawdzanie granic tablic, detektory wycieków pamięci i rozszerzone logowanie. Te sprawdzenia spowalniają działanie aplikacji, ale wykrywają błędy na wczesnych etapach programowania — zanim kod trafi do użytkownika.
Różnica między kompilacjami Debug i Release jest zasadnicza: to dwa różne zestawy flag kompilatora, konfiguracji podpisu i ustawień pakowania. Zrozumienie tych różnic pomaga uniknąć sytuacji, gdy „w symulatorze działa, a na prawdziwym urządzeniu — nie”.
| Parametr | Debug | Release |
|---|---|---|
| Optymalizacja | Wyłączona (-O0) | Włączona (-Os lub -O2) |
| Symbole | Pełna tabela DWARF | Strip-symbole (usunięte) |
| Podpis | Certyfikat Development | Certyfikat Distribution |
| Profile | Debug provisioning profile | App Store / Ad Hoc profile |
| Logowanie | Pełne (wszystkie poziomy) | Wyłączone lub minimalne |
| Obfuskacja | Wyłączona | Włączona (ProGuard/R8) |
| Rozmiar .apk/.ipa | Większy (symbole + bez kompresji) | Mniejszy (R8 + zasoby) |
Kompilacja Debug jest używana na wszystkich etapach programowania i testowania na lokalnych urządzeniach. Kompilacja Release jest budowana przed wysłaniem do App Store Connect lub Google Play Console. Debugowanie na kompilacji Release jest technicznie możliwe, ale wyjątkowo niewygodne z powodu zmienionych nazw metod (R8) i braku symbolication dla logów awarii.
Jednym z częstych problemów jest kod, który działa w Debug, ale zawodzi w Release. Przyczyną jest UB (undefined behavior) w kodzie, który kompilator różnie obsługuje przy różnych poziomach optymalizacji. Typowy przykład: odczyt niezainicjalizowanej zmiennej lub naruszenie strict aliasing. Do wykrywania takich błędów używaj analizatora statycznego (Clang Static Analyzer, ktlint) przed każdą kompilacją Release.
LLDB — to wysokowydajny debugger oparty na LLVM, obsługujący C, C++, Objective-C, Swift i Kotlin/Native. LLDB udostępnia interfejs REPL, w którym można wykonywać dowolne wyrażenia, zmieniać wartości zmiennych i wywoływać funkcje w kontekście zatrzymanej aplikacji.
Breakpoint — kluczowe narzędzie debuggera. Ustawiasz punkt na linii kodu, a aplikacja zatrzymuje się, gdy wykonanie dotrze do tej linii. LLDB obsługuje kilka typów punktów: warunkowe (uruchamiają się tylko po spełnieniu warunku), symboliczne (na wywołanie funkcji) i jednorazowe (uruchamiają się raz i automatycznie usuwane).
Watchpoint — punkt obserwacji zmiany zmiennej. Podajesz adres pamięci, a debugger wstrzymuje wykonanie przy każdym zapisie na ten adres. To narzędzie jest niezbędne przy poszukiwaniu wyścigów danych i nieprawidłowych mutacji współdzielonych obiektów. Do przeglądania hierarchii UIKit używaj UIView Inspector dostępnego w Xcode.
// Ustawienie warunkowego breakpointa
(lldb) breakpoint set --name "viewDidLoad" --condition "self.isViewLoaded == false"
// Watchpoint na właściwość
(lldb) watchpoint set variable self->_loadingState
// Wykonanie kodu w kontekście zatrzymania
(lldb) expr self.view.backgroundColor = UIColor.redColor
Obydwa IDE udostępniają graficzne inspektory oparte na LLDB. Android Studio zawiera Layout Inspector (podgląd hierarchii View), Network Inspector (śledzenie zapytań HTTP) i Database Inspector (SQLite w czasie rzeczywistym). Xcode udostępnia Debug Memory Graph (analiza wycieków pamięci) i View Debugger (podgląd 3D warstw UIKit).
Począwszy od Androida 11 debugowanie przez Wi-Fi działa bez połączenia USB: wystarczy zeskanować kod QR z Android Studio. iOS obsługuje debugowanie przez Wi-Fi od Xcode 9+ — urządzenie łączy się raz przez USB, po czym sesje debugowania mogą odbywać się przez sieć. Dla serwerów CI debugowanie przez Wi-Fi nie jest odpowiednie z powodu nieprzewidywalnych opóźnień i utraty pakietów, dlatego w zautomatyzowanych pipeline’ach zawsze używa się USB. Niemniej jednak do lokalnego programowania debugowanie przez Wi-Fi jest znacznie wygodniejsze — programista nie jest przywiązany do kabla i może testować aplikację na urządzeniu znajdującym się na drugim końcu pokoju.
Android Debug Bridge (ADB) — uniwersalne narzędzie do interakcji z urządzeniem Android z wiersza poleceń. Przez ADB można zainstalować aplikację, uruchomić debugowanie, skopiować pliki, wykonać polecenia shell i wyświetlić logi. Android Studio używa ADB pod maską do wszystkich operacji debugowania.
Android Studio obsługuje dwa tryby debugowania: Run (normalne uruchomienie) i Debug (uruchomienie z podłączonym debuggerem). W trybie Debug możesz ustawiać breakpointy bezpośrednio w edytorze, przeglądać zmienne w Debug Tool Window i oceniać wyrażenia w Evaluate Expression. Do debugowania procesów tła (Service, BroadcastReceiver) używaj Attach Debugger to Android Process.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Breakpoint tutaj wstrzyma wykonanie
val button = findViewById<Button>(R.id.btn_debug)
button.setOnClickListener {
startDebugProcess()
}
}
private fun startDebugProcess() {
val data = fetchDataFromApi()
Log.d("Debug", "Data loaded: $data")
}
}
Polecenia ADB shell dają dostęp do systemu plików urządzenia bez praw root. Możesz wyświetlić zawartość katalogu databases, skopiować plik .db na komputer i otworzyć go dowolnym klientem SQLite. Android Studio Database Inspector automatyzuje ten proces: widzisz na żywo dane bazy w czasie rzeczywistym i możesz wykonywać zapytania SQL bezpośrednio z IDE.
Xcode udostępnia zintegrowane środowisko debugowania oparte na LLDB. Programista może uruchomić aplikację na symulatorze lub fizycznym urządzeniu, ustawić breakpointy i używać Debug Navigator do kontroli wątków wykonania. W przeciwieństwie do Androida, iOS nie pozwala na uruchomienie dwóch kompilacji Debug jednocześnie na jednym urządzeniu bez specjalnej konfiguracji.
Symulator uruchomia aplikację jako natywny proces macOS, co zapewnia najszybszy cykl debugowania. Na fizycznym urządzeniu debugowanie odbywa się przez USB lub Wi-Fi (od iOS 16), a LLDB komunikuje się z debugserver na urządzeniu. Wydajność debugowania na urządzeniu jest niższa z powodu ograniczonej przepustowości USB 2.0, ale tylko fizyczne urządzenie pozwala testować rzeczywiste scenariusze: powiadomienia push, aparat, czujniki.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
let label = UILabel()
label.text = "Debug Mode"
label.textColor = .systemBlue
view.addSubview(label)
}
}
Xcode Organizer zbiera logi awarii z urządzeń testerów przez Crash Logs. Do symbolication (konwersja adresów na nazwy funkcji) wymagany jest plik .dSYM, który jest generowany przy każdej kompilacji Debug. W kompilacji Release dSYM również jest tworzony, ale logi awarii z App Store trzeba ładować do Organizera ręcznie lub przez serwis bitcode.
Często zadawane pytania
Technicznie tak — przez Ad Hoc z certyfikatem Debug, ale Apple i Google nie zalecają tego robić. Kompilacja Debug zawiera symbole debugowania i obniżoną wydajność, co pogarsza UX i zwiększa rozmiar aplikacji 2–3 razy.
Przyczyna — wyłączona optymalizacja kompilatora (-O0). Kompilator nie wbudowuje funkcji, nie usuwa martwego kodu i zachowuje wszystkie zmienne pośrednie. Dodatkowo Debug włącza sprawdzanie asercji i granic tablic, których nie ma w Release.
W Xcode wybierz Window → Devices and Simulators, zaznacz „Connect via network” dla swojego urządzenia. Urządzenie i Mac muszą być w tej samej sieci Wi-Fi. Po podłączeniu przez USB jeden raz, debugowanie będzie działać przez Wi-Fi przy kolejnych uruchomieniach.
Attach to process umożliwia podłączenie debuggera do już uruchomionego procesu bez ponownego uruchamiania aplikacji. Jest to przydatne do debugowania Service, BroadcastReceiver lub procesów uruchamianych przez zdarzenie systemowe, gdzie standardowy Debug Run nie ma zastosowania.
NSLog i print domyślnie wyświetlają log tylko w konfiguracji Debug. Dla Release używaj os_log z flagą OSLogType.default — zapisuje komunikaty w Unified Logging System i jest dostępny przez Console.app na Macu.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również