NSRunloop的解析及autorelease的釋放時機

憶江南的部落格發表於2015-07-27

http://blog.csdn.net/xinruiios/article/details/8929915

iOS的執行時是由一個一個runloop組成的,每個runloop都會執行下圖所示的一些步驟:

每個runloop中都建立一個Autorelease Pool,並在runloop的末尾進行釋放,
所以,一般情況下,每個接受autorelease訊息的物件,都會在下個runloop開始前被釋放。也就是說,在一段同步的程式碼中執行過程中,生成的物件接受autorelease訊息後,一般是不會在程式碼段執行完成前釋放的。

當然也有讓autorelease提前生效的辦法:自己建立Pool並進行釋放

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

NSArray * array = [[[NSArray alloc] init] autorelease];

[pool drain];

上面的array就會在[pool drain]執行時被釋放。

所以對於你遇到的問題,可以在for迴圈外巢狀一個Autorelease Pool進行管理,例如

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

for (int i = 0; i < 10000; i++)

{

    // ... 

}

[pool drain];

但由於你提到了生成的每個例項可能會比較大。只在迴圈外巢狀,可能導致在pool釋放前,記憶體裡已經有10000個例項存在,造成瞬間佔用記憶體過大的情況。

因此,如果你的每個例項僅需要在單次迴圈過程中用到,那麼可以考慮可以在迴圈內建立pool並釋放

for (int i = 0; i < 10000; i++)

{

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // ...

    [pool drain];

}


對於多執行緒來說,每一個執行緒都有自己的runloop, 主執行緒是預設開啟的,建立的子執行緒要手動開啟,因為NSApplication只啟動main applicaiton thread

執行緒中沒有sourcerunloop會自動結束。 

事件由NSRunLoop 類處理。RunLoop監視作業系統的輸入源,如果沒有事件資料,不消耗任何CPU 資源。如果有事件資料,run loop 就傳送訊息,通知各個物件。

 currentRunLoop 獲得 runloop reference,給 runloop 傳送run 訊息啟動它。

 

下面介紹四種情況是使用runloop的場合:

 1.使用埠或自定義輸入源和其他執行緒通訊

 2.子執行緒中使用了定時器

 3.cocoa中使用任何performSelector到了執行緒中執行方法

 4.使執行緒履行週期性任務,(我把這個理解與2相同)

如果我們在子執行緒中用了NSURLConnection非同步請求,那也需要用到runloop,不然執行緒退出了,相應的delegate方法就不能觸發。


這裡通過小示例簡單介紹以下有關runloop方面的問題:

1.首先簡單執行執行runlooprun函式並不會讓系統停住等待事件,而是需要在執行runloop之前新增source,只有在有source的情況下執行緒才會停下來監聽各種事件。

2.runloop的使用:

1)生成一個runloop source

    // add send source 

    CFRunLoopSourceContext  src_context ;  

    NSError * emsg = nil ; 

 

    // init send source context 

    src_context.version = 0;

    src_context.info = inst;

    src_context.retain = NULL;

    src_context.release = NULL;

    src_context.copyDescription = NULL;

    src_context.equal = NULL;

    src_context.hash = NULL;

    src_context.schedule = NULL;

    src_context.cancel = NULL;

    src_context.perform = &callback ;//設定喚醒是呼叫的回撥函式

 

    // create send source from context 

 

   CFRunLoopSourceRef runloopSource ; 

    runloopSource = CFRunLoopSourceCreate (NULL, 0, &src_context) ;

2)將source加入執行緒所屬的runloop中

 

    // add the send source into  run loop

    CFRunLoopRef       threadRunLoop ;

    threadRunLoop =  CFRunLoopGetCurrent() ; 

    CFRunLoopAddSource (threadRunLoop ,

                        runloopSource,

                        kCFRunLoopDefaultMode);  

3)執行runloop

   CFRunLoopRun() ; 


4)如何呼叫runloop(首先可以將各個執行緒的runloop和source儲存起來)

 

   CFRunLoopSourceSignal(runloopSource) ;// 引數是你呼叫的runloop的source 

   CFRunLoopWakeUp(threadRunLoop) ;//這句話的作用時立即執行該runloop的事件,如果沒有這句話系統會在空閒的時候執行剛才的runloopSource相關的事件

3.如何停掉runloop退出執行緒

    CFRunLoopStop(threadRunLoop) ;這個函式可以停掉runloop是執行緒正常退出

4.ios整個系統基本上是基於runloop這種架構的,ios程式的main執行緒整體上也是基於runloop的,各種事件的響應應該也是基於source這種思路。


相關文章