CFRunLoop CF 차원에서 CFRunLoopMode 메커니즘 iOS 프로그램 ScrollView의 미끄럼이 왜 이렇게 매끄러운지 알아보기
간단히 말하면run loop은 이벤트 구동의 큰 순환이다. 아래 코드와 같다.
int main(int argc, char * argv[]) {
//
while (AppIsRunning) {
// ,
id whoWakesMe = SleepForWakingUp();
//
id event = GetEvent(whoWakesMe);
//
HandleEvent(event);
}
return 0;
}
코코아는 Run Loops와 관련된
스택 맨 아래쪽은 start(dyld), 위쪽은main, UIAPplication(main.m)-> GSEventRunModal(Graphic Services)-> RunLoopRunSpecific, CFRunLoopRun, CFRunLoopDoSouces0, CFRUNLOOOOP IS CALLING OUT TO A SOURCE0 PERFORM FORM FUCTION - Handle Touch Tovent
RunLoop 원리
CFRunLoop 소스 코드:http://opensource.apple.com/source/CF/CF-855.17/
실행 순서의 위조 코드
SetupThisRunLoopRunTimeoutTimer(); // by GCD timer
do {
__CFRunLoopDoObservers(kCFRunLoopBeforeTimers);
__CFRunLoopDoObservers(kCFRunLoopBeforeSources);
__CFRunLoopDoBlocks();
__CFRunLoopDoSource0();
CheckIfExistMessagesInMainDispatchQueue(); // GCD
__CFRunLoopDoObservers(kCFRunLoopBeforeWaiting);
var wakeUpPort = SleepAndWaitForWakingUpPorts();
// mach_msg_trap
// Zzz...
// Received mach_msg, wake up
__CFRunLoopDoObservers(kCFRunLoopAfterWaiting);
// Handle msgs
if (wakeUpPort == timerPort) {
__CFRunLoopDoTimers();
} else if (wakeUpPort == mainDispatchQueuePort) {
// GCD
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__()
} else {
__CFRunLoopDoSource1();
}
__CFRunLoopDoBlocks();
} while (!stop && !timeout);
구성
Thread는 CFRunLoop을 포함하고 CFRunLoop은 CFRunLoopMode를 포함하며 모델은 CFRunLoopSource, CFRunLoopTimer와 CFRunLoopObserver를 포함한다.
CFRunLoopMode
RunLoop은 하나의 모드에서만 실행할 수 있습니다. 모드를 바꾸려면 현재 loop을 멈추고 다시 시작해야 합니다.이 메커니즘을 이용하면 ScrollView 과정에서 NSDefaultRunLoopMode의 모델은 UITrackingRunLoopMode로 전환하여 ScrollView의 원활한 슬라이딩이 NSDefaultRunLoopMode에서만 처리할 수 있는 이벤트의 영향을 받지 않도록 합니다.동시에 모델은 맞춤형으로 제작할 수 있습니다.
// timer NSDefaultRunLoopMode
[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(timerTick:)
userInfo:nil
repeats:YES];
// NSRunLoopCommonModes
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:@selector(timerTick:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
CFRunLoopTimer
NSTimer는 RunLoopTimer의 패키지입니다.
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;
- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;
CFRunLoopSource
코코아 프레임워크의 많은 메커니즘, 예를 들어 CAAnimation 등은 모두 RunLoopObserver가 촉발한 것이다.observer에서 현재 상태의 변화를 알립니다.
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),
kCFRunLoopBeforeTimers = (1UL << 1),
kCFRunLoopBeforeSources = (1UL << 2),
kCFRunLoopBeforeWaiting = (1UL << 5),
kCFRunLoopAfterWaiting = (1UL << 6),
kCFRunLoopExit = (1UL << 7),
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
RunLoop 사용 사례
AFNetworking
NSOperation+NSURLconnection 병행 모델을 사용하면 NSURLconnection 다운로드가 완료되기 전 라인이 종료되어 NSOperation 대상이 리셋을 받지 못하는 문제에 직면하게 된다.AFNetWorking에서 이 문제를 해결하는 방법은 공식적인 거예요.guidhttps://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html#//apple_ref/occ/instm/NSURLConnection/initWithRequest:delegate:startImmediately:NSURLconnection에 적힌 delegate 방법은connection에서 시작하는 루틴runloop에서 호출되어야 하기 때문에AFNetWorking은 애플의 D를 직접 참고했다emohttps://developer.apple.com/LIBRARY/IOS/samplecode/MVCNetworking/Introduction/Intro.html실현 방법은 단독으로 글로벌 thread를 만들고, 내장된runloop을 설치하며, 모든connection은 이runloop에서 시작합니다. 리셋도 주 라인을 차지하지 않고, CPU 자원을 소모하지 않습니다.
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[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;
}
이 방법으로 상주 서비스의 라인을 만들 수 있습니다.
TableView에서 그림을 부드럽게 스크롤 지연 불러오기
CFRunLoopMode의 특성을 이용하여 NSDefaultRunLoopMode의 모델에 그림을 불러올 수 있습니다. 이렇게 하면 UITrackingRunLoopMode를 굴릴 때 이 모델이 불러오지 않고 영향을 주지 않습니다.
UIImage *downloadedImage = ...;
[self.avatarImageView performSelector:@selector(setImage:)
withObject:downloadedImage
afterDelay:0
inModes:@[NSDefaultRunLoopMode]];
프로그램이 붕괴되었을 때의 신호를 받아 자율적으로 처리한다. 예를 들어 팝업 알림 등이다.
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
NSArray *allModes = CFBridgingRelease(CFRunLoopCopyAllModes(runLoop));
while (1) {
for (NSString *mode in allModes) {
CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
}
}
비동기 테스트
- (BOOL)runUntilBlock:(BOOL(^)())block timeout:(NSTimeInterval)timeout
{
__block Boolean fulfilled = NO;
void (^beforeWaiting) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) =
^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
fulfilled = block();
if (fulfilled) {
CFRunLoopStop(CFRunLoopGetCurrent());
}
};
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, 0, beforeWaiting);
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
// Run!
CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, false);
CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
CFRelease(observer);
return fulfilled;
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.