Tampaknya cukup jelas bahwa itu seharusnya mengatur segalanya.
- Kapan tepatnya itu berjalan?
- Mengapa ada dua tanda kurung?
- Apakah
__attribute__
suatu fungsi? Makro? Sintaksis? - Apakah ini berfungsi di C? C ++?
- Apakah fungsinya berfungsi dengan harus statis?
- Kapan
__attribute__((destructor))
lari?
__attribute__((constructor))
static void initialize_navigationBarImages() {
navigationBarImages = [[NSMutableDictionary alloc] init];
}
__attribute__((destructor))
static void destroy_navigationBarImages() {
[navigationBarImages release];
}
c++
objective-c
c
gcc
Casebash
sumber
sumber
#define __attribute__(x)
). Jika Anda memiliki beberapa atribut, misalnya,__attribute__((noreturn, weak))
akan sulit untuk "makro keluar" jika hanya ada satu set tanda kurung..init/.fini
. (Anda dapat secara sah memiliki banyak konstruktor dan destruktor dalam satu unit terjemahan, tidak pernah kelipatan dalam satu perpustakaan - bagaimana cara kerjanya?) Sebaliknya, pada platform menggunakan format biner ELF (Linux, dll.), Konstruktor dan destruktor direferensikan di bagian.ctors
dan.dtors
header. Benar, di masa lalu, fungsi yang dinamaiinit
danfini
akan dijalankan pada pustaka dinamis dan bongkar jika ada, tapi itu sudah usang sekarang, digantikan oleh mekanisme yang lebih baik ini.__attribute__
adalah jika Anda tidak menggunakan gcc, karena itu juga, adalah ekstensi gcc..init
Saya.fini
tidak ditinggalkan. Itu masih bagian dari standar ELF dan saya berani mengatakan itu akan selamanya. Kode di.init
/.fini
dijalankan oleh loader / runtime-linker ketika kode dimuat / dibongkar. Yakni pada setiap beban ELF (misalnya pustaka bersama) di.init
akan dijalankan. Masih mungkin untuk menggunakan mekanisme itu untuk mencapai hal yang sama dengan__attribute__((constructor))/((destructor))
. Ini sekolah tua tetapi memiliki beberapa manfaat..ctors
/.dtors
mekanisme misalnya memerlukan dukungan oleh skrip sistem-rtl / loader / linker. Ini jauh dari pasti untuk tersedia di semua sistem, misalnya sistem tertanam di mana kode dieksekusi pada bare metal. Yaitu bahkan jika__attribute__((constructor))/((destructor))
didukung oleh GCC, tidak pasti itu akan berjalan karena terserah linker untuk mengaturnya dan ke loader (atau dalam beberapa kasus, kode boot) untuk menjalankannya. Untuk menggunakan.init
/.fini
sebagai gantinya, cara termudah adalah dengan menggunakan tanda tautan: -init & -fini (yaitu dari baris perintah GCC, sintaksnya adalah-Wl -init my_init -fini my_fini
).Pada sistem yang mendukung kedua metode, salah satu manfaat yang mungkin adalah bahwa kode masuk
.init
dijalankan sebelum.ctors
dan kode masuk.fini
setelahnya.dtors
. Jika pesanan relevan, itu setidaknya satu cara kasar tetapi mudah untuk membedakan antara fungsi init / exit.Kelemahan utama adalah bahwa Anda tidak dapat dengan mudah memiliki lebih dari satu
_init
dan satu_fini
fungsi per setiap modul yang dapat dimuat dan mungkin harus memecah kode menjadi lebih.so
dari termotivasi. Lain adalah bahwa ketika menggunakan metode linker yang dijelaskan di atas, satu menggantikan fungsi _init asli dan_fini
default (disediakan olehcrti.o
). Di sinilah segala macam inisialisasi biasanya terjadi (di Linux ini adalah di mana tugas variabel global diinisialisasi). Cara yang dijelaskan di siniPerhatikan di tautan di atas bahwa cascading ke aslinya
_init()
tidak diperlukan karena masih ada. Namuncall
dalam perakitan inline adalah x86-mnemonic dan memanggil fungsi dari perakitan akan terlihat sangat berbeda untuk banyak arsitektur lainnya (seperti ARM misalnya). Yaitu kode tidak transparan..init
/.fini
dan.ctors
/.detors
mekanisme serupa, tetapi tidak cukup. Kode dalam.init
/.fini
berjalan "apa adanya". Yaitu Anda dapat memiliki beberapa fungsi di.init
/.fini
, tetapi secara sintaksis AFAIK sulit untuk menempatkannya di sana sepenuhnya secara transparan dalam C murni tanpa memecah kode dalam banyak.so
file kecil ..ctors
Saya.dtors
terorganisir secara berbeda dari.init
/.fini
..ctors
/.dtors
Bagian keduanya hanya tabel dengan pointer ke fungsi, dan "pemanggil" adalah loop yang disediakan sistem yang memanggil setiap fungsi secara tidak langsung. Yaitu loop-caller bisa arsitektur spesifik, tetapi karena itu bagian dari sistem (jika ada sama sekali yaitu) tidak masalah.Cuplikan berikut menambahkan pointer fungsi baru ke
.ctors
array fungsi, terutama dengan cara yang sama__attribute__((constructor))
(metode dapat hidup berdampingan dengan__attribute__((constructor)))
.Satu juga dapat menambahkan fungsi pointer ke bagian yang diciptakan sendiri sepenuhnya berbeda. Skrip linker yang dimodifikasi dan fungsi tambahan yang meniru loader
.ctors
/.dtors
loop diperlukan dalam kasus tersebut. Tetapi dengan itu orang dapat mencapai kontrol yang lebih baik atas urutan eksekusi, menambahkan argumen dan mengembalikan eta penanganan kode (Dalam proyek C ++ misalnya, akan berguna jika membutuhkan sesuatu yang berjalan sebelum atau setelah konstruktor global).Saya lebih suka
__attribute__((constructor))/((destructor))
jika memungkinkan, ini adalah solusi sederhana dan elegan bahkan rasanya seperti curang. Untuk coders bare-metal seperti saya, ini tidak selalu menjadi pilihan.Beberapa referensi bagus di buku Linker & loader .
sumber
__attribute__((constructor))/((destructor))
destruktor tidak berjalan. Saya mencoba beberapa hal seperti menambahkan entri ke .dtor seperti yang ditunjukkan di atas. Tetapi tidak berhasil. Masalahnya mudah diduplikasi dengan menjalankan kode dengan numactl. Misalnya, anggap test_code berisi destructor (tambahkan printf ke fungsi konstruktor dan desctructor untuk men-debug masalah). Kemudian jalankanLD_PRELOAD=./test_code numactl -N 0 sleep 1
. Anda akan melihat bahwa konstruktor dipanggil dua kali tetapi destruktor hanya sekali.Halaman ini memberikan pemahaman yang besar tentang
constructor
dandestructor
pelaksanaan atribut dan bagian dalam dalam ELF yang memungkinkan mereka untuk bekerja. Setelah mencerna informasi yang disediakan di sini, saya mengumpulkan sedikit informasi tambahan dan (meminjam contoh bagian dari Michael Ambrus di atas) membuat contoh untuk menggambarkan konsep dan membantu pembelajaran saya. Hasil-hasil tersebut disediakan di bawah ini bersama dengan contoh sumber.Seperti dijelaskan di utas ini, atribut
constructor
dandestructor
membuat entri di bagian.ctors
dan.dtors
dari file objek. Anda dapat menempatkan referensi ke fungsi di salah satu bagian dalam salah satu dari tiga cara. (1) menggunakansection
atribut; (2)constructor
dandestructor
atribut atau (3) dengan panggilan inline-assembly (seperti yang dirujuk tautan dalam jawaban Ambrus).Penggunaan
constructor
dandestructor
atribut memungkinkan Anda untuk menetapkan prioritas pada konstruktor / destruktor untuk mengontrol urutan pelaksanaannya sebelummain()
dipanggil atau setelah kembali. Semakin rendah nilai prioritas yang diberikan, semakin tinggi prioritas eksekusi (prioritas yang lebih rendah dieksekusi sebelum prioritas yang lebih tinggi sebelum main () - dan setelah prioritas yang lebih tinggi setelah main ()). Nilai prioritas yang Anda berikan harus lebih besar daripada100
sebagai kompiler cadangan nilai prioritas antara 0-100 untuk implementasi. Aconstructor
ataudestructor
ditentukan dengan prioritas dijalankan sebelumconstructor
ataudestructor
ditentukan tanpa prioritas.Dengan atribut 'bagian' atau dengan perakitan inline, Anda juga dapat menempatkan referensi fungsi di bagian
.init
dan.fini
kode ELF yang akan dieksekusi sebelum konstruktor dan setelah destruktor apa pun, masing-masing. Setiap fungsi yang dipanggil oleh referensi fungsi yang ditempatkan di.init
bagian, akan dieksekusi sebelum referensi fungsi itu sendiri (seperti biasa).Saya telah mencoba menggambarkan masing-masing contoh di bawah ini:
keluaran:
Contoh ini membantu memperkuat perilaku konstruktor / penghancur, semoga akan bermanfaat bagi orang lain juga.
sumber
MAX_RESERVED_INIT_PRIORITY
), dan mereka sama dengan C ++ (init_priority
) 7,7 C ++ - Atribut Variabel Khusus, Fungsi, dan Jenis Atribut . Kemudian saya mencoba dengan99
:warning: constructor priorities from 0 to 100 are reserved for the implementation [enabled by default] void construct0 () __attribute__ ((constructor (99)));
.Berikut ini adalah contoh "konkret" (dan mungkin berguna ) tentang bagaimana, mengapa, dan kapan menggunakan konstruksi yang berguna, namun tidak sedap dipandang ini ...
Xcode menggunakan "global" "default pengguna" untuk memutuskan
XCTestObserver
kelas mana yang memuntahkannya ke konsol yang terkepung .Dalam contoh ini ... ketika saya secara implisit memuat pustaka psuedo ini, sebut saja ...
libdemure.a
, melalui bendera di target pengujian saya á la ..Aku ingin..
Saat memuat (mis. Ketika
XCTest
memuat bundel pengujian saya), timpa kelas "default"XCTest
"observer" ... (melaluiconstructor
fungsi) PS: Sejauh yang saya tahu .. apa pun yang dilakukan di sini dapat dilakukan dengan efek setara di dalam+ (void) load { ... }
metode kelas ' .jalankan tes saya .... dalam kasus ini, dengan verbositas yang lebih tidak masuk akal dalam log (implementasi atas permintaan)
Kembalikan kelas "global"
XCTestObserver
ke keadaan semula itu .. agar tidak mengganggu jalan lainXCTest
yang belum masuk dalam kereta musik (alias. Ditautkan kelibdemure.a
). Saya kira ini secara historis dilakukan dalamdealloc
.. tapi saya tidak akan mulai mengacaukan wanita tua itu.Begitu...
Tanpa bendera penghubung ... (Polisi mode mengeroyok Cupertino menuntut pembalasan , namun standar Apple menang, seperti yang diinginkan, di sini )
DENGAN
-ldemure.a
bendera penghubung ... (Hasil yang bisa dipahami , terkesiap ... "terima kasihconstructor
/destructor
" ... Sorak sorai )sumber
Ini adalah contoh konkret lainnya. Ini untuk perpustakaan bersama. Fungsi utama perpustakaan bersama adalah untuk berkomunikasi dengan pembaca kartu pintar. Tetapi ia juga dapat menerima 'informasi konfigurasi' saat runtime melalui udp. Udp ditangani oleh utas yang HARUS dimulai pada waktu init.
Perpustakaan ditulis dalam c.
sumber