1、簡介網絡
Dispatch Sources經常使用於處理跟系統有關的事件,協調處理指定的低級別的系統事件。在配置Dispatch Source時,需指定監控的事件類型、Dispatch Queues、Event Handle(blocks/functions)。當被監控的事件發生時,Dispatch Source提交Event Handle到指定的Dispatch Queues。app
不一樣於手動提交到queue中的任務,dispatch sources給應用提供了持續的事件資源。dispatch source除了明確取消,不然會持續與dispatch queue相關聯。無論何時指定的事件發生時,就會提交任務到關聯着的dispatch queue中。例如,定時器事件週期性的發生,還有大多數只有在指定條件下才發生的事件。爲此,dispatch sources持有關聯的dispatch queue,避免事件仍然會發生而dispatch queue被釋放了。異步
爲了不event handle被積壓在某個dispatch queue中,dispatch sources實現事件合併方案。若是前一個任務已出列並在處理時,新的事件到來了,dispatch source合併新事件和舊事件的數據。合併規則取決於事件的類型,合併可能代替舊事件,或者更新舊事件的數據。例如,基於信號的dispatch source會提供最近相關的信息,但也報告自從上次事件處理髮生以來總共發出了多少信號量。socket
Dispatch Sources包括這幾類:Timer dispatch sources、Signal dispatch sources、Descriptor sources、Process dispatch sources、Mach port dispatch sources和Custom dispatch sources。async
一、Timer dispatch sources週期性通知。
二、Signal dispatch sources爲unix信號發出時通知。
三、Descriptor sources各類各樣的file-和socket-操做通知。如從文件或者網絡中讀/寫數據,或文件名被重命名,或文件被刪、被移動、數據內容改動時。
四、Process dispatch sources父子process退出時等等操做通知。
五、Mach port dispatch sources
六、Custom dispatch sources函數
2、建立Dispatch Sources動畫
dispatch_source_create函數返回的是出於暫停狀態的dispatch source,在暫停狀態時,dispatch source接收通知但並不執行event handle。ui
一、Event Handlespa
event handle用於處理dispatch source的通知,經過dispatch_source_set_event_handle函數,爲dispatch source建立function/block類型的event handle。當事件到來時,dispatch source提交event handle到指定的dispatch queue。unix
event handle爲處理即將到來的全部事件負責。假設上一個event handle已經在隊列中等待被執行,又有新的event handle請求添加到queue中,dispatch source會合並兩個事件。然而當event handel正在執行,dispatch source等待它執行結束後,再將event handle提交到queue中。
// 基於block的event handle沒有參數也沒有返回值。 void (^dispatch_block_t)(void) // 基於function的event handle包括上下文指針和dispatch source對象,無返回值。 void (*dispatch_function_t)(void *)
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, myDescriptor, 0, myQueue); dispatch_source_set_event_handler(source, ^{ // block從外部捕獲到source變量 size_t estimated = dispatch_source_get_data(source); // Continue reading the descriptor... }); dispatch_resume(source);
二、Cancellation Handle
Cancellation handle用於在dispatch source被釋放前清理dispatch source。在大多數dispatch source類型中它是選擇性被實現,除了descriptor/mach port dispatch source需經過cancellation handle去關閉descriptor和釋放mach port。
dispatch_source_set_cancel_handler(mySource, ^{ close(fd); // Close a file descriptor opened earlier. });
三、Target Queue
在建立dispatch source須要指定調度event handle/cancellation handle的dispatch queue。在指定以後,還能夠經過dispatch_set_target_queue函數修改關聯的dispatch queue。通常修改target queue是用於修改queue的優先級,該操做是異步操做。所以在作修改操做前,已在舊dispatch queue中的任務繼續被調度執行。若是剛好在修改過程當中,添加任務到queue,該queue多是舊queue,也多是新queue。
四、Custom Data
跟GCD同樣,dispatch source能夠經過dispatch_set_context關聯自定義數據,原理是經過context pointer存儲event handle中須要用到的數據。注意的是建立了context pointer,就必須經過cancellation handle最終釋放那些存儲的數據。
另外一種方案是經過event handle用block實現,雖然也能捕獲變量,但變量隨時可能被釋放。所以這種方案須要經過拷貝並持有數據防止變量被回收,最終再經過cancellation handle釋放該變量。
五、Memory Management
知足內存管理原則,能夠經過dispatch_retain/dispatch_release來控制。
3、Dispatch Source案例
一、Create a Timer
timer dispatch source是週期性的timers,類型爲DISPATCH_SOURCE_TYPE_TIMER,leeway值是設置的容差值,若是leeway爲0,系統也沒法保證在指定週期執行任務。它經常使用於遊戲等應用刷新頻幕和動畫。
當電腦進入休眠時,timer dispatch source也被暫停,電腦恢復時它恢復,暫停會影響timer下一次執行。若是經過dispatch_time建立的timer,時間爲相對時間,它會使用系統鬧鐘,系統鬧鐘在電腦休眠時不會轉動。但若是經過 dispatch_walltime建立的timer,時間爲絕對時間,它使用wall鬧鐘,經常使用於大的時間間隔。
dispatch_source_t CreateDispatchTimer(uint64_t interval, uint64_t leeway, dispatch_queue_t queue, dispatch_block_t block) { dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); if (timer){ dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway); // 時間間隔夠長,因此用dispatch_walltime()函數 dispatch_source_set_event_handler(timer, block); dispatch_resume(timer); } return timer; } void MyCreateTimer() { // 每30秒執行一次,容差1秒,event handle中具體實現爲MyPeriodicTask() dispatch_source_t aTimer = CreateDispatchTimer(30ull * NSEC_PER_SEC, 1ull * NSEC_PER_SEC, dispatch_get_main_queue(), ^{ MyPeriodicTask(); }); // Store it somewhere for later use. if (aTimer){ MyStoreTimer(aTimer); } }
除了timer dispatch source按期處理系統事件,還有dispatch_after在指定時間以後執行一次某事件,dispatch_after就像指定了時間的dispatch_async函數。
二、Reading Data from a Descriptor
dispatch_source_t ProcessContentsOfFile(const char* filename) { // Prepare the file for reading. int fd = open(filename, O_RDONLY); if (fd == -1) return NULL; fcntl(fd, F_SETFL, O_NONBLOCK); // 避免阻塞讀數據進程 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_source_t readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, queue); if (!readSource){ close(fd); return NULL; } // Event Handler dispatch_source_set_event_handler(readSource, ^{
size_t estimated = dispatch_source_get_data(readSource) + 1; // 讀取數據至buffer char* buffer = (char*)malloc(estimated); if (buffer){ ssize_t actual = read(fd, buffer, (estimated)); Boolean done = MyProcessFileData(buffer, actual); // 處理數據 free(buffer); // 讀取完畢,取消該source。 if (done) dispatch_source_cancel(readSource); } }); // Cancellation Handler dispatch_source_set_cancel_handler(readSource, ^{close(fd);}); // 開始讀文件 dispatch_resume(readSource);
return readSource; }
三、Writing Data to a Descriptor
dispatch_source_t WriteDataToFile(const char* filename) { int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, (S_IRUSR | S_IWUSR | S_ISUID | S_ISGID)); if (fd == -1) return NULL; fcntl(fd, F_SETFL); // Block during the write. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_source_t writeSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, queue); if (!writeSource){ close(fd); return NULL; } dispatch_source_set_event_handler(writeSource, ^{ size_t bufferSize = MyGetDataSize(); void* buffer = malloc(bufferSize); size_t actual = MyGetData(buffer, bufferSize); write(fd, buffer, actual); free(buffer); // Cancel and release the dispatch source when done. dispatch_source_cancel(writeSource); }); dispatch_source_set_cancel_handler(writeSource, ^{close(fd);}); dispatch_resume(writeSource); return (writeSource); }
四、Monitoring a File-System Object
dispatch_source_t MonitorNameChangesToFile(const char* filename) { int fd = open(filename, O_EVTONLY); if (fd == -1) return NULL; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_RENAME, queue); if (source){ // Copy the filename for later use. int length = strlen(filename); char* newString = (char*)malloc(length + 1); newString = strcpy(newString, filename); dispatch_set_context(source, newString); // Install the event handler to process the name change dispatch_source_set_event_handler(source, ^{ const char* oldFilename = (char*)dispatch_get_context(source); MyUpdateFileName(oldFilename, fd); }); // Install a cancellation handler to free the descriptor // and the stored string. dispatch_source_set_cancel_handler(source, ^{ char* fileStr = (char*)dispatch_get_context(source); free(fileStr); close(fd); }); // Start processing events. dispatch_resume(source); } else close(fd); return source; }
五、Monitoring Signals
void InstallSignalHandler() { // Make sure the signal does not terminate the application. signal(SIGHUP, SIG_IGN); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGHUP, 0, queue); if (source){ dispatch_source_set_event_handler(source, ^{ MyProcessSIGHUP(); }); // Start processing signals dispatch_resume(source); } }
六、Monitoring a Process
void MonitorParentProcess() { pid_t parentPID = getppid(); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, parentPID, DISPATCH_PROC_EXIT, queue); if (source){ dispatch_source_set_event_handler(source, ^{ MySetAppExitFlag(); dispatch_source_cancel(source); dispatch_release(source); }); dispatch_resume(source); } }
4、取消Dispatch Source
void RemoveDispatchSource(dispatch_source_t mySource) { dispatch_source_cancel(mySource); dispatch_release(mySource); }
5、暫停和恢復Dispatch Source