Trong khi một UIScrollView
(hoặc một lớp dẫn xuất của nó) đang cuộn, có vẻ như tất cả những NSTimers
thứ đang chạy sẽ bị tạm dừng cho đến khi cuộn xong.
Có cách nào để giải quyết vấn đề này không? Đề bài? Một cài đặt ưu tiên? Có gì không?
Trong khi một UIScrollView
(hoặc một lớp dẫn xuất của nó) đang cuộn, có vẻ như tất cả những NSTimers
thứ đang chạy sẽ bị tạm dừng cho đến khi cuộn xong.
Có cách nào để giải quyết vấn đề này không? Đề bài? Một cài đặt ưu tiên? Có gì không?
Câu trả lời:
Một giải pháp dễ dàng và đơn giản để thực hiện là:
NSTimer *timer = [NSTimer timerWithTimeInterval:...
target:...
selector:....
userInfo:...
repeats:...];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
Đối với bất kỳ ai sử dụng Swift 3
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)
như lệnh đầu tiên thay vì Timer.scheduleTimer()
, vì scheduleTimer()
thêm bộ đếm thời gian vào runloop và lệnh gọi tiếp theo là một cách thêm vào cùng một runloop nhưng với chế độ khác. Đừng làm cùng một công việc hai lần.
Đây là phiên bản nhanh chóng.
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: aSelector, userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
Bạn phải chạy một luồng khác và một vòng lặp khác nếu bạn muốn bộ hẹn giờ kích hoạt trong khi cuộn; vì bộ tính giờ được xử lý như một phần của vòng lặp sự kiện, nếu bạn đang bận xử lý việc cuộn chế độ xem của mình, bạn sẽ không bao giờ xem được bộ hẹn giờ. Mặc dù lỗi hiệu suất / pin khi chạy bộ hẹn giờ trên các luồng khác có thể không đáng để xử lý trong trường hợp này.
cho bất kỳ ai sử dụng Swift 4:
timer = Timer(timeInterval: 1, target: self, selector: #selector(timerUpdated), userInfo: nil, repeats: true)
RunLoop.main.add(timer, forMode: .common)
tl; dr the runloop đang thực hiện cuộn nên nó không thể xử lý bất kỳ sự kiện nào nữa - trừ khi bạn đặt bộ đếm thời gian theo cách thủ công để nó cũng có thể xảy ra khi runloop đang xử lý các sự kiện chạm. Hoặc thử một giải pháp thay thế và sử dụng GCD
A phải đọc cho bất kỳ nhà phát triển iOS nào. Rất nhiều thứ cuối cùng được thực thi thông qua RunLoop.
Bắt nguồn từ tài liệu của Apple .
Một vòng lặp chạy rất giống với tên của nó. Nó là một vòng lặp mà chuỗi của bạn nhập vào và sử dụng để chạy các trình xử lý sự kiện để đáp ứng với các sự kiện đến
Vì bộ hẹn giờ và các sự kiện định kỳ khác được phân phối khi bạn chạy vòng lặp chạy, việc phá vỡ vòng lặp đó sẽ làm gián đoạn việc phân phối các sự kiện đó. Ví dụ điển hình của hành vi này xảy ra bất cứ khi nào bạn thực hiện quy trình theo dõi chuột bằng cách nhập một vòng lặp và liên tục yêu cầu các sự kiện từ ứng dụng. Bởi vì mã của bạn đang nắm bắt các sự kiện trực tiếp, thay vì để ứng dụng gửi các sự kiện đó một cách bình thường, các bộ hẹn giờ hoạt động sẽ không thể kích hoạt cho đến khi quy trình theo dõi chuột của bạn thoát và trả lại quyền kiểm soát cho ứng dụng.
Điều này xảy ra RẤT NHIỀU LẦN, mà chúng tôi không bao giờ nhận ra. Ý tôi là chúng tôi đặt bộ hẹn giờ kích hoạt lúc 10: 10: 10: 00, nhưng đường chạy đang thực hiện một sự kiện kéo dài đến 10: 10: 10: 05, do đó bộ hẹn giờ được kích hoạt vào 10: 10: 10: 06
Tương tự, nếu bộ đếm thời gian kích hoạt khi vòng lặp chạy đang trong quá trình thực hiện quy trình xử lý, thì bộ định thời sẽ đợi cho đến lần tiếp theo thông qua vòng lặp chạy để gọi quy trình xử lý của nó. Nếu vòng lặp chạy hoàn toàn không chạy, đồng hồ sẽ không bao giờ hoạt động.
Bạn có thể định cấu hình bộ hẹn giờ để tạo sự kiện chỉ một lần hoặc nhiều lần. Bộ hẹn giờ lặp lại tự động lên lịch lại dựa trên thời gian bắn đã lên lịch, không phải thời gian bắn thực tế. Ví dụ: nếu bộ hẹn giờ được lên lịch để bắn vào một thời điểm cụ thể và cứ sau 5 giây sau đó, thời gian bắn theo lịch sẽ luôn rơi vào khoảng thời gian 5 giây ban đầu, ngay cả khi thời gian bắn thực tế bị trì hoãn. Nếu thời gian bắn bị trì hoãn đến mức bỏ lỡ một hoặc nhiều thời gian bắn theo lịch trình, bộ đếm thời gian chỉ được kích hoạt một lần trong khoảng thời gian đã bỏ lỡ. Sau khi kích hoạt trong khoảng thời gian đã bỏ lỡ, bộ đếm thời gian được lên lịch lại cho thời gian kích hoạt theo lịch trình tiếp theo.
Bạn không thể. Hệ điều hành chỉ tự thay đổi cho bạn. Ví dụ: khi người dùng nhấn, thì chế độ sẽ chuyển sang eventTracking
. Khi người dùng nhấn xong, chế độ sẽ quay trở lại default
. Nếu bạn muốn một thứ gì đó được chạy ở một chế độ cụ thể, thì bạn phải đảm bảo điều đó xảy ra.
Khi người dùng cuộn, Chế độ vòng lặp chạy sẽ trở thành tracking
. RunLoop được thiết kế để sang số. Khi chế độ được đặt thành eventTracking
, thì nó sẽ ưu tiên (hãy nhớ rằng chúng tôi có số lõi CPU hạn chế) để chạm vào các sự kiện. Đây là một thiết kế kiến trúc của các nhà thiết kế hệ điều hành .
Theo mặc định, bộ hẹn giờ KHÔNG được lên lịch trên tracking
chế độ. Họ được lên lịch vào:
Tạo bộ hẹn giờ và lên lịch cho vòng chạy hiện tại ở chế độ mặc định .
Bên scheduledTimer
dưới thực hiện điều này:
RunLoop.main.add(timer, forMode: .default)
Nếu bạn muốn bộ đếm thời gian của mình hoạt động khi cuộn thì bạn phải thực hiện một trong hai thao tác sau:
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
Hoặc chỉ cần làm:
RunLoop.main.add(timer, forMode: .common)
Cuối cùng, thực hiện một trong những điều trên có nghĩa là chuỗi của bạn không bị chặn bởi các sự kiện chạm. tương đương với:
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.
Bạn có thể cân nhắc sử dụng GCD cho bộ đếm thời gian của mình, điều này sẽ giúp bạn "bảo vệ" mã của mình khỏi các vấn đề quản lý vòng lặp chạy.
Để không lặp lại, chỉ cần sử dụng:
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
// your code here
}
Đối với việc sử dụng bộ hẹn giờ lặp lại:
Xem cách sử dụng DispatchSourceTimer
Tìm hiểu sâu hơn từ cuộc thảo luận tôi đã có với Daniel Jalkut:
Câu hỏi: Làm thế nào để GCD (luồng nền), ví dụ như asyncAfter trên luồng nền được thực thi bên ngoài RunLoop? Sự hiểu biết của tôi từ điều này là mọi thứ phải được thực thi trong RunLoop
Không nhất thiết - mỗi luồng có nhiều nhất một vòng lặp chạy, nhưng có thể có 0 nếu không có lý do gì để phối hợp thực thi "quyền sở hữu" của luồng.
Luồng là khả năng chi trả ở mức hệ điều hành cung cấp cho quy trình của bạn khả năng phân chia chức năng của nó trên nhiều bối cảnh thực thi song song. Các vòng lặp chạy là khả năng chi trả ở mức khung công tác cho phép bạn chia nhỏ thêm một luồng đơn lẻ để nó có thể được chia sẻ hiệu quả bằng nhiều đường dẫn mã.
Thông thường, nếu bạn gửi một cái gì đó được chạy trên một chuỗi, nó có thể sẽ không có một vòng chạy trừ khi một cái gì đó gọi [NSRunLoop currentRunLoop]
mà sẽ ngầm tạo ra một chuỗi .
Tóm lại, các chế độ về cơ bản là một cơ chế lọc cho các đầu vào và bộ hẹn giờ