Wydaje się całkiem jasne, że ma to wszystko skonfigurować.
- Kiedy dokładnie działa?
- Dlaczego są dwa nawiasy?
- Jest
__attribute__
funkcją? Makro? Składnia? - Czy to działa w C? C ++?
- Czy funkcja, z którą współpracuje, musi być statyczna?
- Kiedy działa
__attribute__((destructor))
?
__attribute__((constructor))
static void initialize_navigationBarImages() {
navigationBarImages = [[NSMutableDictionary alloc] init];
}
__attribute__((destructor))
static void destroy_navigationBarImages() {
[navigationBarImages release];
}
c++
objective-c
c
gcc
Casebash
źródło
źródło
#define __attribute__(x)
). Jeśli masz wiele atrybutów, np__attribute__((noreturn, weak))
. Trudno byłoby „wyprowadzić makro”, gdyby istniał tylko jeden zestaw nawiasów..init/.fini
. (Możesz poprawnie mieć wiele konstruktorów i destruktorów w jednej jednostce tłumaczącej, nigdy nie wskazywać wielu w jednej bibliotece - jak by to działało?) Zamiast tego, na platformach używających formatu binarnego ELF (Linux itp.), Odniesienia do konstruktorów i destruktorów w sekcjach.ctors
i.dtors
nagłówka. To prawda, że w dawnych czasach funkcje o nazwachinit
ifini
byłyby uruchamiane przy dynamicznym ładowaniu i zwalnianiu biblioteki, gdyby istniały, ale teraz jest to przestarzałe, zastępowane przez ten lepszy mechanizm.__attribute__
jest to, że nie używasz gcc, ponieważ to także jest rozszerzenie gcc..init
/.fini
nie jest przestarzałe. Nadal jest częścią standardu ELF i śmiem twierdzić, że będzie na zawsze. Kod w.init
/.fini
jest uruchamiany przez moduł ładujący / wykonawczy-linker, gdy kod jest ładowany / rozładowywany. Tj. Na każdym ładowaniu ELF (na przykład w bibliotece współdzielonej).init
zostanie uruchomiony. Nadal można użyć tego mechanizmu, aby osiągnąć mniej więcej to samo, co w przypadku__attribute__((constructor))/((destructor))
. Jest oldschoolowy, ale ma pewne zalety..ctors
.dtors
Na przykład mechanizm / wymaga obsługi przez skrypt system-rtl / loader / linker-script. Jest to dalekie od pewności, że będzie dostępne we wszystkich systemach, na przykład głęboko osadzonych systemach, w których kod jest wykonywany na czystym metalu. To__attribute__((constructor))/((destructor))
znaczy, nawet jeśli jest obsługiwany przez GCC, nie jest pewne, czy będzie działać, ponieważ to do konsolidatora należy go zorganizować i do modułu ładującego (lub w niektórych przypadkach kodu rozruchowego), aby go uruchomić. Aby użyć.init
/.fini
zamiast, najprostszym sposobem jest użycie flag linkera: -init & -fini (tj. Z linii poleceń GCC, składnia byłaby-Wl -init my_init -fini my_fini
).W systemie obsługującym obie metody jedną z możliwych korzyści jest to, że kod
.init
jest uruchamiany przed,.ctors
a kod.fini
później.dtors
. Jeśli kolejność jest istotna, to przynajmniej jeden prosty, ale łatwy sposób na rozróżnienie między funkcjami inicjowania / kończenia.Główną wadą jest to, że nie można łatwo mieć więcej niż jednej
_init
i jednej_fini
funkcji na każdy moduł, który można załadować, i prawdopodobnie trzeba by fragmentować kod w więcej.so
niż motywowany. Innym jest fakt, że podczas korzystania z metody linkera opisanej powyżej, zastępuje się oryginalną_fini
funkcję _init i funkcje domyślne (dostarczone przezcrti.o
). W tym miejscu zwykle dochodzi do różnego rodzaju inicjalizacji (w Linuksie inicjowane jest przypisywanie zmiennych globalnych). Sposób na to opisano tutajZauważ w powyższym linku, że kaskadowanie do oryginału
_init()
nie jest potrzebne, ponieważ nadal jest na swoim miejscu. Wcall
zestawie wbudowanym jest jednak mnemonik x86, a wywołanie funkcji z zestawu wyglądałoby zupełnie inaczej dla wielu innych architektur (na przykład ARM). Tj. Kod nie jest przejrzysty..init
Mechanizmy /.fini
i.ctors
/.detors
są podobne, ale niezupełnie. Kod w.init
/.fini
działa „jak jest”. To znaczy , możesz mieć kilka funkcji w.init
/.fini
, ale AFAIK trudno syntaktycznie umieścić je tam w pełni transparentnie w czystym C bez rozbijania kodu w wielu małych.so
plikach..ctors
/.dtors
są inaczej zorganizowane niż.init
/.fini
. Sekcje.ctors
/.dtors
są tylko tabelami ze wskaźnikami do funkcji, a „obiekt wywołujący” to dostarczona przez system pętla, która wywołuje każdą funkcję pośrednio. To znaczy, że osoba wywołująca pętlę może być specyficzna dla architektury, ale ponieważ jest częścią systemu (jeśli w ogóle istnieje, tj.), Nie ma to znaczenia.Poniższy fragment kodu dodaje nowe wskaźniki funkcji do
.ctors
tablicy funkcji, zasadniczo w ten sam sposób, co__attribute__((constructor))
robi (metoda może współistnieć z__attribute__((constructor)))
.Można również dodać wskaźniki funkcji do zupełnie innej wymyślonej przez siebie sekcji. W takim przypadku potrzebny jest zmodyfikowany skrypt linkera i dodatkowa funkcja naśladująca moduł ładujący
.ctors
/.dtors
pętlę. Ale dzięki niemu można uzyskać lepszą kontrolę nad kolejnością wykonywania, dodawać argumenty argumentu i obsługiwać kod powrotu (np. W projekcie C ++ byłoby przydatne, gdyby potrzebował czegoś działającego przed lub po globalnych konstruktorach).Wolę
__attribute__((constructor))/((destructor))
tam, gdzie to możliwe, jest to proste i eleganckie rozwiązanie, nawet jeśli czuję się jak oszukiwanie. W przypadku koderów bez systemu operacyjnego, takich jak ja, nie zawsze jest to opcja.Dobre referencje w książce Łączniki i ładowarki .
źródło
__attribute__((constructor))/((destructor))
destruktor nie działa. Próbowałem kilka rzeczy, takich jak dodanie wpisu .dtor, jak pokazano powyżej. Ale bez powodzenia. Problem można łatwo powielić, uruchamiając kod za pomocą numactl. Załóżmy na przykład, że kod_testowy zawiera destruktor (dodaj printf do funkcji konstruktora i desctructor, aby debugować problem). Potem biegnijLD_PRELOAD=./test_code numactl -N 0 sleep 1
. Zobaczysz, że konstruktor jest wywoływany dwa razy, ale destruktor tylko raz.Ta strona zapewnia doskonałe zrozumienie implementacji
constructor
idestructor
atrybutów oraz sekcji w ELF, które pozwalają im pracować. Po zapoznaniu się z informacjami tutaj, skompilowałem trochę dodatkowych informacji i (pożyczając przykład sekcji od Michaela Ambrusa powyżej) stworzyłem przykład, aby zilustrować pojęcia i pomóc w nauce. Wyniki te podano poniżej wraz z przykładowym źródłem.Jak wyjaśniono w tym wątku, atrybuty
constructor
idestructor
tworzą wpisy w sekcji.ctors
i.dtors
pliku obiektowego. Możesz umieścić odniesienia do funkcji w każdej sekcji na jeden z trzech sposobów. (1) za pomocąsection
atrybutu; (2)constructor
idestructor
atrybuty lub (3) z wywołaniem assemblera (zgodnie z linkiem w odpowiedzi Ambrusa).Zastosowanie
constructor
idestructor
atrybuty pozwalają dodatkowo przypisać priorytet konstruktorowi / destruktorowi, aby kontrolować jego kolejność wykonywania przedmain()
wywołaniem lub po powrocie. Im niższa podana wartość priorytetu, tym wyższy priorytet wykonania (niższe priorytety wykonują przed wyższymi priorytetami przed main () - i kolejne wyższe priorytety po main ()). Podane wartości priorytetów muszą być większe niż100
w przypadku, gdy kompilator rezerwuje wartości priorytetów w zakresie od 0-100 do wdrożenia. Aconstructor
lubdestructor
określone z priorytetem jest wykonywane przed aconstructor
lubdestructor
określone bez priorytetu.Z „sekcja” atrybutu lub z inline-montaż, można również odniesienia funkcyjne miejsce w
.init
i.fini
sekcji kodu ELF, które zostaną wykonane przed każdym konstruktora i po każdym destructor, odpowiednio. Wszelkie funkcje wywoływane przez odwołanie do funkcji umieszczone w.init
sekcji będą wykonywane przed samym odwołaniem do funkcji (jak zwykle).Próbowałem zilustrować każdą z nich w poniższym przykładzie:
wynik:
Przykład pomógł utrwalić zachowanie konstruktora / destruktora, miejmy nadzieję, że przyda się także innym.
źródło
MAX_RESERVED_INIT_PRIORITY
) i że były one takie same jak C ++ (init_priority
) 7.7 C ++ - zmienna specyficzna, funkcja i atrybuty typu . Potem próbowałem go99
:warning: constructor priorities from 0 to 100 are reserved for the implementation [enabled by default] void construct0 () __attribute__ ((constructor (99)));
.Oto „konkretny” (i prawdopodobnie przydatny ) przykład tego, jak, dlaczego i kiedy używać tych poręcznych, ale nieestetycznych konstrukcji ...
Xcode używa „globalnego” „domyślnego użytkownika”, aby zdecydować, która
XCTestObserver
klasa wyrzuca swoje serce do oblężonej konsoli.W tym przykładzie ... kiedy domyślnie ładuję tę bibliotekę psuedo, nazwijmy ją ...
libdemure.a
, poprzez flagę w moim testowym celu á la ..Chcę..
Podczas ładowania (tj. Gdy
XCTest
ładuje mój pakiet testowy), przesłońXCTest
klasę „domyślnego” „obserwatora” ... (za pomocąconstructor
funkcji) PS: O ile wiem ... wszystko, co tu zrobiono, mogłoby zostać wykonane z równoważnym skutkiem wewnątrz mojego+ (void) load { ... }
metoda klasy .uruchom moje testy .... w tym przypadku, przy mniej logicznej gadatliwości w logach (implementacja na żądanie)
Przywróć
XCTestObserver
klasę „globalną” do pierwotnego stanu, aby nie zakłócać innychXCTest
biegów, które nie dotarły do modnego wozu (zwanego też linkiemlibdemure.a
). Wydaje mi się, że historycznie zostało to zrobione wdealloc
... ale nie zamierzam zaczynać z tym starym wiedźmą.Więc...
Bez flagi linkera ... (rój mody policja Cupertino domaga się zemsty , jednak domyślnie Apple dominuje, jak jest pożądane, tutaj )
Z
-ldemure.a
flagą linkera ... (Zrozumiałe wyniki, zadyszka ... "dziękiconstructor
/destructor
" ... Tłum wiwatuje )źródło
Oto kolejny konkretny przykład: biblioteka współdzielona. Główną funkcją biblioteki współdzielonej jest komunikacja z czytnikiem kart inteligentnych. Ale może również otrzymywać „informacje o konfiguracji” w czasie wykonywania przez udp. Udp jest obsługiwany przez wątek, który MUSI zostać uruchomiony w czasie inicjacji.
Biblioteka została napisana w ok.
źródło