OC RunLoopアプリケーション例
18488 ワード
知識点:1、RunLoopの基礎知識2、RunLoopとNSTimer 3、RunLoopとPerform Selector 4、RunLoop、スレッド、AutoreleasePoolの3者連絡5、RunLoopとスレッド通信6、RunLoopの各種状態傍受7、RunLoopとNSNotificationQueue
####RunLoopとは?
NSRunLoopアップル公式ドキュメントCoreFoundationソース
RunLoop入門私を見れば十分です-簡書RunLoopはもう入門しましたか?応用しない?簡書
RunLoop|Garan no douを深く理解する
runloopベースのスレッドの保存、破棄、通信IOS--RunLoopのインスタンス化説明
RunLoopには5種類の運転モードがあり、そのうちよく見られるのは1.2種類である.
###RunLoopアプリケーション###1、NSTimerメインスレッドの下:
scheduledTimerWithTimeInterval
#####RunLoopのrunとstop 1、CFRunLoopStopはすべてCFRunloopでrunloopを実行することを直接停止することができます
方式1:run
方式2:runUntilDate
方式3:runMode:beforeDate:(実はCFRunLoopRunInModeを呼び出します)
あるイベントを一定時間リスニングしたり、30分以内にonTimerFiredを30 sおきに実行したりします.
#####2、performSelector: withObject: afterDelay: inModes
プライマリスレッド
サブスレッド
###3、常駐スレッドAFNetworking 2.0には、すべてのリクエストを専門に処理する常駐スレッドのコールバックイベントが作成されています.
####4、スレッド、RunLoop、AutoreleasePoolの3つの関係########スレッドとRunLoop CFRunLoopGetMain()とCFRunLoopGetCurrent()のソースコード
上のコードから分かるように,スレッドとRunLoopの間には一つ一つ対応しており,その関係はグローバルなDictionaryに保存されている.プログラムが作成されたばかりの頃はRunLoopはありませんでしたが、自分から取得しなければ、それはずっとありません.RunLoopの作成は初回取得時に発生し,RunLoopの破棄はスレッド終了時に発生する.1つのスレッドの内部でのみRunLoopを取得できます(メインスレッドを除きます).
#######AutoreleasePoolはRunLoop 1、1つのスレッドに対応するRunLoop(メインスレッドシステムが起動し、他のスレッドはロードして取得して起動する必要がある)2、スレッドがRunLoopを起動していない場合、autoreleaseオブジェクトに遭遇すると次のような状況になります.
3、スレッドがRunLoopを起動し、RunLoop内部でAutoreleasePoolを管理する
4、またRunLoopを起動する前に@autoreleasepool{...}ラップの意味:大きな解放プールを作成し、{}を解放する間に作成された一時オブジェクトを解放します.一般的に良いフレームワークの著者はそうします(上のAFNetworkingはそうします).
#####NSRunLoopとNSMachPortからのスレッド通信の例:
次のように印刷します.
######カスタム入力ソースでスレッド通信を実現
出力結果は次のとおりです.
#####CFRunLoopObserverRefは観察者であり、RunLoopの状態変化を傍受できる
CFRunLoopObserverRef使用demo:メインスレッドRunLoopの空き時間を利用して処理し、UIのことをいくつかして、カートンを減らします(これでマルチスレッドは必要ありません)
NSNotificationQueueもrunloopと関係があるOC--NSNotificationCenter再認識
####RunLoopとは?
NSRunLoopアップル公式ドキュメントCoreFoundationソース
RunLoop入門私を見れば十分です-簡書RunLoopはもう入門しましたか?応用しない?簡書
RunLoop|Garan no douを深く理解する
runloopベースのスレッドの保存、破棄、通信IOS--RunLoopのインスタンス化説明
RunLoopには5種類の運転モードがあり、そのうちよく見られるのは1.2種類である.
1. kCFRunLoopDefaultMode:App Mode, Mode
2. UITrackingRunLoopMode: Mode, ScrollView , Mode
3. UIInitializationRunLoopMode: App Mode,
4. GSEventReceiveRunLoopMode: Mode,
5. kCFRunLoopCommonModes: Mode, kCFRunLoopDefaultMode UITrackingRunLoopMode , Mode
###RunLoopアプリケーション###1、NSTimerメインスレッドの下:
- (void)timer
{
NSTimer *aTimer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
// 1、 NSDefaultRunLoopMode , RunLoop ,
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
// 2、 UITrackingRunLoopMode ( UIScrollView), RunLoop ,
[[NSRunLoop mainRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
// 3、 NSRunLoopCommonModes :UITrackingRunLoopMode NSDefaultRunLoopMode
[[NSRunLoop mainRunLoop] addTimer:aTimer forMode:NSRunLoopCommonModes];
}
scheduledTimerWithTimeInterval
- (void)viewDidLoad {
[super viewDidLoad];
//
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// RunLoop:[[NSRunLoop currentRunLoop] addTimer: t forMode: NSDefaultRunLoopMode];
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timerFired:)userInfo:nil repeats:YES];
// currentRunLoop run
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
});
}
#####RunLoopのrunとstop 1、CFRunLoopStopはすべてCFRunloopでrunloopを実行することを直接停止することができます
- (void)testTimer
{
NSTimer *aTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(runrun) userInfo:nil repeats:YES];
//
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
// UI , UITrackingRunLoopMode , NSDefaultRunLoopMode
[currentRunLoop addTimer:aTimer forMode:NSDefaultRunLoopMode];
// run ,
// 1
// 2
// 3
}
方式1:run
/* 1:
NSDefaultRunLoopMode
runloop :
1、 CFRunLoopStop
2、(1) timer port;(2) ( )
*/
[currentRunLoop run];
方式2:runUntilDate
/* 2:
NSDefaultRunLoopMode
runloop :
1、 CFRunLoopStop
2、(1) timer port;(2)
*/
[currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];//
方式3:runMode:beforeDate:(実はCFRunLoopRunInModeを呼び出します)
// returnAfterSourceHandled YES, timer ,runloop
CFRunLoopRunInMode (mode, seconds, returnAfterSourceHandled);returnAfterSourceHandled = YES ;
/* 3:
runMode、 ,
runloop :
(1) CFRunLoopStop;
(2) timer port;
(3)
*/
BOOL result = [currentRunLoop runMode:UITrackingRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:20]];
if (result) {
// PerfromSelector* Input Source ,Run Loop YES, NO。
}else {
// NO
}
}
あるイベントを一定時間リスニングしたり、30分以内にonTimerFiredを30 sおきに実行したりします.
@autoreleasepool {
NSTimer * udpateTimer = [NSTimer timerWithTimeInterval:30
target:self
selector:@selector(onTimerFired:)
userInfo:nil
repeats:YES];
NSRunLoop * runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:udpateTimer forMode:NSRunLoopCommonModes];
[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:60*30]];
}
#####2、performSelector: withObject: afterDelay: inModes
プライマリスレッド
// performSelector RunLoopMode
// performSelector NSTimer afterDelay
[self performSelector:@selector(performSelector) withObject:nil afterDelay:2 inModes:@[NSDefaultRunLoopMode]];
サブスレッド
BBThread *thread = [[BBThread alloc] initWithBlock:^{
[self performSelector:@selector(performSelector) withObject:nil afterDelay:2 inModes:@[NSDefaultRunLoopMode]];
// currentRunLoop
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
[currentRunLoop run];
}];
[thread start];
###3、常駐スレッドAFNetworking 2.0には、すべてのリクエストを専門に処理する常駐スレッドのコールバックイベントが作成されています.
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
// port, Thread
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread =
[[NSThread alloc] initWithTarget:self
selector:@selector(networkRequestThreadEntryPoint:)
object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
####4、スレッド、RunLoop、AutoreleasePoolの3つの関係########スレッドとRunLoop CFRunLoopGetMain()とCFRunLoopGetCurrent()のソースコード
/// Dictionary,key pthread_t, value CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
/// loopsDic
static CFSpinLock_t loopsLock;
/// pthread RunLoop。
CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
OSSpinLockLock(&loopsLock);
if (!loopsDic) {
// , Dic, RunLoop。
loopsDic = CFDictionaryCreateMutable();
CFRunLoopRef mainLoop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
}
/// Dictionary 。
CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));
if (!loop) {
/// ,
loop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, thread, loop);
/// , , RunLoop。
_CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
}
OSSpinLockUnLock(&loopsLock);
return loop;
}
CFRunLoopRef CFRunLoopGetMain() {
return _CFRunLoopGet(pthread_main_thread_np());
}
CFRunLoopRef CFRunLoopGetCurrent() {
return _CFRunLoopGet(pthread_self());
}
上のコードから分かるように,スレッドとRunLoopの間には一つ一つ対応しており,その関係はグローバルなDictionaryに保存されている.プログラムが作成されたばかりの頃はRunLoopはありませんでしたが、自分から取得しなければ、それはずっとありません.RunLoopの作成は初回取得時に発生し,RunLoopの破棄はスレッド終了時に発生する.1つのスレッドの内部でのみRunLoopを取得できます(メインスレッドを除きます).
#######AutoreleasePoolはRunLoop 1、1つのスレッドに対応するRunLoop(メインスレッドシステムが起動し、他のスレッドはロードして取得して起動する必要がある)2、スレッドがRunLoopを起動していない場合、autoreleaseオブジェクトに遭遇すると次のような状況になります.
1)、 autorelease , key poolpage;
2)、 page.add(obj)OK;
3)、 autoreleaseNoPage, page;
4)、 , page pop。
3、スレッドがRunLoopを起動し、RunLoop内部でAutoreleasePoolを管理する
1)、RunLoop objc_autoreleasePoolPush;
2)、RunLoop objc_autoreleasePoolPop objc_autoreleasePoolPush;
3)、RunLoop objc_autoreleasePoolPop;
4、またRunLoopを起動する前に@autoreleasepool{...}ラップの意味:大きな解放プールを作成し、{}を解放する間に作成された一時オブジェクトを解放します.一般的に良いフレームワークの著者はそうします(上のAFNetworkingはそうします).
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
// port, Thread
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
#####NSRunLoopとNSMachPortからのスレッド通信の例:
- (void)testDemo3
{
// , NSMachPort
NSMachPort *mainPort = [[NSMachPort alloc]init];
NSPort *threadPort = [NSMachPort port];
//
threadPort.delegate = self;
// runloop
[[NSRunLoop currentRunLoop]addPort:mainPort forMode:NSDefaultRunLoopMode];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// Port
[[NSRunLoop currentRunLoop]addPort:threadPort forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
});
NSString *s1 = @"hello";
NSData *data = [s1 dataUsingEncoding:NSUTF8StringEncoding];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSMutableArray *array = [NSMutableArray arrayWithArray:@[mainPort,data]];
// 2 threadPort , : 。msgid 。
//components, 。reserved: ( , ...)
[threadPort sendBeforeDate:[NSDate date] msgid:1000 components:array from:mainPort reserved:0];
});
}
// NSMachPort , , id。 NSPortMessage
- (void)handlePortMessage:(id)message
{
NSLog(@" , :%@",[NSThread currentThread]);
// KVC
NSArray *array = [message valueForKeyPath:@"components"];
NSData *data = array[1];
NSString *s1 = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",s1);
// NSMachPort *localPort = [message valueForKeyPath:@"localPort"];
// NSMachPort *remotePort = [message valueForKeyPath:@"remotePort"];
}
次のように印刷します.
2016-11-23 16:50:20.604 TestRunloop3[1322:120162] , :{number = 3, name = (null)}
2016-11-23 16:50:26.551 TestRunloop3[1322:120162] hello
######カスタム入力ソースでスレッド通信を実現
- (void)testDemo4
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"starting thread.......");
_runLoopRef = CFRunLoopGetCurrent();
// _source_context。
bzero(&_source_context, sizeof(_source_context));
// ,
_source_context.perform = fire;
//
_source_context.info = "hello";
// source
_source = CFRunLoopSourceCreate(NULL, 0, &_source_context);
// source RunLoop
CFRunLoopAddSource(_runLoopRef, _source, kCFRunLoopDefaultMode);
// runloop YES,
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 9999999, YES);
NSLog(@"end thread.......");
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (CFRunLoopIsWaiting(_runLoopRef)) {
NSLog(@"RunLoop ");
//
CFRunLoopSourceSignal(_source);
// , ,
CFRunLoopWakeUp(_runLoopRef);
}else {
NSLog(@"RunLoop ");
// , , ,
CFRunLoopSourceSignal(_source);
}
});
}
//
static void fire(void* info){
NSLog(@" ");
printf("%s",info);
}
出力結果は次のとおりです.
2016-11-24 10:42:24.045 TestRunloop3[4683:238183] starting thread.......
2016-11-24 10:42:26.045 TestRunloop3[4683:238082] RunLoop
2016-11-24 10:42:31.663 TestRunloop3[4683:238183]
hello
2016-11-24 10:42:31.663 TestRunloop3[4683:238183] end thread.......
#####CFRunLoopObserverRefは観察者であり、RunLoopの状態変化を傍受できる
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//
/*
CFAllocatorRef allocator: CFAllocatorGetDefault()
CFOptionFlags activities: kCFRunLoopAllActivities
Boolean repeats:YES: NO:
CFIndex order: , 0
: observer: activity:
*/
/*
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // RunLoop
kCFRunLoopBeforeTimers = (1UL << 1), // Timer
kCFRunLoopBeforeSources = (1UL << 2), // Source
kCFRunLoopBeforeWaiting = (1UL << 5), //
kCFRunLoopAfterWaiting = (1UL << 6),//
kCFRunLoopExit = (1UL << 7),// RunLoop
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"RunLoop ");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"RunLoop Timers ");
break;
case kCFRunLoopBeforeSources:
NSLog(@"RunLoop Sources ");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"RunLoop ");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"RunLoop ");
break;
case kCFRunLoopExit:
NSLog(@"RunLoop ");
break;
default:
break;
}
});
// RunLoop
/*
CFRunLoopRef rl: RunLoop, RunLoop
CFRunLoopObserverRef observer
CFStringRef mode RunLoop
*/
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
/*
CF (Core Foundation)
Create、Copy、Retain , , release
GCD iOS6.0 ,6.0 GCD ARC ,
*/
CFRelease(observer);
}
CFRunLoopObserverRef使用demo:メインスレッドRunLoopの空き時間を利用して処理し、UIのことをいくつかして、カートンを減らします(これでマルチスレッドは必要ありません)
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
if (activity == kCFRunLoopBeforeWaiting) {
// RunLoop
}
});
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);
CFRelease(observer);
NSNotificationQueueもrunloopと関係があるOC--NSNotificationCenter再認識