Memahami NSRunLoop

108

Adakah yang bisa menjelaskan untuk apa NSRunLoop? jadi yang saya tahu NSRunLoopapakah ada sesuatu yang berhubungan dengan NSThreadkan? Jadi asumsikan saya membuat seperti Thread

NSThread* th=[[NSThread alloc] initWithTarget:self selector:@selector(someMethod) object:nil];
[th start];

-(void) someMethod
{
    NSLog(@"operation");
}

jadi setelah Thread ini menyelesaikan pekerjaannya kan? mengapa menggunakan RunLoopsatau di mana menggunakan? dari dokumen Apple Saya telah membaca sesuatu tetapi tidak jelas bagi saya, jadi harap jelaskan sesederhana mungkin

taffarel
sumber
Pertanyaan ini terlalu luas cakupannya. Harap persempit pertanyaan Anda menjadi pertanyaan yang lebih spesifik.
Jody Hagins
3
pada awalnya saya ingin tahu apa yang dilakukan di NSRunLoop genereal dan bagaimana itu terhubung dengan Thread
taffarel

Jawaban:

211

Run loop adalah abstraksi yang (antara lain) menyediakan mekanisme untuk menangani sumber input sistem (soket, port, file, keyboard, mouse, timer, dll).

Setiap NSThread memiliki run loopnya sendiri, yang dapat diakses melalui metode currentRunLoop.

Secara umum, Anda tidak perlu mengakses run loop secara langsung, meskipun ada beberapa komponen (jaringan) yang memungkinkan Anda menentukan loop run mana yang akan mereka gunakan untuk pemrosesan I / O.

Run loop untuk thread tertentu akan menunggu hingga satu atau beberapa sumber inputnya memiliki beberapa data atau peristiwa, lalu mengaktifkan penangan input yang sesuai untuk memproses setiap sumber input yang "siap".

Setelah melakukannya, ia akan kembali ke loopnya, memproses masukan dari berbagai sumber, dan "tidur" jika tidak ada pekerjaan yang harus dilakukan.

Itu adalah deskripsi tingkat tinggi (mencoba menghindari terlalu banyak detail).

EDIT

Upaya untuk menanggapi komentar. Saya memecahnya menjadi beberapa bagian.

  • itu berarti saya hanya dapat mengakses / menjalankan untuk menjalankan loop di dalam utas, bukan?

Memang. NSRunLoop bukanlah thread safe, dan hanya boleh diakses dari konteks thread yang menjalankan loop.

  • apakah ada contoh sederhana bagaimana menambahkan event untuk menjalankan loop?

Jika Anda ingin memantau sebuah port, Anda cukup menambahkan port tersebut ke loop run, dan run loop akan mengawasi aktivitas port tersebut.

- (void)addPort:(NSPort *)aPort forMode:(NSString *)mode

Anda juga dapat menambahkan timer secara eksplisit dengan

- (void)addTimer:(NSTimer *)aTimer forMode:(NSString *)mode
  • apa artinya itu akan kembali ke loopnya?

Run loop akan memproses semua event yang siap setiap iterasi (sesuai dengan modenya). Anda perlu melihat dokumentasi untuk mengetahui tentang mode jalan, karena itu sedikit di luar cakupan jawaban umum.

  • apakah run loop tidak aktif ketika saya memulai utas?

Di sebagian besar aplikasi, run loop utama akan berjalan secara otomatis. Namun, Anda bertanggung jawab untuk memulai putaran proses dan merespons peristiwa masuk untuk utas yang Anda putar.

  • apakah mungkin untuk menambahkan beberapa event ke loop run Thread di luar thread?

Saya tidak yakin apa yang Anda maksud di sini. Anda tidak menambahkan acara ke run loop. Anda menambahkan sumber input dan sumber pengatur waktu (dari utas yang memiliki run loop). Run loop kemudian mengawasi mereka untuk aktivitas. Anda tentu saja dapat memberikan masukan data dari thread dan proses lain, tetapi masukan akan diproses oleh run loop yang memantau sumber-sumber tersebut di thread yang menjalankan loop jalan.

  • apakah itu berarti bahwa terkadang saya dapat menggunakan run loop untuk memblokir utas untuk suatu waktu

Memang. Nyatanya, run loop akan "tetap" di event handler sampai event handler itu kembali. Anda dapat melihat ini di aplikasi apa pun dengan cukup sederhana. Pasang penangan untuk setiap tindakan IO (mis., Menekan tombol) yang tidur. Anda akan memblokir loop proses utama (dan seluruh UI) hingga metode tersebut selesai.

Hal yang sama berlaku untuk semua run loop.

Saya sarankan Anda membaca dokumentasi berikut tentang run loop:

https://developer.apple.com/documentation/foundation/nsrunloop

dan bagaimana mereka digunakan dalam utas:

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW1

Jody Hagins
sumber
2
itu berarti saya hanya dapat mengakses / menjalankan untuk menjalankan loop di dalam utas, bukan? apakah ada contoh sederhana bagaimana menambahkan event untuk menjalankan loop? apa artinya itu akan kembali ke loopnya? apakah run loop tidak aktif ketika saya memulai utas? apakah mungkin untuk menambahkan beberapa event ke loop run Thread di luar thread? apakah itu berarti terkadang saya dapat menggunakan run loop untuk memblokir thread untuk sementara waktu?
taffarel
"Pasang penangan untuk tindakan IO apa pun (mis., Menekan tombol) yang tidur." Apakah maksud Anda jika saya terus menahan jari saya pada tombol, itu akan terus memblokir utas untuk sementara waktu ?!
Madu
Tidak. Yang saya maksud adalah bahwa runloop tidak memproses kejadian baru sampai penangan selesai. Jika anda tidur (atau melakukan beberapa operasi yang membutuhkan waktu lama) pada handler, maka run loop akan memblokir sampai handler menyelesaikan pekerjaannya.
Jody Hagins
@taffarel apakah mungkin untuk menambahkan beberapa acara ke loop run Thread di luar utas? Jika itu berarti "dapatkah saya membuat kode berjalan di runloop utas lain sesuka hati" maka jawabannya memang ya. Panggil saja performSelector:onThread:withObject:waitUntilDone:, meneruskan NSThreadobjek dan pemilih Anda akan dijadwalkan di runloop utas itu.
Mecki
12

Run loop adalah hal yang memisahkan aplikasi interaktif dari alat baris perintah .

  • Alat baris perintah diluncurkan dengan parameter, menjalankan perintahnya, lalu keluar.
  • Aplikasi interaktif menunggu masukan pengguna, bereaksi, lalu melanjutkan menunggu.

Dari sini

Mereka memungkinkan Anda untuk menunggu sampai pengguna mengetuk dan merespons sesuai, menunggu sampai Anda mendapatkan penyelesaianHandler dan menerapkan hasilnya, tunggu sampai Anda mendapatkan pengatur waktu dan melakukan suatu fungsi. Jika Anda tidak memiliki runloop maka Anda tidak dapat mendengarkan / menunggu ketukan pengguna, Anda tidak dapat menunggu sampai panggilan jaringan terjadi, Anda tidak dapat dibangunkan dalam x menit kecuali Anda menggunakan DispatchSourceTimeratauDispatchWorkItem

Juga dari komentar ini :

Untaian latar belakang tidak memiliki runloop sendiri, tetapi Anda dapat menambahkannya saja. Misalnya AFNetworking 2.x melakukannya. Itu telah dicoba dan teknik yang benar untuk NSURLConnection atau NSTimer pada utas latar belakang, tetapi kami tidak melakukan ini sendiri lagi, karena API yang lebih baru menghilangkan kebutuhan untuk melakukannya. Tetapi tampaknya URLSession melakukannya, misalnya, di sini adalah permintaan sederhana , menjalankan [lihat panel kiri gambar] penangan penyelesaian pada antrean utama, dan Anda dapat melihatnya menjalankan loop pada utas latar belakang


Khususnya tentang: "Thread latar belakang tidak memiliki runloopnya sendiri". Timer berikut gagal diaktifkan untuk pengiriman asinkron :

class T {
    var timer: Timer?

    func fireWithoutAnyQueue() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in
            print("without any queue") // success. It's being ran on main thread, since playgrounds begin running from main thread
        })
    }

    func fireFromQueueAsnyc() {
        let queue = DispatchQueue(label: "whatever")
        queue.async {
            self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { (_) in
                print("from a queue — async") // failed to print
            })
        }
    }

    func fireFromQueueSnyc() {
        let queue = DispatchQueue(label: "whatever")
        queue.sync {
            timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { (_) in
                print("from a queue — sync") // success. Weird. Read my possible explanation below
            })
        }
    }

    func fireFromMain() {
        DispatchQueue.main.async {
            self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { (_) in
                print("from main queue — sync") //success
            })
        }
    }
}

Saya pikir alasan syncblokir juga berjalan adalah karena:

blok sinkronisasi biasanya dieksekusi dari dalam antrian sumbernya . Dalam contoh ini, antrian sumber adalah antrian utama, antrian apapun yang merupakan antrian tujuan.

Untuk menguji bahwa saya masuk ke RunLoop.currentdalam setiap pengiriman.

Pengiriman sinkronisasi memiliki runloop yang sama dengan antrean utama. Sedangkan RunLoop dalam blok async adalah instance yang berbeda dari yang lain. Anda mungkin berpikir bagaimana mengapa RunLoop.currentmengembalikan nilai yang berbeda. Bukankah itu nilai bersama !? Pertanyaan bagus! Baca lebih lanjut:

CATATAN PENTING:

The properti kelas current adalah TIDAK variabel global.

Mengembalikan putaran proses untuk utas saat ini .

Ini kontekstual. Itu hanya terlihat dalam cakupan utas yaitu Penyimpanan lokal- utas . Untuk lebih lanjut lihat di sini .

Ini adalah masalah umum dengan pengatur waktu. Anda tidak memiliki masalah yang sama jika Anda menggunakanDispatchSourceTimer

Madu
sumber
8

RunLoops mirip seperti kotak tempat sesuatu terjadi begitu saja.

Pada dasarnya, dalam RunLoop, Anda pergi ke proses beberapa acara dan kemudian kembali. Atau kembalikan jika tidak memproses peristiwa apa pun sebelum batas waktu tercapai. Anda dapat mengatakannya sebagai mirip dengan NSURLConnections asynchronous, Memproses data di latar belakang tanpa mengganggu loop Anda saat ini dan tetapi pada saat yang sama, Anda memerlukan data secara sinkron. Yang dapat dilakukan dengan bantuan RunLoop yang membuat Anda asinkron NSURLConnectiondan menyediakan data pada waktu panggilan. Anda dapat menggunakan RunLoop seperti ini:

NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];

while (YourBoolFlag && [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate:loopUntil]) {
    loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];
}

Dalam RunLoop ini, ini akan berjalan sampai Anda menyelesaikan beberapa pekerjaan Anda yang lain dan menyetel YourBoolFlag ke false .

Demikian pula, Anda dapat menggunakannya di utas.

Semoga ini bisa membantu Anda.

Akshay Sunderwani
sumber
0

Run loop adalah bagian dari infrastruktur dasar yang terkait dengan utas. Run loop adalah loop pemrosesan peristiwa yang Anda gunakan untuk menjadwalkan pekerjaan dan mengoordinasikan penerimaan peristiwa yang masuk. Tujuan dari run loop adalah untuk membuat thread Anda tetap sibuk ketika ada pekerjaan yang harus dilakukan dan meletakkan thread Anda ke mode sleep ketika tidak ada.

Dari sini


Fitur terpenting dari CFRunLoop adalah CFRunLoopModes. CFRunLoop bekerja dengan sistem "Run Loop Sources". Sumber didaftarkan pada loop run untuk satu atau beberapa mode, dan run loop itu sendiri dibuat untuk berjalan dalam mode tertentu. Ketika sebuah event tiba di sebuah sumber, itu hanya ditangani oleh run loop jika mode sumber cocok dengan mode run loop saat ini.

Dari sini

dengApro
sumber