Bir UIScrollView
(veya bunun türetilmiş bir sınıfı) kaydırılırken, kaydırma NSTimers
bitene kadar çalışanların tümü duraklatılmış gibi görünüyor .
Bunu aşmanın bir yolu var mı? İş Parçacığı? Öncelikli bir ayar mı? Herhangi bir şey?
Bir UIScrollView
(veya bunun türetilmiş bir sınıfı) kaydırılırken, kaydırma NSTimers
bitene kadar çalışanların tümü duraklatılmış gibi görünüyor .
Bunu aşmanın bir yolu var mı? İş Parçacığı? Öncelikli bir ayar mı? Herhangi bir şey?
Yanıtlar:
Uygulanması kolay ve basit bir çözüm şunları yapmaktır:
NSTimer *timer = [NSTimer timerWithTimeInterval:...
target:...
selector:....
userInfo:...
repeats:...];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
Swift 3 kullanan herkes için
timer = Timer.scheduledTimer(timeInterval: 0.1,
target: self,
selector: aSelector,
userInfo: nil,
repeats: true)
RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)
timer = Timer(timeInterval: 0.1, target: self, selector: aSelector, userInfo: nil, repeats: true)
yerine ilk komut olarak çağırmak daha iyidir Timer.scheduleTimer()
, çünkü çalışma scheduleTimer()
döngüsüne zamanlayıcı ekler ve sonraki çağrı aynı çalışma döngüsüne ancak farklı modda başka bir eklemedir. Aynı işi iki kez yapmayın.
Bu hızlı versiyon.
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: aSelector, userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
Kaydırma sırasında zamanlayıcıların ateşlenmesini istiyorsanız, başka bir iş parçacığı ve başka bir çalışma döngüsü çalıştırmanız gerekir; Zamanlayıcılar olay döngüsünün bir parçası olarak işlendiğinden, görünümünüzü kaydırmakla meşgulseniz, zamanlayıcılara asla ulaşamazsınız. Zamanlayıcıları diğer iş parçacıklarında çalıştırmanın performans / pil cezası bu durumu ele almaya değmeyebilir.
Swift 4'ü kullanan herkes için:
timer = Timer(timeInterval: 1, target: self, selector: #selector(timerUpdated), userInfo: nil, repeats: true)
RunLoop.main.add(timer, forMode: .common)
tl; dr runloop kaydırma yapıyor, böylece daha fazla olayı işleyemez - zamanlayıcıyı manuel olarak ayarlamadığınız sürece runloop dokunma olaylarını işlerken de gerçekleşebilir. Veya alternatif bir çözüm deneyin ve GCD'yi kullanın
Herhangi bir iOS geliştiricisi için mutlaka okunması gerekir. Sonuçta RunLoop aracılığıyla birçok şey yürütülür.
Apple'ın belgelerinden türetilmiştir .
Bir çalışma döngüsü, adından çok daha farklıdır. İş parçacığınızın girdiği ve gelen olaylara yanıt olarak olay işleyicilerini çalıştırmak için kullandığı bir döngüdür.
Zamanlayıcılar ve diğer periyodik olaylar, çalıştırma döngüsünü çalıştırdığınızda teslim edildiğinden, bu döngünün atlatılması bu olayların teslimini bozar. Bu davranışın tipik örneği, bir döngü girerek ve uygulamadan tekrar tekrar olaylar talep ederek bir fare izleme rutini uyguladığınızda ortaya çıkar. Kodunuz, uygulamanın bu olayları normal şekilde göndermesine izin vermek yerine olayları doğrudan yakaladığından, etkin zamanlayıcılar, fare izleme rutininizden çıkıp denetimi uygulamaya geri verene kadar tetiklenemez.
Bu, biz farkına bile varmadan ÇOK ZAMAN olur. Demek istediğim, zamanlayıcıyı 10: 10: 10: 00'a ayarladık, ancak döngü 10: 10: 10: 05'e kadar süren bir olayı gerçekleştiriyor, bu nedenle zamanlayıcı 10: 10: 10: 06'da çalışıyor.
Benzer şekilde, bir zamanlayıcı çalıştırma döngüsü bir işleyici rutini yürütmenin ortasındayken tetiklenirse, zamanlayıcı işleyici rutinini çağırmak için çalıştırma döngüsü boyunca bir sonraki sefere kadar bekler. Çalışma döngüsü hiç çalışmıyorsa, zamanlayıcı asla çalışmaz.
Zamanlayıcıları yalnızca bir kez veya tekrar tekrar olaylar oluşturacak şekilde yapılandırabilirsiniz. Tekrarlanan bir zamanlayıcı, gerçek ateşleme zamanına göre değil, planlanan atış süresine göre otomatik olarak yeniden planlar. Örneğin, bir zamanlayıcı belirli bir zamanda ve ondan sonra her 5 saniyede bir ateşlemek üzere programlandıysa, gerçek ateşleme süresi gecikse bile, programlanan ateşleme süresi her zaman orijinal 5 saniyelik zaman aralıklarına denk gelecektir. Ateşleme süresi, programlanmış ateşleme sürelerinden bir veya daha fazlasını kaçıracak kadar geciktirilirse, zamanlayıcı, kaçırılan süre boyunca yalnızca bir kez çalıştırılır. Kaçırılan süre için ateş ettikten sonra, zamanlayıcı bir sonraki planlanmış atış zamanı için yeniden planlanır.
Yapamazsın. İşletim sistemi sizin için sadece kendini değiştirir. örneğin, kullanıcı dokunduğunda, mod olarak değişir eventTracking
. Kullanıcı tıklamaları bittiğinde, mod geri döner default
. Bir şeyin belirli bir modda çalıştırılmasını istiyorsanız, bunun olmasını sağlamak size bağlıdır.
Kullanıcı kaydırırken Döngü Çalıştırma Modu olur tracking
. RunLoop, vites değiştirmek için tasarlanmıştır. Mod bir kez ayarlandığında eventTracking
, dokunma olaylarına öncelik verir (sınırlı CPU çekirdeğimiz olduğunu unutmayın). Bu, işletim sistemi tasarımcıları tarafından yapılmış bir mimari tasarımdır .
Varsayılan olarak, zamanlayıcılar tracking
modda programlanmaz . Planlanacaklar:
Bir zamanlayıcı oluşturur ve bunu varsayılan modda mevcut çalışma döngüsünde programlar .
Aşağıdakiler scheduledTimer
bunu yapar:
RunLoop.main.add(timer, forMode: .default)
Zamanlayıcınızın kaydırma sırasında çalışmasını istiyorsanız, şunlardan birini yapmanız gerekir:
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self,
selector: #selector(fireTimer), userInfo: nil, repeats: true) // sets it on `.default` mode
RunLoop.main.add(timer, forMode: .tracking) // AND Do this
Veya sadece şunu yapın:
RunLoop.main.add(timer, forMode: .common)
Sonuçta senin parçacığı yukarıdaki yollarla birini yaparak değil dokunma olayları tarafından engellendi. şuna eşdeğerdir:
RunLoop.main.add(timer, forMode: .default)
RunLoop.main.add(timer, forMode: .eventTracking)
RunLoop.main.add(timer, forMode: .modal) // This is more of a macOS thing for when you have a modal panel showing.
Zamanlayıcınız için kodunuzu çalıştırma döngüsü yönetimi sorunlarından "korumanıza" yardımcı olacak GCD'yi kullanmayı düşünebilirsiniz.
Yinelenmeyenler için sadece şunu kullanın:
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
// your code here
}
Tekrarlayan zamanlayıcılar için şunu kullanın:
DispatchSourceTimer'ı nasıl kullanacağınızı görün
Daniel Jalkut ile yaptığım bir tartışmadan daha derine inersek:
Soru: GCD (arka plan iş parçacıkları), örneğin bir arka plan iş parçacığındaki asyncAfter RunLoop'un dışında nasıl çalıştırılır? Bundan anladığım kadarıyla her şey bir RunLoop içinde yürütülecektir.
Her iş parçacığının en fazla bir çalıştırma döngüsü vardır, ancak iş parçacığının "sahipliğini" yürütmeyi koordine etmek için bir neden yoksa sıfır olabilir.
İş parçacıkları, sürecinize işlevselliğini birden çok paralel yürütme bağlamına bölme yeteneği veren işletim sistemi düzeyinde bir olanaktır. Çalıştırma döngüleri, birden çok kod yolu tarafından verimli bir şekilde paylaşılabilmesi için tek bir iş parçacığını daha da bölmenize olanak tanıyan çerçeve düzeyinde bir olanaktır.
Tipik olarak, bir iş parçacığı üzerinde çalıştırılan bir şey gönderirseniz, dolaylı olarak bir [NSRunLoop currentRunLoop]
tane oluşturacak bir şey çağırmadıkça muhtemelen bir runloop'a sahip olmayacaktır.
Özetle, modlar temelde girişler ve zamanlayıcılar için bir filtre mekanizmasıdır