A CFRunLoopSource object is an abstraction of an input source that can be put into a run loop. Input sources typically generate asynchronous events, such as messages arriving on a network port or actions performed by the user.
An input source type normally defines an API for creating and operating on objects of the type, as if it were a separate entity from the run loop, then provides a function to create a CFRunLoopSource for an object. The run loop source can then be registered with the run loop and act as an intermediary between the run loop and the actual input source type object. Examples of input sources includeCFMachPort,CFMessagePort, and CFSocket.
There are two categories of sources. Version 0 sources, so named because theversion field of their context structure is 0, are managed manually by the application. When a source is ready to fire, some part of the application, perhaps code on a separate thread waiting for an event, must callCFRunLoopSourceSignal to tell the run loop that the source is ready to fire. The run loop source for CFSocket is currently implemented as a version 0 source.
Version 1 sources are managed by the run loop and kernel. These sources use Mach ports to signal when the sources are ready to fire. A source is automatically signaled by the kernel when a message arrives on the source’s Mach port. The contents of the message are given to the source to process when the source is fired. The run loop sources for CFMachPort and CFMessagePort are currently implemented as version 1 sources.
When creating your own custom run loop source, you can choose which version works best for you.
A run loop source can be registered in multiple run loops and run loop modes at the same time. When the source is signaled, whichever run loop that happens to detect the signal first will fire the source. Adding a source to multiple threads’ run loops can be used to manage a pool of “worker” threads that is processing discrete sets of data, such as client-server messages over a network or entries in a job queue filled by a “manager” thread. As messages arrive or jobs get added to the queue, the source gets signaled and a random thread receives and processes the request.
@interface RunLoopContext : NSObject
{
id target;
SEL selector;
CFRunLoopSourceContext context;
CFRunLoopSourceRef source;
CFRunLoopRef runLoop;
CFStringRef mode;
CFRunLoopTimerRef timer;
CFRunLoopTimerContext timerContext;
}
#pragma mark -
#pragma mark Property directives
@property (assign) id target;
@property (assign) SEL selector;
@property (readonly) CFRunLoopSourceContext context;
@property (readonly) CFRunLoopSourceRef source;
@property (readonly) CFRunLoopRef runLoop;
#pragma mark -
#pragma mark Custom initializers
-(id) initWithTarget: (id) aTarget selector: (SEL) aSelector;
#pragma mark -
#pragma mark Public methods
-(BOOL) isValid;
-(void) invalidate;
-(void) signal;
-(void) invoke;
-(void) scheduleInCFRunLoop: (CFRunLoopRef) aRunLoop forMode: (CFStringRef) mode;
-(void) remove;
@end
#pragma mark -
#pragma mark CFRunLoopSourceContext callbacks
void RunLoopSourceScheduleRoutine (void *info, CFRunLoopRef rl, CFStringRef mode);
void RunLoopSourcePerformRoutine (void *info);
void RunLoopSourceCancelRoutine (void *info, CFRunLoopRef rl, CFStringRef mode);
void RunLoopSourceTimerCallback (CFRunLoopTimerRef timer __unused, void *info);
RunLoopContext.m
#import "RunLoopContext.h"
@implementation RunLoopContext
#pragma mark -
#pragma mark Synthesize directives
@synthesize target;
@synthesize selector;
@synthesize context;
@synthesize source;
@synthesize runLoop;
#pragma mark -
#pragma mark Custom initializers
-(id) initWithTarget: (id) aTarget selector: (SEL) aSelector
{
if (self = [super init])
{
target = aTarget;
selector = aSelector;
}
return self;
}
#pragma mark -
#pragma mark Public methods
-(BOOL) isValid
{
return CFRunLoopSourceIsValid(source);
}
-(void) invalidate
{
CFRunLoopSourceInvalidate(source);
}
-(void) signal
{
//当手动调用此方法的时候,将会触发 RunLoopSourceContext的performCallback
CFRunLoopSourceSignal(source);
CFRunLoopWakeUp(runLoop);
}
-(void) invoke
{
// Perform the target selector.
[target performSelector: selector];
}
-(void) remove
{ //当手动调用此方法的时候,将会触发 RunLoopSourceContext的cancelCallback
CFRunLoopRemoveSource(runLoop, source, mode);
}
-(void) scheduleInCFRunLoop: (CFRunLoopRef) aRunLoop forMode: (CFStringRef) aMode
{
// Setup the context.
context.version = 0;
context.info = self;
context.retain = NULL;
context.release = NULL;
context.copyDescription = CFCopyDescription;
context.equal = CFEqual;
context.hash = CFHash;
context.schedule = RunLoopSourceScheduleRoutine;
context.cancel = RunLoopSourceCancelRoutine;
context.perform = RunLoopSourcePerformRoutine;
// Store the configured runloop and mode.
runLoop = aRunLoop;
mode = aMode;
// Create the CFRunLoopSourceRef.
source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
// Add the new CFRunLoopSourceRef to the indicated runloop.
CFRunLoopAddSource(runLoop, source, mode);
bzero(&timerContext, sizeof(timerContext));
timerContext.info = source;
timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), 1, 0, 0, RunLoopSourceTimerCallback, &timerContext);
CFRunLoopAddTimer(runLoop, timer, mode);
}
#pragma mark -
#pragma mark Overriden inherited methods
-(void) dealloc
{
// Invalidate.
[self invalidate];
// Set retained objects/values to nil.
target = nil;
selector = nil;
// Invoke the inherited dealloc method.
[super dealloc];
}
@end
#pragma mark -
#pragma mark CFRunLoopTimer callbacks
void RunLoopSourceTimerCallback (CFRunLoopTimerRef timer __unused, void *info){
NSLog(@"call RunLoopSourceTimerCallback function......");
CFRunLoopSourceSignal(info);
//这样CFTimer则不会重复调用, 否则CFRunLoopTimerRef会自动加入到runloop中继续下被调用
CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
}
#pragma mark -
#pragma mark CFRunLoopSourceContext callbacks
//当source添加进runloop的时候,调用此回调方法
void RunLoopSourceScheduleRoutine(void* info, CFRunLoopRef rl, CFStringRef mode)
{
NSLog(@"call RunLoopSourceScheduleRoutine function......");
// Cast the info pointer to a RunLoopContext instance.
// RunLoopContext* ctx = (RunLoopContext*) info;
// if (ctx)
// {
// [ctx invoke];
// }
}
//当sourcer接收到消息的时候,调用此回调方法
void RunLoopSourcePerformRoutine (void* info)
{
NSLog(@"call RunLoopSourcePerformRoutine function......");
// Cast the info pointer to a RunLoopContext instance.
RunLoopContext* ctx = (RunLoopContext*) info;
if (ctx)
{
[ctx invoke];
}
}
//当source 从runloop里删除的时候,调用此回调方法
void RunLoopSourceCancelRoutine (void* info, CFRunLoopRef rl, CFStringRef mode)
{
NSLog(@"call RunLoopSourceCancelRoutine function......");
}
调用
AppDelegate.m
#import "AppDelegate.h"
#import "MyObject.h"
#import "RunLoopContext.h"
@implementation AppDelegate
@synthesize window = _window;
- (void)dealloc
{
[super dealloc];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
//MyObject *object = [[MyObject alloc] init];
//[NSThread detachNewThreadSelector:@selector(testRunloop1) toTarget:object withObject:nil];
RunLoopContext *context = [[RunLoopContext alloc] initWithTarget:self selector:@selector(threadFunction)];
[context scheduleInCFRunLoop:CFRunLoopGetCurrent() forMode:kCFRunLoopDefaultMode];
//[context signal];
//[context remove];
NSLog(@"output thread ......");
}
-(void)threadFunction{
NSLog(@"run Thread Function in loop......");
}
可以通过CFRunLoopTimer去调用,也可以通过手动调用[context signal]去通知runloop处理事件