NSRunLoop'u Anlamak


109

Biri ne olduğunu açıklayabilir NSRunLoopmi? bildiğim gibi doğru NSRunLoopile bağlantılı bir şey NSThreadmi? Öyleyse, şöyle bir Konu oluşturduğumu varsayalım

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

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

bu iş parçacığı çalışmasını tamamladıktan sonra tamam mı? neden kullanılır RunLoopsveya nerede kullanılır? Apple belgelerinden bir şey okudum ama benim için net değil, bu yüzden lütfen mümkün olduğunca basit açıklayın


Bu sorunun kapsamı çok geniştir. Lütfen sorunuzu daha spesifik bir şekilde düzeltin.
Jody Hagins

3
ilk başta ben Genereal NSRunLoop yapmak bilmek istiyorum ve Konu ile bağlantılı nasıl
Taffarel

Yanıtlar:


211

Bir çalıştırma döngüsü, (diğer şeylerin yanı sıra) sistem giriş kaynaklarını (soketler, bağlantı noktaları, dosyalar, klavye, fare, zamanlayıcılar vb.) İşlemek için bir mekanizma sağlayan bir soyutlamadır.

Her NSThread'in, currentRunLoop yöntemi ile erişilebilen kendi çalışma döngüsü vardır.

Genel olarak, G / Ç işleme için hangi çalışma döngüsünü kullanacaklarını belirlemenize olanak tanıyan bazı (ağ oluşturma) bileşenleri olsa da, çalıştırma döngüsüne doğrudan erişmeniz gerekmez.

Belirli bir iş parçacığı için bir çalışma döngüsü, giriş kaynaklarından bir veya daha fazlasının bazı veri veya olaylara sahip olmasını bekler, ardından "hazır" olan her bir giriş kaynağını işlemek için uygun giriş işleyicilerini çalıştırır.

Bunu yaptıktan sonra, kendi döngüsüne geri dönecek, çeşitli kaynaklardan gelen girdileri işleyecek ve yapılacak bir iş yoksa "uyuyacak".

Bu oldukça yüksek seviyeli bir tanımdır (çok fazla ayrıntıdan kaçınmaya çalışmak).

DÜZENLE

Yorumu ele alma girişimi. Parçalara ayırdım.

  • bu sadece iş parçacığı içinde döngü çalıştırmak için erişebileceğim / çalıştırabileceğim anlamına mı geliyor?

Aslında. NSRunLoop iş parçacığı için güvenli değildir ve yalnızca döngüyü çalıştıran iş parçacığının bağlamından erişilmelidir.

  • Döngüyü çalıştırmak için olay eklemenin nasıl basit bir örneği var?

Bir bağlantı noktasını izlemek istiyorsanız, bu bağlantı noktasını çalıştırma döngüsüne eklemeniz yeterlidir ve ardından çalıştırma döngüsü, etkinlik için bu bağlantı noktasını izler.

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

Ayrıca şununla açıkça bir zamanlayıcı da ekleyebilirsiniz:

- (void)addTimer:(NSTimer *)aTimer forMode:(NSString *)mode
  • daha sonra kendi döngüsüne döneceği anlamına gelir?

Çalıştırma döngüsü, her yinelemede tüm hazır olayları işleyecektir (moduna göre). Genel bir yanıtın kapsamının biraz ötesinde olduğundan, çalıştırma modlarını keşfetmek için belgelere bakmanız gerekecektir.

  • iş parçacığını başlattığımda run döngüsü devre dışı mı?

Çoğu uygulamada, ana çalıştırma döngüsü otomatik olarak çalışacaktır. Ancak, döndürdüğünüz iş parçacıkları için çalıştırma döngüsünü başlatmak ve gelen olaylara yanıt vermekten siz sorumlusunuz.

  • İş parçacığı dışındaki iş parçacığı çalışma döngüsüne bazı olaylar eklemek mümkün mü?

Burada ne demek istediğinden emin değilim. Çalıştırma döngüsüne olay eklemezsiniz. Giriş kaynakları ve zamanlayıcı kaynakları eklersiniz (çalışma döngüsüne sahip olan iş parçacığından). Çalıştırma döngüsü daha sonra etkinlik için onları izler. Elbette, diğer iş parçacıkları ve işlemlerden veri girişi sağlayabilirsiniz, ancak girdi, çalıştırma döngüsünü çalıştıran iş parçacığı üzerindeki kaynakları izleyen çalıştırma döngüsü tarafından işlenecektir.

  • bazen iş parçacığını bir süreliğine engellemek için çalıştırma döngüsünü kullanabileceğim anlamına mı geliyor

Aslında. Aslında, bir çalışma döngüsü, olay işleyicisi dönene kadar olay işleyicisinde "kalacaktır". Bunu herhangi bir uygulamada yeterince basit bir şekilde görebilirsiniz. Uyuyan herhangi bir IO eylemi (örneğin, düğmeye basma) için bir işleyici kurun. Bu yöntem tamamlanıncaya kadar ana çalıştırma döngüsünü (ve tüm kullanıcı arayüzünü) engelleyeceksiniz.

Aynısı herhangi bir çalışma döngüsü için de geçerlidir.

Çalıştırma döngüleri ile ilgili aşağıdaki belgeleri okumanızı öneririm:

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

ve konu başlıklarında nasıl kullanıldıkları:

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


2
bu sadece iş parçacığı içinde döngü çalıştırmak için erişebileceğim / çalıştırabileceğim anlamına mı geliyor? Döngüyü çalıştırmak için olay eklemenin nasıl basit bir örneği var? daha sonra kendi döngüsüne döneceği anlamına gelir? iş parçacığını başlattığımda run döngüsü devre dışı mı? İş parçacığı dışındaki iş parçacığı çalışma döngüsüne bazı olaylar eklemek mümkün mü? bazen iş parçacığını bir süreliğine engellemek için çalıştırma döngüsünü kullanabileceğim anlamına mı geliyor?
taffarel

"Uyuyan herhangi bir GÇ eylemi (örneğin, düğmeye basma) için bir işleyici yükleyin." Parmağımı düğmede tutmaya devam edersem, ipliği bir süre bloke etmeye devam edecek mi ?!
Honey

Hayır. Demek istediğim, işleyici tamamlanana kadar runloop yeni olayları işlemiyor. İşleyicide uyursanız (veya uzun süren bir işlem yaparsanız), işleyici çalışmasını tamamlayana kadar çalıştırma döngüsü engellenir.
Jody Hagins

@taffarel , iş parçacığı dışındaki Thread çalışma döngüsüne bazı olaylar eklemek mümkün mü? Bu, "başka bir iş parçacığının çalışma döngüsünde istendiğinde çalıştıran kod yapabilir miyim" anlamına geliyorsa, yanıt gerçekten evettir. Sadece çağırın performSelector:onThread:withObject:waitUntilDone:, bir NSThreadnesneyi iletin ve seçiciniz o iş parçacığının runloop'una göre programlanacaktır.
Mecki

12

Çalıştırma döngüleri, etkileşimli uygulamaları komut satırı araçlarından ayıran şeydir .

  • Komut satırı araçları parametrelerle başlatılır, komutlarını uygular ve sonra çıkar.
  • Etkileşimli uygulamalar kullanıcı girişini bekler , tepki verir ve beklemeye devam eder.

Gönderen burada

Kullanıcı dokunana kadar beklemenize ve buna göre yanıt vermenize, tamamlayıcıyı tamamlayana kadar beklemenize ve sonuçlarını uygulamanıza, bir zamanlayıcı alana kadar beklemenize ve bir işlevi gerçekleştirmenize olanak tanırlar. Bir runloop'unuz yoksa, kullanıcı dokunuşlarını dinleyemezsiniz / bekleyemezsiniz, bir ağ çağrısının gerçekleşmesini bekleyemezsiniz, kullanmadığınız DispatchSourceTimerveya kullanmadığınız sürece x dakika içinde uyanamazsınız.DispatchWorkItem

Ayrıca bu yorumdan :

Arka plan konularının kendi runloop'ları yoktur, ancak sadece bir tane ekleyebilirsiniz. Örneğin, AFNetworking 2.x bunu yaptı. Arka plan iş parçacıkları üzerinde NSURLConnection veya NSTimer için denenmiş ve doğru bir teknikti, ancak daha yeni API'ler bunu yapma ihtiyacını ortadan kaldırdığı için bunu artık kendimiz yapmıyoruz. Ancak URLSession'ın yaptığı gibi, örneğin burada basit bir istek var , ana kuyrukta [resmin sol paneline bakın] tamamlama işleyicileri çalıştırıyor ve arka planda bir çalıştırma döngüsüne sahip olduğunu görebilirsiniz.


Özellikle şunlar hakkında: "Arka plan iş parçacıklarının kendi çalışma döngüleri yoktur". Aşağıdaki zamanlayıcı, eşzamansız bir gönderim için çalışmaz :

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
            })
        }
    }
}

Bence syncbloğun da çalışmasının nedeni şudur:

senkronizasyon blokları genellikle sadece kaynak kuyruklarından yürütülür . Bu örnekte, kaynak kuyruk ana kuyruktur, hedef kuyruk ne olursa olsun kuyruktur.

RunLoop.currentHer gönderiye giriş yaptığımı test etmek için .

Senkronizasyon gönderimi, ana kuyrukla aynı çalışma döngüsüne sahipti . Zaman uyumsuz blok içindeki RunLoop diğerlerinden farklı bir örnekti. Neden RunLoop.currentfarklı bir değeri nasıl döndürdüğünü düşünüyor olabilirsiniz . Paylaşılan bir değer değil mi ? Harika soru! Daha fazlasını okuyun:

ÖNEMLİ NOT:

Sınıf özellik current global değişkeni DEĞİLDİR.

Geçerli iş parçacığı için çalışma döngüsünü döndürür .

Bağlamsaldır. Yalnızca iş parçacığı kapsamında görülebilir, yani İş Parçacığı yerel depolama . Bununla ilgili daha fazla bilgi için buraya bakın .

Bu, zamanlayıcılarla ilgili bilinen bir sorundur. Kullanırsanız aynı sorunu yaşamazsınızDispatchSourceTimer


8

RunLoops, işlerin hemen gerçekleştiği bir kutu gibidir.

Temel olarak, bir RunLoop'ta, bazı olayları işlemeye gidersiniz ve ardından geri dönersiniz. Veya zaman aşımına ulaşılmadan önce herhangi bir olayı işlemiyorsa geri dönün. Eşzamansız NSURLConnections, Mevcut döngünüze müdahale etmeden arka planda veri işleme ve aynı zamanda eşzamanlı olarak verilere ihtiyacınız olduğunu söyleyebilirsiniz. Bu, eşzamansız hale getiren NSURLConnectionve arama anında veri sağlayan RunLoop yardımı ile yapılabilir . RunLoop'u şu şekilde kullanabilirsiniz:

NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];

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

Eğer bazı diğer çalışma tamamlamak ve set kadar bu RunLoop, bu çalışacaktır YourBoolFlag için YANLıŞ .

Benzer şekilde, onları dizilerde de kullanabilirsiniz.

Umarım bu size yardımcı olur.


0

Çalıştırma döngüleri, iş parçacıklarıyla ilişkili temel altyapının bir parçasıdır. Bir çalışma döngüsü, işi planlamak ve gelen olayların alınmasını koordine etmek için kullandığınız bir olay işleme döngüsüdür. Bir çalışma döngüsünün amacı, yapılacak iş olduğunda iş parçacığınızı meşgul tutmak ve iş parçacığı olmadığında iş parçacığını uyku moduna geçirmektir.

Buradan


CFRunLoop'un en önemli özelliği CFRunLoopModes'tur. CFRunLoop, "Döngü Kaynakları Çalıştır" sistemiyle çalışır. Kaynaklar, bir veya birkaç mod için bir çalışma döngüsüne kaydedilir ve çalışma döngüsünün kendisi belirli bir modda çalışacak şekilde yapılır. Bir kaynağa bir olay ulaştığında, yalnızca kaynak modu çalışma döngüsü akım modu ile eşleşiyorsa çalışma döngüsü tarafından işlenir.

Buradan

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.