Hiểu NSRunLoop


108

Bất cứ ai có thể giải thích cho những gì là NSRunLoop? vì vậy như tôi biết NSRunLooplà một cái gì đó được kết nối với NSThreadphải không? Vì vậy, giả sử tôi tạo một Chủ đề như

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

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

vì vậy sau khi Thread này kết thúc công việc của mình phải không? tại sao sử dụng RunLoopshoặc sử dụng ở đâu? từ tài liệu của Apple Tôi đã đọc một cái gì đó nhưng nó không rõ ràng với tôi, vì vậy hãy giải thích càng đơn giản càng tốt


Câu hỏi này có phạm vi quá rộng. Vui lòng tinh chỉnh câu hỏi của bạn thành một cái gì đó cụ thể hơn.
Jody Hagins

3
Lúc đầu tôi muốn biết những gì làm trong genereal NSRunLoop và làm thế nào nó được kết nối với chủ đề
taffarel

Câu trả lời:


211

Vòng lặp chạy là một phần trừu tượng (trong số những thứ khác) cung cấp một cơ chế để xử lý các nguồn đầu vào của hệ thống (ổ cắm, cổng, tệp, bàn phím, chuột, bộ định thời, v.v.).

Mỗi NSThread có vòng lặp chạy riêng, có thể được truy cập thông qua phương thức currentRunLoop.

Nói chung, bạn không cần phải truy cập trực tiếp vào vòng lặp chạy, mặc dù có một số thành phần (mạng) có thể cho phép bạn chỉ định vòng lặp chạy mà chúng sẽ sử dụng để xử lý I / O.

Vòng lặp chạy cho một luồng nhất định sẽ đợi cho đến khi một hoặc nhiều nguồn đầu vào của nó có một số dữ liệu hoặc sự kiện, sau đó kích hoạt (các) trình xử lý đầu vào thích hợp để xử lý từng nguồn đầu vào đã "sẵn sàng".

Sau khi làm như vậy, nó sẽ quay trở lại vòng lặp của nó, xử lý đầu vào từ nhiều nguồn khác nhau và "ngủ" nếu không có việc gì phải làm.

Đó là một mô tả cấp độ khá cao (cố gắng tránh quá nhiều chi tiết).

BIÊN TẬP

Một nỗ lực để giải quyết bình luận. Tôi đã bẻ nó thành nhiều mảnh.

  • nó có nghĩa là tôi chỉ có thể truy cập / chạy để chạy vòng lặp bên trong luồng phải không?

Thật. NSRunLoop không an toàn cho luồng và chỉ nên được truy cập từ ngữ cảnh của luồng đang chạy vòng lặp.

  • có bất kỳ ví dụ đơn giản nào về cách thêm sự kiện để chạy vòng lặp không?

Nếu bạn muốn giám sát một cổng, bạn chỉ cần thêm cổng đó vào vòng lặp chạy, và sau đó vòng chạy sẽ theo dõi cổng đó hoạt động.

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

Bạn cũng có thể thêm bộ hẹn giờ một cách rõ ràng với

- (void)addTimer:(NSTimer *)aTimer forMode:(NSString *)mode
  • nghĩa là nó sẽ quay trở lại vòng lặp của nó là gì?

Vòng lặp chạy sẽ xử lý tất cả các sự kiện sẵn sàng mỗi lần lặp lại (theo chế độ của nó). Bạn sẽ cần xem tài liệu để khám phá về các chế độ chạy, vì điều đó hơi vượt quá phạm vi của một câu trả lời chung.

  • vòng lặp chạy không hoạt động khi tôi bắt đầu chuỗi?

Trong hầu hết các ứng dụng, vòng chạy chính sẽ tự động chạy. Tuy nhiên, bạn chịu trách nhiệm bắt đầu vòng lặp chạy và phản hồi các sự kiện đến cho các luồng bạn quay.

  • có thể thêm một số sự kiện vào chuỗi chạy vòng lặp bên ngoài chuỗi không?

Tôi không chắc bạn muốn nói gì ở đây. Bạn không thêm sự kiện vào vòng lặp chạy. Bạn thêm nguồn đầu vào và nguồn hẹn giờ (từ luồng sở hữu vòng lặp chạy). Vòng chạy sau đó xem chúng để biết hoạt động. Tất nhiên, bạn có thể cung cấp đầu vào dữ liệu từ các luồng và quy trình khác, nhưng đầu vào sẽ được xử lý bởi vòng lặp đang theo dõi các nguồn đó trên luồng đang chạy vòng lặp.

  • nó có nghĩa là đôi khi tôi có thể sử dụng vòng lặp chạy để chặn luồng trong một thời gian

Thật. Trên thực tế, một vòng lặp chạy sẽ "ở lại" trong một trình xử lý sự kiện cho đến khi trình xử lý sự kiện đó quay trở lại. Bạn có thể thấy điều này trong bất kỳ ứng dụng nào đơn giản là đủ. Cài đặt trình xử lý cho bất kỳ hành động IO nào (ví dụ: nhấn nút) ở chế độ ngủ. Bạn sẽ chặn vòng lặp chạy chính (và toàn bộ giao diện người dùng) cho đến khi phương thức đó hoàn tất.

Điều tương tự cũng áp dụng cho bất kỳ vòng lặp chạy nào.

Tôi khuyên bạn nên đọc tài liệu sau về vòng lặp chạy:

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

và cách chúng được sử dụng trong chuỗi:

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


2
nó có nghĩa là tôi chỉ có thể truy cập / chạy để chạy vòng lặp bên trong luồng phải không? có bất kỳ ví dụ đơn giản nào về cách thêm sự kiện để chạy vòng lặp không? nghĩa là nó sẽ quay trở lại vòng lặp của nó là gì? vòng lặp chạy không hoạt động khi tôi bắt đầu chuỗi? có thể thêm một số sự kiện vào chuỗi chạy vòng lặp bên ngoài chuỗi không? nó có nghĩa là đôi khi tôi có thể sử dụng vòng lặp chạy để chặn luồng trong một thời gian?
taffarel

"Cài đặt trình xử lý cho bất kỳ hành động IO nào (ví dụ: nhấn nút) ở chế độ ngủ." Ý bạn là nếu tôi tiếp tục giữ ngón tay của mình trên nút, nó sẽ tiếp tục chặn luồng trong một thời gian?!
Honey,

Không. Ý tôi là runloop không xử lý các sự kiện mới cho đến khi trình xử lý hoàn thành. Nếu bạn ngủ (hoặc thực hiện một số thao tác mất nhiều thời gian) trong trình xử lý, thì vòng lặp chạy sẽ chặn cho đến khi trình xử lý hoàn thành công việc của nó.
Jody Hagins

@taffarel có thể thêm một số sự kiện vào chuỗi chạy vòng lặp bên ngoài chuỗi không? Nếu điều đó có nghĩa là "tôi có thể làm cho mã chạy trên runloop của một luồng khác theo ý muốn" thì câu trả lời thực sự là có. Chỉ cần gọi performSelector:onThread:withObject:waitUntilDone:, truyền một NSThreadđối tượng và bộ chọn của bạn sẽ được lập lịch trên đường chạy của chuỗi đó.
Mecki

12

Vòng lặp chạy là thứ ngăn cách các ứng dụng tương tác với các công cụ dòng lệnh .

  • Các công cụ dòng lệnh được khởi chạy với các tham số, thực hiện lệnh của chúng, sau đó thoát.
  • Các ứng dụng tương tác chờ người dùng nhập, phản hồi, sau đó tiếp tục chờ.

Từ đây

Chúng cho phép bạn đợi cho đến khi người dùng nhấn và phản hồi tương ứng, đợi cho đến khi bạn nhận được một Trình xử lý và áp dụng kết quả của nó, đợi cho đến khi bạn nhận được bộ đếm thời gian và thực hiện một chức năng. Nếu bạn không có runloop thì bạn không thể nghe / chờ thao tác của người dùng, bạn không thể đợi cho đến khi cuộc gọi mạng đang diễn ra, bạn không thể bị đánh thức sau x phút trừ khi bạn sử dụng DispatchSourceTimerhoặcDispatchWorkItem

Cũng từ bình luận này :

Các chuỗi nền không có các vòng chạy riêng của chúng, nhưng bạn chỉ có thể thêm một vòng lặp. Ví dụ: AFNetworking 2.x đã làm được. Đó là kỹ thuật đã được thử và đúng cho NSURLConnection hoặc NSTimer trên các luồng nền, nhưng chúng tôi không còn tự làm điều này nữa, vì các API mới hơn loại bỏ sự cần thiết phải làm như vậy. Nhưng có vẻ như URLSession thực hiện, ví dụ: đây là một yêu cầu đơn giản , đang chạy trình xử lý hoàn thành [xem bảng bên trái của hình ảnh] trên hàng đợi chính và bạn có thể thấy nó có một vòng lặp chạy trên chuỗi nền


Cụ thể về: "Chủ đề nền không có vòng lặp riêng". Các bộ đếm thời gian sau thất bại trong việc cứu hỏa cho một async công văn:

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

Tôi nghĩ lý do synckhối cũng chạy là vì:

các khối đồng bộ thường chỉ được thực thi từ trong hàng đợi nguồn của chúng . Trong ví dụ này, hàng đợi nguồn là hàng đợi chính, hàng đợi bất kỳ là hàng đợi đích.

Để kiểm tra rằng tôi đã đăng nhập RunLoop.currentbên trong mọi công văn.

Công văn đồng bộ có vòng chạy giống như hàng đợi chính. Trong khi RunLoop trong khối async là một phiên bản khác với những cái khác. Bạn có thể đang nghĩ tại sao RunLoop.currentlại trả về một giá trị khác. Nó không phải là một giá trị được chia sẻ !? Câu hỏi tuyệt vời! Đọc thêm:

LƯU Ý QUAN TRỌNG:

Các bất động sản lớp current KHÔNG phải là một biến toàn cầu.

Trả về vòng lặp chạy cho luồng hiện tại .

Đó là ngữ cảnh. Nó chỉ hiển thị trong phạm vi của luồng tức là lưu trữ cục bộ của Thread . Để biết thêm về điều đó, hãy xem ở đây .

Đây là một vấn đề đã biết với bộ hẹn giờ. Bạn không gặp vấn đề tương tự nếu bạn sử dụngDispatchSourceTimer


8

RunLoops giống như một chiếc hộp nơi mọi thứ diễn ra.

Về cơ bản, trong RunLoop, bạn xử lý một số sự kiện và sau đó quay lại. Hoặc quay lại nếu nó không xử lý bất kỳ sự kiện nào trước khi hết thời gian chờ. Bạn có thể nói nó tương tự như NSURLConnections không đồng bộ, Xử lý dữ liệu ở chế độ nền mà không can thiệp vào vòng lặp hiện tại của bạn và đồng thời, bạn yêu cầu dữ liệu đồng bộ. Điều này có thể được thực hiện với sự trợ giúp của RunLoop làm cho không đồng bộ của bạn NSURLConnectionvà cung cấp dữ liệu tại thời điểm gọi. Bạn có thể sử dụng RunLoop như sau:

NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];

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

Trong RunLoop này, nó sẽ chạy cho đến khi bạn hoàn thành một số công việc khác của mình và đặt YourBoolFlag thành false .

Tương tự, bạn có thể sử dụng chúng trong chủ đề.

Hy vọng điều này sẽ giúp bạn.


0

Các vòng lặp chạy là một phần của cơ sở hạ tầng cơ bản được liên kết với các luồng. Vòng lặp chạy là một vòng lặp xử lý sự kiện mà bạn sử dụng để lập lịch làm việc và điều phối việc nhận các sự kiện đến. Mục đích của vòng lặp là giữ cho chuỗi của bạn bận khi có việc phải làm và đặt chuỗi của bạn ở trạng thái ngủ khi không có việc nào.

Từ đây


Tính năng quan trọng nhất của CFRunLoop là CFRunLoopModes. CFRunLoop hoạt động với hệ thống “Nguồn chạy vòng lặp”. Các nguồn được đăng ký trên một vòng chạy cho một hoặc một số chế độ và bản thân vòng chạy được tạo để chạy trong một chế độ nhất định. Khi một sự kiện đến trên một nguồn, nó chỉ được xử lý bởi vòng lặp chạy nếu chế độ nguồn khớp với chế độ hiện tại của vòng chạy.

Từ đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.