Swift: ARC dan manajemen memori

Menjadi bahasa tingkat tinggi modern, Swift pada dasarnya menangani manajemen memori dalam aplikasi Anda, mengalokasikan dan membebaskan memori. Ini karena mekanisme yang disebut Penghitungan Referensi Otomatis , atau ARC . Dalam panduan ini, Anda akan mempelajari cara kerja ARC dan cara mengelola memori dengan benar di Swift. Memahami mekanisme ini, Anda bisa memengaruhi umur objek yang terletak di heap ( heap ).

Dalam panduan ini, Anda akan membangun pengetahuan Anda tentang Swift dan ARC dengan mempelajari yang berikut:

  • bagaimana ARC bekerja
  • apa itu siklus referensi dan bagaimana cara memperbaikinya dengan benar
  • cara membuat loop tautan contoh
  • Cara menemukan loop tautan menggunakan alat visual yang ditawarkan oleh Xcode
  • bagaimana menangani jenis referensi dan tipe nilai

Memulai


Unduh bahan sumbernya. Buka proyek di folder Siklus / Pemula . Pada bagian pertama panduan kami, memahami konsep-konsep utama, kami akan menangani secara eksklusif file t MainViewController.swif .

Tambahkan kelas ini di bagian bawah MainViewController.swift:

class User { let name: String init(name: String) { self.name = name print("User \(name) was initialized") } deinit { print("Deallocating user named: \(name)") } } 

Kelas Pengguna didefinisikan di sini, yang, dengan bantuan pernyataan cetak , memberi sinyal kepada kami tentang inisialisasi dan rilis instance kelas.

Sekarang buat instance kelas Pengguna di bagian atas MainViewController.

Tempatkan kode ini sebelum metode viewDidLoad () :

 let user = User(name: "John") 

Luncurkan aplikasi. Buat konsol Xcode terlihat dengan Command-Shift-Y untuk melihat output dari pernyataan cetak.

Perhatikan bahwa User John diinisialisasi muncul di konsol, tetapi pernyataan cetak di dalam deinit tidak dieksekusi. Ini berarti bahwa objek ini tidak dirilis, karena tidak keluar dari ruang lingkup .

Dengan kata lain, hingga view controller yang berisi objek ini keluar dari cakupan, objek tidak akan pernah dibebaskan.

Apakah dia dalam ruang lingkup?


Dengan membungkus instance dari kelas Pengguna dalam suatu metode, kami akan mengizinkannya keluar dari ruang lingkup, sehingga memungkinkan ARC untuk membebaskannya.

Mari kita buat metode runScenario () di dalam kelas MainViewController dan pindahkan inisialisasi instance kelas Pengguna di dalamnya.

 func runScenario() { let user = User(name: "John") } 

runScenario () mendefinisikan ruang lingkup instance Pengguna. Saat keluar dari zona ini, pengguna harus dibebaskan.

Sekarang panggil runScenario () menambahkan ini di akhir viewDidLoad ():

 runScenario() 

Luncurkan aplikasi. Output konsol sekarang terlihat seperti ini:

Pengguna John diinisialisasi
Deallocating pengguna bernama: John

Ini berarti bahwa Anda telah merilis objek yang telah meninggalkan bidang tampilan.

Seumur hidup objek



Keberadaan objek dibagi menjadi lima tahap:

  • alokasi memori: dari tumpukan atau dari tumpukan
  • inisialisasi: kode dieksekusi di dalam init
  • penggunaan
  • deinitialization: kode dieksekusi di dalam deinit
  • memori bebas: memori yang dialokasikan dikembalikan ke tumpukan atau tumpukan

Tidak ada cara langsung untuk melacak langkah-langkah mengalokasikan dan membebaskan memori, tetapi Anda dapat menggunakan kode di dalam init dan deinit.

Jumlah referensi , juga dikenal sebagai jumlah penggunaan , menentukan kapan suatu objek tidak lagi dibutuhkan. Penghitung ini menunjukkan jumlah mereka yang "menggunakan" objek ini. Objek menjadi tidak perlu saat penghitung penggunaan nol. Kemudian objek tersebut diinisialisasi dan dirilis.



Ketika objek Pengguna diinisialisasi, jumlah referensi adalah 1, karena konstanta pengguna merujuk ke objek ini.

Pada akhir runScenario (), pengguna keluar dari ruang lingkup dan jumlah referensi dikurangi menjadi 0. Akibatnya, pengguna tidak diinisialisasi dan kemudian dibebaskan.

Siklus Referensi


Dalam kebanyakan kasus, ARC berfungsi sebagaimana mestinya. Pengembang biasanya tidak perlu khawatir tentang kebocoran memori ketika benda yang tidak digunakan tetap tidak terisi tanpa batas.

Tapi tidak selalu! Kemungkinan memori bocor.

Bagaimana ini bisa terjadi? Bayangkan sebuah situasi di mana dua benda tidak lagi digunakan, tetapi masing-masing merujuk pada yang lain. Karena setiap jumlah referensi bukan 0, tidak satupun dari mereka akan dibebaskan.



Ini adalah siklus referensi yang kuat . Situasi ini membingungkan ARC dan tidak memungkinkannya untuk menghapus memori.

Seperti yang Anda lihat, jumlah referensi di akhir bukan 0, dan meskipun tidak diperlukan lagi objek, object1 dan object2 tidak akan dibebaskan.

Lihat tautan kami


Untuk menguji semua ini dalam tindakan, tambahkan kode ini setelah kelas Pengguna di MainViewController.swift:

 class Phone { let model: String var owner: User? init(model: String) { self.model = model print("Phone \(model) was initialized") } deinit { print("Deallocating phone named: \(model)") } } 

Kode ini menambahkan kelas Telepon baru dengan dua properti, satu untuk model dan satu untuk pemilik, serta metode init dan deinit. Properti pemilik adalah opsional, karena ponsel mungkin tidak memiliki pemilik.

Sekarang tambahkan baris ini ke runScenario ():

 let iPhone = Phone(model: "iPhone Xs") 

Ini akan membuat instance kelas Phone.

Pegang ponsel


Sekarang tambahkan kode ini ke kelas Pengguna, segera setelah properti nama:

 private(set) var phones: [Phone] = [] func add(phone: Phone) { phones.append(phone) phone.owner = self } 

Tambahkan array ponsel yang dimiliki oleh pengguna. Setter ditandai sebagai pribadi, jadi tambahkan (telepon :) harus digunakan.

Luncurkan aplikasi. Seperti yang Anda lihat, turunan dari objek Telepon dan objek Pengguna dibebaskan sesuai kebutuhan.

Pengguna John diinisialisasi
Telepon iPhone XS diinisialisasi
Deallocating phone bernama: iPhone Xs
Deallocating pengguna bernama: John

Sekarang tambahkan ini di akhir runScenario ():
 user.add(phone: iPhone) 


Di sini kami menambahkan iPhone kami ke daftar ponsel yang dimiliki oleh pengguna , dan juga mengatur properti pemilik ponsel ke ' pengguna '.

Jalankan aplikasi lagi. Anda akan melihat bahwa objek pengguna dan iPhone tidak dirilis. Siklus hubungan yang kuat di antara mereka mencegah ARC untuk melepaskannya.



Tautan Lemah


Untuk memutus siklus tautan kuat, Anda dapat menetapkan hubungan antara objek sebagai lemah.

Secara default, semua tautan kuat dan penugasan mengarah ke peningkatan jumlah referensi. Saat menggunakan referensi yang lemah, jumlah referensi tidak bertambah.

Dengan kata lain, tautan lemah tidak memengaruhi manajemen kehidupan suatu objek . Tautan lemah selalu dinyatakan opsional . Dengan begitu, saat jumlah tautan menjadi 0, tautan dapat disetel ke nol.



Dalam ilustrasi ini, garis putus-putus menunjukkan tautan lemah. Perhatikan bahwa jumlah referensi objek1 adalah 1, karena variabel1 merujuknya. Jumlah referensi objek2 adalah 2, karena direferensikan oleh variabel2 dan objek1.

object2 juga mereferensikan object1, tetapi WEAK , yang artinya tidak mempengaruhi jumlah referensi dari object1.

Ketika variabel1 dan variabel2 dibebaskan, object1 memiliki jumlah referensi 0, yang membebaskannya. Ini, pada gilirannya, melepaskan referensi kuat ke object2, yang sudah mengarah ke rilisnya.

Di kelas Telepon, ubah deklarasi properti pemilik sebagai berikut:

 weak var owner: User? 

Dengan mendeklarasikan referensi properti pemilik sebagai 'lemah', kami memutus loop tautan kuat antara kelas Pengguna dan Telepon.



Luncurkan aplikasi. Sekarang pengguna dan ponsel dilepaskan dengan benar.

Tautan yang Tidak Dimiliki


Ada juga pengubah tautan lain yang tidak menambah jumlah referensi: tidak dimiliki .

Apa perbedaan antara yang tidak dimiliki dan yang lemah ? Referensi yang lemah selalu opsional dan secara otomatis menjadi nol ketika objek yang direferensikan dirilis.

Inilah sebabnya mengapa kita harus mendeklarasikan properti lemah sebagai variabel opsional tipe: properti ini harus berubah.

Tautan yang tidak dimiliki, sebaliknya, tidak pernah opsional. Jika Anda mencoba mengakses properti yang tidak dimiliki yang merujuk ke objek yang dibebaskan, Anda mendapatkan kesalahan yang terlihat seperti gaya membuka bungkus yang berisi variabel nil (paksaan membuka bungkus).



Ayo coba terapkan yang tidak dimiliki .

Tambahkan kelas CarrierSubscription baru di akhir MainViewController.swift:

 class CarrierSubscription { let name: String let countryCode: String let number: String let user: User init(name: String, countryCode: String, number: String, user: User) { self.name = name self.countryCode = countryCode self.number = number self.user = user print("CarrierSubscription \(name) is initialized") } deinit { print("Deallocating CarrierSubscription named: \(name)") } } 

CarrierSubscription memiliki empat properti:

Nama: nama penyedia.
CountryCode: kode negara.
Nomor: nomor telepon.
Pengguna: tautan ke pengguna.

Siapa penyedia Anda?


Sekarang tambahkan ini ke kelas Pengguna setelah properti nama:

 var subscriptions: [CarrierSubscription] = [] 

Di sini kami menyimpan berbagai penyedia pengguna.

Sekarang tambahkan ini ke kelas Phone, setelah properti pemilik:

 var carrierSubscription: CarrierSubscription? func provision(carrierSubscription: CarrierSubscription) { self.carrierSubscription = carrierSubscription } func decommission() { carrierSubscription = nil } 

Ini menambahkan properti CarrierSubscription opsional dan dua metode untuk mendaftarkan dan membatalkan pendaftaran ponsel dengan penyedia.

Sekarang tambahkan kelas CarrierSubscription di dalam metode init, tepat sebelum pernyataan cetak:

 user.subscriptions.append(self) 

Kami menambahkan CarrierSubscription ke array penyedia pengguna.

Terakhir, tambahkan ini di akhir metode runScenario ():

 let subscription = CarrierSubscription( name: "TelBel", countryCode: "0032", number: "31415926", user: user) iPhone.provision(carrierSubscription: subscription) 

Kami membuat langganan ke penyedia untuk pengguna dan menghubungkan telepon ke sana.

Luncurkan aplikasi. Di konsol Anda akan melihat:

Pengguna John diinisialisasi
Telepon iPhone X diinisialisasi
CarrierSubscription TelBel diinisialisasi

Dan lagi siklus tautan! pengguna, iPhone, dan berlangganan tidak gratis pada akhirnya.

Bisakah Anda menemukan masalah?



Memutuskan rantai


Tautan dari pengguna ke langganan atau tautan dari langganan ke pengguna harus tidak dimiliki untuk memutus perulangan. Pertanyaannya adalah opsi mana yang harus dipilih. Mari kita lihat strukturnya.

Pengguna memiliki langganan ke penyedia, tetapi sebaliknya - tidak, langganan ke penyedia tidak memiliki pengguna.

Selain itu, tidak ada gunanya keberadaan CarrierSubscription tanpa referensi ke pengguna yang memilikinya.

Karena itu, tautan pengguna harus tidak dimiliki.

Ubah deklarasi pengguna di CarrierSubscription:

 unowned let user: User 

Sekarang pengguna tidak dimiliki, yang memecah loop tautan dan memungkinkan Anda untuk membebaskan semua objek.



Loop link dalam penutupan


Tautan siklus untuk objek terjadi ketika objek memiliki properti yang saling referensi. Seperti objek, penutupan adalah tipe referensi, dan dapat menyebabkan loop referensi. Penutupan menangkap objek yang mereka gunakan.

Misalnya, jika Anda menetapkan penutupan ke properti kelas, dan penutupan ini menggunakan properti dari kelas yang sama, maka kami mendapatkan loop tautan. Dengan kata lain, objek memegang tautan ke penutupan melalui properti. Penutupan berisi referensi ke objek melalui nilai diri yang ditangkap.



Tambahkan kode ini ke CarrierSubscription segera setelah properti pengguna:

 lazy var completePhoneNumber: () -> String = { self.countryCode + " " + self.number } 

Penutupan ini menghitung dan mengembalikan nomor telepon lengkap. Properti dinyatakan malas , properti akan ditetapkan pada penggunaan pertama.

Ini diperlukan karena menggunakan self.countryCode dan self.number, yang tidak akan tersedia sampai kode initializer dijalankan.

Tambahkan runScenario () ke akhir:

 print(subscription.completePhoneNumber()) 

Memanggil completePhoneNumber () akan menjalankan penutupan.

Luncurkan aplikasi dan Anda akan melihat bahwa pengguna dan iPhone dilepaskan, tetapi CarrierSubscription tidak, karena siklus tautan yang kuat antara objek dan penutupan.



Daftar Tangkapan


Swift menyediakan cara sederhana dan elegan untuk memutus perulangan tautan yang kuat dalam penutupan. Anda mendeklarasikan daftar tangkap di mana Anda menentukan hubungan antara penutupan dan objek yang ditangkapnya.

Untuk mendemonstrasikan daftar penangkapan, pertimbangkan kode berikut:

 var x = 5 var y = 5 let someClosure = { [x] in print("\(x), \(y)") } x = 6 y = 6 someClosure() // Prints 5, 6 print("\(x), \(y)") // Prints 6, 6 

x ada dalam daftar capture penutupan, sehingga nilai x disalin ke definisi penutupan. Itu ditangkap oleh nilai.

y tidak ada dalam daftar tangkap, ia ditangkap dengan referensi. Ini berarti bahwa nilai y akan seperti apa pada saat rangkaian itu dipanggil.

Daftar kunci membantu mengidentifikasi interaksi yang lemah atau tidak dimiliki sehubungan dengan objek yang ditangkap dalam loop. Dalam kasus kami, pilihan yang sesuai tidak dimiliki, karena penutupan tidak dapat terjadi jika turunan CarrierSubscription dilepaskan.

Pegang dirimu


Ganti definisi CompletePhoneNumber dengan CarrierSubscription ::

 lazy var completePhoneNumber: () -> String = { [unowned self] in return self.countryCode + " " + self.number } 

Kami menambahkan [diri yang tidak dimiliki] ke daftar penangkapan penutupan. Ini berarti bahwa kami menangkap diri sebagai tautan yang tidak dimiliki alih-alih yang kuat.

Luncurkan aplikasi dan Anda akan melihat bahwa CarrierSubscription sekarang dirilis.

Faktanya, sintaksis di atas adalah bentuk pendek yang lebih panjang dan lebih lengkap di mana variabel baru muncul:

 var closure = { [unowned newID = self] in // Use unowned newID here... } 

Di sini newID adalah salinan diri yang tidak dimiliki. Di luar penutupan, diri tetap ada. Dalam formulir singkat yang diberikan sebelumnya, kami membuat variabel diri baru yang mengaburkan diri yang ada di dalam penutupan.

Gunakan yang tidak dimiliki dengan hati-hati


Dalam kode Anda, hubungan antara diri dan completePhoneNumber ditetapkan sebagai tidak dimiliki.

Jika Anda yakin bahwa objek yang digunakan dalam penutupan tidak akan dirilis, Anda dapat menggunakan yang tidak dimiliki. Jika ya, Anda dalam masalah!

Tambahkan kode ini di akhir MainViewController.swift:

 class WWDCGreeting { let who: String init(who: String) { self.who = who } lazy var greetingMaker: () -> String = { [unowned self] in return "Hello \(self.who)." } } 

Sekarang inilah akhir dari runScenario ():

 let greetingMaker: () -> String do { let mermaid = WWDCGreeting(who: "caffeinated mermaid") greetingMaker = mermaid.greetingMaker } print(greetingMaker()) // ! 

Buka aplikasi dan Anda akan melihat crash dan sesuatu seperti itu di konsol:

Pengguna John diinisialisasi
Telepon iPhone XS diinisialisasi
CarrierSubscription TelBel diinisialisasi
0032 31415926
Kesalahan fatal: Mencoba membaca referensi yang tidak dimiliki tetapi objek 0x600000f0de30 sudah dibatalkan alokasi2019-02-24 12: 29: 40.744248-0600 Siklus [33489: 5926466] Kesalahan fatal: Mencoba membaca referensi yang tidak dikenal tetapi objek 0x600000f0de30 sudah dibatalkan alokasinya

Pengecualian terjadi karena penutupan menunggu untuk diri sendiri. Siapa yang ada, tetapi itu dirilis segera setelah putri duyung keluar dari ruang lingkup pada akhir blok do.

Contoh ini mungkin terlihat tersedot dari jari, tetapi hal-hal seperti itu terjadi. Misalnya, ketika kita menggunakan penutupan untuk memulai sesuatu nanti, katakanlah, setelah panggilan asinkron di jaringan berakhir.

Meredakan perangkap


Ganti pembuat ucapan di kelas WWDCGreeting dengan ini:

 lazy var greetingMaker: () -> String = { [weak self] in return "Hello \(self?.who)." } 

Kami melakukan dua hal: pertama, kami mengganti yang tidak dimiliki dengan yang lemah. Kedua, karena diri telah menjadi lemah, kita mengakses milik siapa melalui diri? Abaikan peringatan Xcode, kami akan segera memperbaikinya.

Aplikasi tidak lagi macet, tetapi jika Anda menjalankannya, kami mendapatkan hasil yang lucu: "Halo nil."

Mungkin hasilnya cukup dapat diterima, tetapi seringkali kita perlu melakukan sesuatu jika objek itu dibebaskan. Ini bisa dilakukan dengan menggunakan pernyataan penjaga.

Ganti teks penutup dengan ini:

 lazy var greetingMaker: () -> String = { [weak self] in guard let self = self else { return "No greeting available." } return "Hello \(self.who)." } 

Pernyataan penjaga menetapkan diri yang diambil dari diri yang lemah. Jika diri nihil, penutupan kembali "Tidak ada salam." Kalau tidak, diri menjadi referensi yang kuat, sehingga objek dijamin hidup sampai akhir penutupan.

Mencari loop tautan di Xcode 10


Sekarang setelah Anda memahami cara kerja ARC, apa itu loop tautan dan bagaimana cara memecahnya, inilah saatnya untuk melihat contoh aplikasi nyata.

Buka proyek Starter yang terletak di folder Kontak.

Luncurkan aplikasi.



Ini adalah pengelola kontak paling sederhana. Coba klik pada kontak, tambahkan beberapa yang baru.

Penugasan File:

ContactsTableViewController: menampilkan semua kontak.
DetailViewController: menampilkan informasi terperinci dari kontak yang dipilih.
NewContactViewController: memungkinkan Anda untuk menambahkan kontak baru.
ContactTableViewCell: sel tabel menampilkan detail kontak.
Kontak: model kontak.
Nomor: model nomor telepon.

Namun, dengan proyek ini, semuanya buruk: ada siklus tautan. Pada awalnya, pengguna tidak akan melihat masalah karena ukuran kecil dari memori yang bocor, untuk alasan yang sama sulit untuk menemukan kebocoran.

Untungnya, Xcode 10 memiliki alat bawaan untuk menemukan kebocoran memori terkecil.

Luncurkan aplikasi lagi. Hapus 3-4 kontak menggunakan gesek ke kiri dan tombol hapus. Sepertinya mereka benar-benar menghilang, kan?



Di mana itu mengalir?


Ketika aplikasi sedang berjalan, klik tombol Debug Memory Graph:



Amati Masalah Runtime di navigator Debug. Mereka ditandai dengan kotak ungu dengan tanda seru putih di dalamnya:



Pilih salah satu objek Kontak yang bermasalah di navigator. Siklusnya terlihat jelas: Kontak dan objek Nomor, merujuk satu sama lain, tahan.



Sepertinya Anda harus melihat ke dalam kode. Perlu diingat bahwa kontak dapat ada tanpa nomor, tetapi tidak sebaliknya.

Bagaimana Anda menyelesaikan loop ini? Tautan dari Kontak ke Nomor atau dari Nomor ke Kontak? lemah atau tidak dimiliki? Coba sendiri dulu!

Jika Anda membutuhkan bantuan ...
Ada 2 solusi yang mungkin: membuat tautan dari Kontak ke Nomor lemah, atau dari Nomor ke Kontak tidak dimiliki.

Dokumentasi Apple merekomendasikan bahwa objek induk memiliki referensi yang kuat untuk "anak" - bukan sebaliknya. Ini berarti bahwa kami memberikan Kontak referensi kuat ke Nomor, dan Nomor - tautan yang tidak dimiliki ke Kontak:

 class Number { unowned var contact: Contact // Other code... } class Contact { var number: Number? // Other code... } 


Bonus: loop dengan tipe referensi dan tipe nilai.


Swift memiliki tipe referensi (kelas dan penutupan) dan tipe nilai (struktur, enumerasi). Jenis nilai disalin ketika diteruskan, dan tipe referensi berbagi nilai yang sama menggunakan tautan.

Ini berarti bahwa dalam hal tipe nilai, tidak ada siklus. Agar perulangan dapat terjadi, kita membutuhkan setidaknya 2 jenis referensi.

Mari kita kembali ke proyek Cycles dan menambahkan kode ini di akhir MainViewController.swift:

 struct Node { // Error var payload = 0 var next: Node? } 

Tidak akan bekerja! Struktur adalah tipe nilai dan tidak dapat memiliki rekursi pada instance itu sendiri. Kalau tidak, struktur seperti itu akan memiliki ukuran yang tak terbatas.

Ubah struktur ke kelas.

 class Node { var payload = 0 var next: Node? } 

Referensi itu sendiri cukup dapat diterima untuk kelas (tipe referensi), sehingga kompiler tidak memiliki masalah.

Sekarang tambahkan ini di akhir MainViewController.swift:

 class Person { var name: String var friends: [Person] = [] init(name: String) { self.name = name print("New person instance: \(name)") } deinit { print("Person instance \(name) is being deallocated") } } 

Dan ini di akhir runScenario ():

 do { let ernie = Person(name: "Ernie") let bert = Person(name: "Bert") ernie.friends.append(bert) // Not deallocated bert.friends.append(ernie) // Not deallocated } 

Luncurkan aplikasi. Harap dicatat: ernie atau bert tidak dirilis.

Tautan dan makna


Ini adalah contoh kombinasi tipe referensi dan tipe nilai yang mengarah ke loop tautan.

ernie dan bert tetap tidak dirilis, memegang satu sama lain di array teman mereka, meskipun array itu sendiri adalah tipe nilai.

Cobalah untuk membuat arsip teman sebagai tidak dimiliki, dan Xcode akan menunjukkan kesalahan: tidak dimiliki hanya berlaku untuk kelas.

Untuk memperbaiki loop ini, kita harus membuat objek wrapper dan menggunakannya untuk menambahkan instance ke array.

Tambahkan definisi berikut sebelum kelas Person:

 class Unowned<T: AnyObject> { unowned var value: T init (_ value: T) { self.value = value } } 

Kemudian ubah definisi teman di kelas Person:

 var friends: [Unowned<Person>] = [] 

Akhirnya, ganti konten blok do di runScenario ():

 do { let ernie = Person(name: "Ernie") let bert = Person(name: "Bert") ernie.friends.append(Unowned(bert)) bert.friends.append(Unowned(ernie)) } 

Luncurkan aplikasi, sekarang ernie dan bert dirilis dengan benar!

Array teman tidak lagi merupakan kumpulan objek Orang. Ini sekarang adalah kumpulan objek yang tidak dimiliki yang berfungsi sebagai pembungkus untuk instance Person.

Untuk mendapatkan objek Orang dari Tidak Dimiliki, gunakan properti nilai:

 let firstFriend = bert.friends.first?.value // get ernie 

Kesimpulan


Anda sekarang memiliki pemahaman yang baik tentang manajemen memori di Swift dan Anda tahu cara kerja ARC. Saya harap publikasi ini bermanfaat bagi Anda.

Apple: Penghitungan Referensi Otomatis

Source: https://habr.com/ru/post/id451130/


All Articles