RunLoop, belirli bir iş parçacığında gelen olayların alınmasını ve işlenmesini koordine eden bir döngüdür.
RunLoop her iş parçacığında mevcuttur ancak varsayılan olarak bekleme modundadır ve herhangi bir iş yapmaz.
Geliştirici gerekirse çalıştırabilir ancak otomatik olarak çalışmayacaktır. Bunu yapmak için kod yazmanız gerekecektir.
Her şeyden önce RunLoop, gelen görevlerin akışını yönetmek ve bunları doğru zamanda yürütmek için tasarlanmıştır.
Bu, en çok kullanıcı arayüzüyle çalışırken, örneğin UIScrollView kullanılırken fark edilir.
Varsayılan olarak ana RunLoop her zaman uygulamada çalışır; sistemden gelen mesajları işler ve uygulamaya iletir. Bu tür mesajların bir örneği, örneğin bir kullanıcının ekrana tıkladığı bir olay olabilir.
Yardımcı iş parçacıkları RunLoop ihtiyacının kendi kendine belirlenmesini gerektirir. İhtiyacınız olursa, kendiniz yapılandırmanız ve çalıştırmanız gerekecektir. RunLoop'u varsayılan olarak çalıştırmak önerilmez; yalnızca iş parçacıklarıyla aktif etkileşime ihtiyaç duyduğumuz durumlarda gereklidir.
Ayrıca uygulamadaki tüm zamanlayıcılar runloop üzerinde yürütülür, dolayısıyla uygulamanızda onlarla etkileşime geçmeniz gerekiyorsa mutlaka runloop'un özelliklerini incelemeniz gerekir.
RunLoop bir döngüdür ve geliştiricinin belirli bir görevi ne zaman çalıştırması gerektiğini anlamasına yardımcı olan çeşitli çalışma modlarına sahiptir.
Yani RunLoop aşağıdaki modlarda olabilir:
Default
- Varsayılan mod, akış ücretsizdir ve bu modda büyük işlemler güvenle gerçekleştirilebilir.
Tracking
- Konu bazı önemli işler yapmakla meşgul. Bu noktada herhangi bir görevi çalıştırmamak veya en azından bazı küçük görevleri yürütmek daha iyidir.
Initialization
- Bu mod, akışın başlatılması sırasında bir kez yürütülür.
EventReceive
- Bu, genellikle kullanılmayan, sistem olaylarını almak için dahili bir moddur.
Common
- Pratik önemi olmayan bir yer tutucu moddur.
Ana RunLoop'ta bu modlar otomatik olarak değiştirilir; Geliştirici, kullanıcının arayüzün asılı olduğunu fark etmemesi için zaman alan görevleri gerçekleştirmek için bunları kullanabilir. Bir örneğe bakalım.
Diğer RunLoop'taki yürütme döngüsü yönetimi tam otomatik değildir. Yürütme döngüsünü uygun bir zamanda başlatacak bir iş parçacığı için kod yazmanız gerekir. Ayrıca olaylara uygun şekilde yanıt vermeniz ve yürütme döngüsünün durmamasını sağlamak için sonsuz döngüler kullanmanız gerekir.
Bir UIScrollView'ımız var ve kullanıcının hiçbir şey fark etmemesi için ana iş parçacığı üzerinde büyük bir görev gerçekleştirmemiz gerekiyor.
Görevi her zamanki gibi tamamlayabiliriz:
DispatchQueue.main.async { sleep(2) self.tableView.refreshControl?.endRefreshing() }
Ama sonuç oldukça kötü olacak. Kullanıcı uygulamada önemli gecikmeler fark edecektir.
Bu olumsuz etki, ana iş parçacığı üzerinde o anda ne olduğuna dikkat etmeden bir görevi çalıştırmamızdan kaynaklanmaktadır.
Bu nedenle büyük görevimizi kullanıcının arayüzle etkileşime girdiği anda gerçekleştirmeye başlıyoruz. Bu da elbette kullanıcının arayüzün asılı olduğunu görmesine neden oluyor.
RunLoop mekanizması kullanılarak bu önlenebilir. Aynı mantığı şunu kullanarak uygulayalım:
CFRunLoopPerformBlock(CFRunLoopGetMain(), CFRunLoopMode.defaultMode.rawValue) { sleep(2) self.tableView.refreshControl?.endRefreshing() }
Burada ne olduğunu açıklayayım. CFRunLoopPerformBlock işlevi, RunLoop aracılığıyla yürütülecek kodu ekler. Kod bloğunun kendisine ek olarak bu fonksiyonun 2 önemli parametresi vardır.
İlki, hangi RunLoop'un işlevi yürütmesi gerektiğini seçmekten sorumludur. Bu örnekte "ana" kullanılmıştır.
İkincisi, görevin tamamlanacağı moddan sorumludur.
Toplamda üç olası mod vardır:
Ortak - varsayılan modu ve izleme modunu birleştirir.
Programı yukarıdaki kodla çalıştırmanın sonucu şöyle olacaktır:
Kullanıcı, kullanıcı arayüzü (UI) ile etkileşime girdiğinde, ana çalışma döngüsü "izleme" moduna geçer ve arayüzün düzgünlüğünü sağlamak için diğer tüm olayların işlenmesini geçici olarak askıya alır. Kullanıcı arayüzle etkileşimi durdurduğunda, çalıştırma döngüsü "varsayılan" moduna döner ve görevimizi yerine getirmeye devam eder.
Döngü, kullanıcı arayüzüne ek olarak zamanlayıcıların işleyişiyle de yakından bağlantılıdır.
Uygulamadaki herhangi bir zamanlayıcı bir döngü içinde çalışır ve özellikle ödeme işleme gibi önemli işlevlerden sorumlularsa, onlarla çalışırken hata yapmamak için ekstra dikkatli olmanız gerekir.
Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in // makeSomething }
Zamanlayıcı varsayılan olarak varsayılan modda başlar, dolayısıyla kullanıcı o anda tabloda geziniyorsa çalışmayı durdurabilir. Bunun nedeni döngünün şu anda izleme modunda olmasıdır. Bu nedenle örnekteki kod düzgün çalışmayabilir. Ortak modda bir zamanlayıcı ekleyerek bu sorunu çözebilirsiniz.
let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in // makeSomething } RunLoop.main.add(timer, forMode: .common)
Ayrıca zamanlayıcılar beklenen zamanda tetiklenmeyebilir veya hiç tetiklenmeyebilir. Bunun nedeni RunLoop'un yalnızca her döngünün başında zamanlayıcıları kontrol etmesidir. RunLoop bu aşamayı geçtikten sonra bir zamanlayıcı tetiklenirse, bir sonraki yinelemenin başlangıcına kadar bundan haberimiz olmayacak. Aynı zamanda görev RunLoop'ta ne kadar uzun süre çalışırsa gecikme de o kadar uzun olur.
Bu sorunu çözmek için yeni bir iş parçacığı oluşturabilir, bu iş parçacığında bir RunLoop başlatabilir ve ardından başka herhangi bir görev eklemeden iş parçacığına bir zamanlayıcı ekleyebilirsiniz; bu şekilde zamanlayıcı düzgün çalışacaktır.
let thread = Thread { let timer = Timer(timeInterval: 1.0, repeats: true) { timer in // makeSomething } RunLoop.current.add(timer, forMode: .default) RunLoop.current.run() } thread.start()
Bu yazımızda RunLoop'un ne olduğuna ve iOS uygulamalarında hangi sorunları çözdüğüne baktık. RunLoop, belirli bir iş parçacığı içinde gelen olayların alınmasını ve işlenmesini koordine eden bir döngüdür ve çeşitli çalışma modlarına sahiptir.
Kullanıcı arayüzü (UI) ve zamanlayıcılarla çalışırken özellikle kullanışlıdır çünkü görevleri doğru zamanda yürütme yeteneğine sahiptir, bu da arayüzün "kapatılmasını" önlemenize ve zamanlayıcıların doğru çalışmasını sağlamanıza olanak tanır.
Run Loop ile çalışmanın ek kodlama gerektirmesine rağmen uygulamanızın verimliliğini ve kararlılığını artıran değerli bir yatırımdır.