performSelector系列接口是runtime的一大特点,能够执行延迟,还能指定线程执行。本文从源码角度分析了其实现原理。objective-c
关于涉及到的数据结构,这里只是简单介绍一下。swift
SEL用于在runtime中表示一个方法名。与其对应的方法实现则使用IMP来表示。api
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
复制代码
Method selectors are used to represent the name of a method at runtime. A method selector is a C string that has been registered (or “mapped“) with the Objective-C runtime. Selectors generated by the compiler are automatically mapped by the runtime when the class is loaded.
You can add new selectors at runtime and retrieve existing selectors using the function sel_registerName.
When using selectors, you must use the value returned from sel_registerName or the Objective-C compiler directive @selector(). You cannot simply cast a C string to SEL.
复制代码
并未找到objc_selector的底层实现,咱们理解它为映射到一个方法名的字符串便可。编译器的@selector()与Runtime自身的sel_registerName均可以获取SEL类型的方法选择器。数组
struct objc_selector {
void *sel_id;
const char *sel_types;
};
复制代码
或者简单理解为char *, 映射到方法的C字符串。bash
struct objc_selector {
char name[64 or ...];
};
复制代码
IMP即为一个函数指针,指向函数的实现体。数据结构
A pointer to the start of a method implementation.
id (*IMP)(id, SEL, ...)
复制代码
This data type is a pointer to the start of the function that implements the method. This function uses standard C calling conventions as implemented for the current CPU architecture. The first argument is a pointer to self (that is, the memory for the particular instance of this class, or, for a class method, a pointer to the metaclass). The second argument is the method selector. The method arguments follow.app
Method即用于表示runtime的一个方法,其内部包含了selector和函数实现_imp,以及方法签名。less
typedef struct objc_method *Method;
An opaque type that represents a method in a class definition. 复制代码
struct _objc_method {
struct objc_selector * _cmd;
const char *method_type;
void *_imp;
};
复制代码
NSObject提供了performSelector的一系列接口。能够说单独的performSelector仅至关于NSInvocation或者objc_msgSend,动态执行传入的selector而已。因此,不少博客讲到performSelector必定与runloop有关,实际上是不许确的。函数
The performSelector: method is equivalent to sending an aSelector message directly to the receiver. The performSelector: method allows you to send messages that aren’t determined until run-time. This means that you can pass a variable selector as the argument.oop
该方法返回SEL对应的方法签名。
/**
* If we respond to the method directly, create and return a method
* signature. Otherwise raise an exception.
*/
- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector
{
struct objc_method *mth;
if (0 == aSelector)
{
return nil;
}
mth = GSGetMethod(object_getClass(self), aSelector, YES, YES);
if (mth != 0)
{
const char *types = method_getTypeEncoding(mth);
if (types != 0)
{
return [NSMethodSignature signatureWithObjCTypes: types];
}
}
[NSException raise: NSInvalidArgumentException format:
@"NSProxy should not implement 'methodSignatureForSelector:'"];
return nil;
}
复制代码
GSMethod即为Method,该GSGetMethod方法出自GNUStep中的runtime实现,调用了runtime的class_copyMethodList函数获取全部方法列表,而后查找到相应的Method。
typedef Method GSMethod;
GSMethod
GSGetMethod(Class cls, SEL sel,
BOOL searchInstanceMethods,
BOOL searchSuperClasses)
{
if (cls == 0 || sel == 0)
{
return 0;
}
if (searchSuperClasses == NO)
{
unsigned int count;
Method method = NULL;
Method *methods;
if (searchInstanceMethods == NO)
{
methods = class_copyMethodList(object_getClass(cls), &count);
}
else
{
methods = class_copyMethodList(cls, &count);
}
if (methods != NULL)
{
unsigned int index = 0;
while ((method = methods[index++]) != NULL)
{
if (sel_isEqual(sel, method_getName(method)))
{
break;
}
}
free(methods);
}
return method;
}
else
{
if (searchInstanceMethods == NO)
{
return class_getClassMethod(cls, sel);
}
else
{
return class_getInstanceMethod(cls, sel);
}
}
}
复制代码
根据SEL来查找到对应的函数实现IMP。
在swift/stdlib/public/runtime/SwiftObject.mm中的实现以下:
- (IMP)methodForSelector:(SEL)sel {
return class_getMethodImplementation(object_getClass(self), sel);
}
复制代码
直接调用runtime的函数class_getMethodImplementation便可。而在libs-base/Source/NSObject.m中的实现以下:
/**
* Returns a pointer to the C function implementing the method used
* to respond to messages with aSelector.
* <br />Raises NSInvalidArgumentException if given a null selector.
*/
- (IMP) methodForSelector: (SEL)aSelector
{
if (aSelector == 0)
[NSException raise: NSInvalidArgumentException
format: @"%@ null selector given", NSStringFromSelector(_cmd)];
/* The Apple runtime API would do:
* return class_getMethodImplementation(object_getClass(self), aSelector);
* but this cannot ask self for information about any method reached by
* forwarding, so the returned forwarding function would ge a generic one
* rather than one aware of hardware issues with returning structures
* and floating points. We therefore prefer the GNU API which is able to
* use forwarding callbacks to get better type information.
*/
return objc_msg_lookup(self, aSelector);
}
复制代码
这里采用了不一样的方式,缘由在注释中写的很明确了。class_getMethodImplementation函数没法获取到消息转发过来的全部方法的信息。
performSelector用于执行一个SEL。
/**
* Performs the specified selector. The selector must correspond to a method
* that takes no arguments.
*/
- (id) performSelector: (SEL)aSelector;
/**
* Performs the specified selector, with the object as the argument. This
* method does not perform any automatic unboxing, so the selector must
* correspond to a method that takes one object argument.
*/
- (id) performSelector: (SEL)aSelector
withObject: (id)anObject;
/**
* Performs the specified selector, with the objects as the arguments. This
* method does not perform any automatic unboxing, so the selector must
* correspond to a method that takes two object arguments.
*/
- (id) performSelector: (SEL)aSelector
withObject: (id)object1
withObject: (id)object2;
/**
* Returns YES if the object can respond to messages with the specified
* selector. The default implementation in NSObject returns YES if the
* receiver has a method corresponding to the method, but other classes may
* return YES if they can respond to a selector using one of the various
* forwarding mechanisms.
*/
- (BOOL) respondsToSelector: (SEL)aSelector;
复制代码
在swift/stdlib/public/runtime/SwiftObject.mm中, performSelector的底层实现以下:
- (id)performSelector:(SEL)aSelector {
return ((id(*)(id, SEL))objc_msgSend)(self, aSelector);
}
- (id)performSelector:(SEL)aSelector withObject:(id)object {
return ((id(*)(id, SEL, id))objc_msgSend)(self, aSelector, object);
}
- (id)performSelector:(SEL)aSelector withObject:(id)object1
withObject:(id)object2 {
return ((id(*)(id, SEL, id, id))objc_msgSend)(self, aSelector, object1,
object2);
}
复制代码
这不就是objc_msgSend么。。。而在libs-base/Source/NSObject.m中的实现以下:
/**
* Causes the receiver to execute the method implementation corresponding
* to aSelector and returns the result.<br />
* The method must be one which takes two arguments and returns an object.
* <br />Raises NSInvalidArgumentException if given a null selector.
*/
- (id) performSelector: (SEL)aSelector
withObject: (id) object1
withObject: (id) object2
{
IMP msg;
if (aSelector == 0)
[NSException raise: NSInvalidArgumentException
format: @"%@ null selector given", NSStringFromSelector(_cmd)];
/* The Apple runtime API would do:
* msg = class_getMethodImplementation(object_getClass(self), aSelector);
* but this cannot ask self for information about any method reached by
* forwarding, so the returned forwarding function would ge a generic one
* rather than one aware of hardware issues with returning structures
* and floating points. We therefore prefer the GNU API which is able to
* use forwarding callbacks to get better type information.
*/
msg = objc_msg_lookup(self, aSelector);
if (!msg)
{
[NSException raise: NSGenericException
format: @"invalid selector '%s' passed to %s",
sel_getName(aSelector), sel_getName(_cmd)];
return nil;
}
return (*msg)(self, aSelector, object1, object2);
}
复制代码
这里,就经过runtime的objc_msg_lookup函数查找到SEL对应的IMP实现,而后进行调用。原理与上边的同样。
因此,单纯的performSelector仅仅是提供了动态执行方法的能力,与NSInvocation同样。
NSProxy中关于performSelector的接口,基本与NSObject一致。
集合也能够批量对其中的对象使用performSelector。如NSOperationQueue中cancelAllOperations的操做:
- (void) cancelAllOperations {
[[self operations] makeObjectsPerformSelector: @selector(cancel)];
}
复制代码
又如其余一些第三方库中的用法,好比ReactiveCoacoa中:
[orderedGroups makeObjectsPerformSelector:@selector(sendError:) withObject:error];
[orderedGroups makeObjectsPerformSelector:@selector(sendCompleted)];
复制代码
其实现原理很简单,就是批量调用performSelector操做而已
/**
* Makes each object in the array perform aSelector.<br />
* This is done sequentially from the first to the last object.
*/
- (void) makeObjectsPerformSelector: (SEL)aSelector
{
NSUInteger c = [self count];
if (c > 0)
{
IMP get = [self methodForSelector: oaiSel];
NSUInteger i = 0;
while (i < c)
{
[(*get)(self, oaiSel, i++) performSelector: aSelector];
}
}
}
复制代码
涉及到延迟的performSelector,无疑是与runloop密切相关的。
延迟执行的方法都在NSObject的一个category (TimedPerformers)中。
/**
* Sets given message to be sent to this instance after given delay,
* in any run loop mode. See [NSRunLoop].
*/
- (void) performSelector: (SEL)aSelector
withObject: (id)argument
afterDelay: (NSTimeInterval)seconds
{
NSRunLoop *loop = [NSRunLoop currentRunLoop];
GSTimedPerformer *item;
item = [[GSTimedPerformer alloc] initWithSelector: aSelector
target: self
argument: argument
delay: seconds];
[[loop _timedPerformers] addObject: item];
RELEASE(item);
[loop addTimer: item->timer forMode: NSDefaultRunLoopMode];
}
复制代码
这里,采用一个GSTimedPerformer对象,将selector、target、argument、delay进行封装。NSRunLoop采用_timedPerformers来保存这些GSTimedPerformer对象,以后将GSTimedPerformer对象的timer加到runloop的DefaultMode中。若是指定了modes,则加到对应的modes中。
因此,performSelector的afterDelay特性是经过runloop+timer来实现的。
而cancel selector的相关方法都是从NSRunLoop对象的_timedPerformers数组中找到对应的GSTimedPerformer,执行其invalidate便可。源码以下:
/**
* Cancels any perform operations set up for the specified target
* in the current run loop.
*/
+ (void) cancelPreviousPerformRequestsWithTarget: (id)target
{
NSMutableArray *perf = [[NSRunLoop currentRunLoop] _timedPerformers];
unsigned count = [perf count];
if (count > 0)
{
GSTimedPerformer *array[count];
IF_NO_GC(RETAIN(target));
[perf getObjects: array];
while (count-- > 0)
{
GSTimedPerformer *p = array[count];
if (p->target == target)
{
[p invalidate];
[perf removeObjectAtIndex: count];
}
}
RELEASE(target);
}
}
/**
* Cancels any perform operations set up for the specified target
* in the current loop, but only if the value of aSelector and argument
* with which the performs were set up match those supplied.<br />
* Matching of the argument may be either by pointer equality or by
* use of the [NSObject-isEqual:] method.
*/
+ (void) cancelPreviousPerformRequestsWithTarget: (id)target
selector: (SEL)aSelector
object: (id)arg
{
NSMutableArray *perf = [[NSRunLoop currentRunLoop] _timedPerformers];
unsigned count = [perf count];
if (count > 0)
{
GSTimedPerformer *array[count];
IF_NO_GC(RETAIN(target));
IF_NO_GC(RETAIN(arg));
[perf getObjects: array];
while (count-- > 0)
{
GSTimedPerformer *p = array[count];
if (p->target == target && sel_isEqual(p->selector, aSelector)
&& (p->argument == arg || [p->argument isEqual: arg]))
{
[p invalidate];
[perf removeObjectAtIndex: count];
}
}
RELEASE(arg);
RELEASE(target);
}
}
复制代码
GSTimedPerformer很简单,即包含了一个selector执行所必须的信息,以及一个NSTimer。
/*
* The GSTimedPerformer class is used to hold information about
* messages which are due to be sent to objects at a particular time.
*/
@interface GSTimedPerformer: NSObject
{
@public
SEL selector;
id target;
id argument;
NSTimer *timer;
}
- (void) fire;
- (id) initWithSelector: (SEL)aSelector
target: (id)target
argument: (id)argument
delay: (NSTimeInterval)delay;
- (void) invalidate;
@end
复制代码
实现以下:
@implementation GSTimedPerformer
- (void) dealloc
{
[self finalize];
TEST_RELEASE(timer);
RELEASE(target);
RELEASE(argument);
[super dealloc];
}
- (void) fire
{
DESTROY(timer);
[target performSelector: selector withObject: argument];
[[[NSRunLoop currentRunLoop] _timedPerformers]
removeObjectIdenticalTo: self];
}
- (void) finalize
{
[self invalidate];
}
- (id) initWithSelector: (SEL)aSelector
target: (id)aTarget
argument: (id)anArgument
delay: (NSTimeInterval)delay
{
self = [super init];
if (self != nil)
{
selector = aSelector;
target = RETAIN(aTarget);
argument = RETAIN(anArgument);
timer = [[NSTimer allocWithZone: NSDefaultMallocZone()]
initWithFireDate: nil
interval: delay
target: self
selector: @selector(fire)
userInfo: nil
repeats: NO];
}
return self;
}
- (void) invalidate
{
if (timer != nil)
{
[timer invalidate];
DESTROY(timer);
}
}
@end
复制代码
这彻底就是NSTimer的使用场景,没啥可说的。将timer加到runloo中,而后timer的时机到了,执行 [target performSelector: selector withObject: argument]; 便可。
上边讲了NSObject的一个category。而runloop自身也有提供performSelector的相关接口。
@interface NSRunLoop(OPENSTEP)
- (void) addPort: (NSPort*)port
forMode: (NSString*)mode;
- (void) cancelPerformSelectorsWithTarget: (id)target;
- (void) cancelPerformSelector: (SEL)aSelector
target: (id)target
argument: (id)argument;
- (void) configureAsServer;
- (void) performSelector: (SEL)aSelector
target: (id)target
argument: (id)argument
order: (NSUInteger)order
modes: (NSArray*)modes;
- (void) removePort: (NSPort*)port
forMode: (NSString*)mode;
@end
复制代码
而其实现相对复杂了一些:
/**
* Sets up sending of aSelector to target with argument.<br />
* The selector is sent before the next runloop iteration (unless
* cancelled before then) in any of the specified modes.<br />
* The target and argument objects are retained.<br />
* The order value is used to determine the order in which messages
* are sent if multiple messages have been set up. Messages with a lower
* order value are sent first.<br />
* If the modes array is empty, this method has no effect.
*/
- (void) performSelector: (SEL)aSelector
target: (id)target
argument: (id)argument
order: (NSUInteger)order
modes: (NSArray*)modes
{
unsigned count = [modes count];
if (count > 0)
{
NSString *array[count];
GSRunLoopPerformer *item;
item = [[GSRunLoopPerformer alloc] initWithSelector: aSelector
target: target
argument: argument
order: order];
if ([modes isProxy])
{
unsigned i;
for (i = 0; i < count; i++)
{
array[i] = [modes objectAtIndex: i];
}
}
else
{
[modes getObjects: array];
}
while (count-- > 0)
{
NSString *mode = array[count];
unsigned end;
unsigned i;
GSRunLoopCtxt *context;
GSIArray performers;
context = NSMapGet(_contextMap, mode);
if (context == nil)
{
context = [[GSRunLoopCtxt alloc] initWithMode: mode
extra: _extra];
NSMapInsert(_contextMap, context->mode, context);
RELEASE(context);
}
performers = context->performers;
end = GSIArrayCount(performers);
for (i = 0; i < end; i++)
{
GSRunLoopPerformer *p;
p = GSIArrayItemAtIndex(performers, i).obj;
if (p->order > order)
{
GSIArrayInsertItem(performers, (GSIArrayItem)((id)item), i);
break;
}
}
if (i == end)
{
GSIArrayInsertItem(performers, (GSIArrayItem)((id)item), i);
}
i = GSIArrayCount(performers);
if (i % 1000 == 0 && i > context->maxPerformers)
{
context->maxPerformers = i;
if (sel_isEqual(aSelector, @selector(fire)))
{
NSLog(@"WARNING ... there are %u performers scheduled"
@" in mode %@ of %@\n(Latest: fires %@)",
i, mode, self, target);
}
else
{
NSLog(@"WARNING ... there are %u performers scheduled"
@" in mode %@ of %@\n(Latest: [%@ %@])",
i, mode, self, NSStringFromClass([target class]),
NSStringFromSelector(aSelector));
}
}
}
RELEASE(item);
}
}
复制代码
该方法能够传入一个order,即为执行的优先级。注意这段代码,根据order的大小,将item(即GSRunLoopPerformer对象)。order值越小,则SEL的优先级越高。
p = GSIArrayItemAtIndex(performers, i).obj;
if (p->order > order)
{
GSIArrayInsertItem(performers, (GSIArrayItem)((id)item), i);
break;
}
复制代码
GSRunLoopPerformer的用法基本与以前的GSTimedPerformer相似。
/*
* The GSRunLoopPerformer class is used to hold information about
* messages which are due to be sent to objects once each runloop
* iteration has passed.
*/
@interface GSRunLoopPerformer: NSObject
{
@public
SEL selector;
id target;
id argument;
unsigned order;
}
- (void) fire;
- (id) initWithSelector: (SEL)aSelector
target: (id)target
argument: (id)argument
order: (NSUInteger)order;
@end
复制代码
@implementation GSRunLoopPerformer
- (void) dealloc
{
RELEASE(target);
RELEASE(argument);
[super dealloc];
}
- (void) fire
{
NS_DURING
{
[target performSelector: selector withObject: argument];
}
NS_HANDLER
{
NSLog(@"*** NSRunLoop ignoring exception '%@' (reason '%@') "
@"raised during performSelector... with target %s(%s) "
@"and selector '%s'",
[localException name], [localException reason],
GSClassNameFromObject(target),
GSObjCIsInstance(target) ? "instance" : "class",
sel_getName(selector));
}
NS_ENDHANDLER
}
- (id) initWithSelector: (SEL)aSelector
target: (id)aTarget
argument: (id)anArgument
order: (NSUInteger)theOrder
{
self = [super init];
if (self)
{
selector = aSelector;
target = RETAIN(aTarget);
argument = RETAIN(anArgument);
order = theOrder;
}
return self;
}
@end
复制代码
咱们看到最关键的一句是 [target performSelector: selector withObject: argument]; ,target是id类型,因此这又回到了NSObject的performSelector函数。
不过GSRunLoopPerformer中并无NSTimer,那么它是如何触发fire函数的呢?
GSRunLoopCtxt对象中有个数组performers,以上操做会将GSRunLoopPerformer对象放到该performers中。进一步追踪该performers数组。
在runloop的 runMode:beforeDate: 方法中:
- (BOOL) runMode: (NSString*)mode beforeDate: (NSDate*)date
{
......
[self _checkPerformers: context];
......
[self acceptInputForMode: mode beforeDate: d];
......
}
复制代码
_checkPerformers中即有触发GSRunLoopPerformer的fire函数的地方。
- (BOOL) _checkPerformers: (GSRunLoopCtxt*)context
{
......
GSRunLoopPerformer *array[count];
for (i = 0; i < count; i++)
{
array[i] = GSIArrayItemAtIndex(performers, i).obj;
}
performers->count = 0;
/* Finally, fire the requests and release them.
*/
for (i = 0; i < count; i++)
{
[array[i] fire];
RELEASE(array[i]);
IF_NO_GC([arp emptyPool];)
}
......
}
复制代码
因此,目前为止的流程以下:
再看指定线程的相关接口是如何实现的呢?NSObject (NSThreadPerformAdditions) 中。
/**
* <p>This method performs aSelector on the receiver, passing anObject as
* an argument, but does so in the specified thread. The receiver
* and anObject are both retained until the method is performed.
* </p>
* <p>The selector is performed when the runloop of aThread next
* runs in one of the modes specified in anArray.<br />
* Where this method has been called more than once before the runloop
* of the thread runs in the required mode, the order in which the
* operations in the thread is done is the same as that in which
* they were added using this method.
* </p>
* <p>If there are no modes in anArray,
* the method has no effect and simply returns immediately.
* </p>
* <p>The argument aFlag specifies whether the method should wait until
* the selector has been performed before returning.<br />
* <strong>NB.</strong> This method does <em>not</em> cause the runloop of
* aThread to be run ... so if the runloop is not executed by some
* code in aThread, the thread waiting for the perform to complete
* will block forever.
* </p>
* <p>As a special case, if aFlag == YES and the current thread is aThread,
* the modes array is ignored and the selector is performed immediately.
* This behavior is necessary to avoid the current thread being blocked by
* waiting for a perform which will never happen because the runloop is
* not executing.
* </p>
*/
- (void) performSelectorOnMainThread: (SEL)aSelector
withObject: (id)anObject
waitUntilDone: (BOOL)aFlag
modes: (NSArray*)anArray
{
/* It's possible that this method could be called before the NSThread * class is initialised, so we check and make sure it's initiailised
* if necessary.
*/
if (defaultThread == nil)
{
[NSThread currentThread];
}
[self performSelector: aSelector
onThread: defaultThread
withObject: anObject
waitUntilDone: aFlag
modes: anArray];
}
/**
* Invokes -performSelector:onThread:withObject:waitUntilDone:modes:
* using the supplied arguments and an array containing common modes.<br />
* These modes consist of NSRunLoopMode, NSConnectionreplyMode, and if
* in an application, the NSApplication modes.
*/
- (void) performSelectorOnMainThread: (SEL)aSelector
withObject: (id)anObject
waitUntilDone: (BOOL)aFlag
{
[self performSelectorOnMainThread: aSelector
withObject: anObject
waitUntilDone: aFlag
modes: commonModes()];
}
复制代码
/**
* <p>This method performs aSelector on the receiver, passing anObject as
* an argument, but does so in the specified thread. The receiver
* and anObject are both retained until the method is performed.
* </p>
* <p>The selector is performed when the runloop of aThread next
* runs in one of the modes specified in anArray.<br />
* Where this method has been called more than once before the runloop
* of the thread runs in the required mode, the order in which the
* operations in the thread is done is the same as that in which
* they were added using this method.
* </p>
* <p>If there are no modes in anArray,
* the method has no effect and simply returns immediately.
* </p>
* <p>The argument aFlag specifies whether the method should wait until
* the selector has been performed before returning.<br />
* <strong>NB.</strong> This method does <em>not</em> cause the runloop of
* aThread to be run ... so if the runloop is not executed by some
* code in aThread, the thread waiting for the perform to complete
* will block forever.
* </p>
* <p>As a special case, if aFlag == YES and the current thread is aThread,
* the modes array is ignored and the selector is performed immediately.
* This behavior is necessary to avoid the current thread being blocked by
* waiting for a perform which will never happen because the runloop is
* not executing.
* </p>
*/
- (void) performSelector: (SEL)aSelector
onThread: (NSThread*)aThread
withObject: (id)anObject
waitUntilDone: (BOOL)aFlag
modes: (NSArray*)anArray
{
GSRunLoopThreadInfo *info;
NSThread *t;
if ([anArray count] == 0) {
return;
}
t = GSCurrentThread();
if (aThread == nil) {
aThread = t;
}
info = GSRunLoopInfoForThread(aThread);
if (t == aThread) {
/* Perform in current thread.
*/
if (aFlag == YES || info->loop == nil) {
/* Wait until done or no run loop.
*/
[self performSelector: aSelector withObject: anObject];
} else {
/* Don't wait ... schedule operation in run loop.
*/
[info->loop performSelector: aSelector
target: self
argument: anObject
order: 0
modes: anArray];
}
} else {
GSPerformHolder *h;
NSConditionLock *l = nil;
if ([aThread isFinished] == YES) {
[NSException raise: NSInternalInconsistencyException
format: @"perform [%@-%@] attempted on finished thread (%@)",
NSStringFromClass([self class]),
NSStringFromSelector(aSelector),
aThread];
}
if (aFlag == YES) {
l = [[NSConditionLock alloc] init];
}
h = [GSPerformHolder newForReceiver: self
argument: anObject
selector: aSelector
modes: anArray
lock: l];
[info addPerformer: h];
if (l != nil) {
[l lockWhenCondition: 1];
[l unlock];
RELEASE(l);
if ([h isInvalidated] == NO) {
/* If we have an exception passed back from the remote thread,
* re-raise it.
*/
if (nil != h->exception) {
NSException *e = AUTORELEASE(RETAIN(h->exception));
RELEASE(h);
[e raise];
}
}
}
RELEASE(h);
}
}
/**
* Invokes -performSelector:onThread:withObject:waitUntilDone:modes:
* using the supplied arguments and an array containing common modes.<br />
* These modes consist of NSRunLoopMode, NSConnectionreplyMode, and if
* in an application, the NSApplication modes.
*/
- (void) performSelector: (SEL)aSelector
onThread: (NSThread*)aThread
withObject: (id)anObject
waitUntilDone: (BOOL)aFlag
{
[self performSelector: aSelector
onThread: aThread
withObject: anObject
waitUntilDone: aFlag
modes: commonModes()];
}
/**
* Creates and runs a new background thread sending aSelector to the receiver
* and passing anObject (which may be nil) as the argument.
*/
- (void) performSelectorInBackground: (SEL)aSelector
withObject: (id)anObject
{
[NSThread detachNewThreadSelector: aSelector
toTarget: self
withObject: anObject];
}
复制代码
若是就在当前线程,则:
若是不在当前线程,初始化一个GSPerformHolder对象,调用GSRunLoopThreadInfo对象的addPerformer:方法。
h = [GSPerformHolder newForReceiver: self
argument: anObject
selector: aSelector
modes: anArray
lock: l];
复制代码
/**
* This class performs a dual function ...
* <p>
* As a class, it is responsible for handling incoming events from
* the main runloop on a special inputFd. This consumes any bytes
* written to wake the main runloop.<br />
* During initialisation, the default runloop is set up to watch
* for data arriving on inputFd.
* </p>
* <p>
* As instances, each instance retains perform receiver and argument
* values as long as they are needed, and handles locking to support
* methods which want to block until an action has been performed.
* </p>
* <p>
* The initialize method of this class is called before any new threads
* run.
* </p>
*/
@interface GSPerformHolder : NSObject
{
id receiver;
id argument;
SEL selector;
NSConditionLock *lock; // Not retained.
NSArray *modes;
BOOL invalidated;
@public
NSException *exception;
}
+ (GSPerformHolder*) newForReceiver: (id)r
argument: (id)a
selector: (SEL)s
modes: (NSArray*)m
lock: (NSConditionLock*)l;
- (void) fire;
- (void) invalidate;
- (BOOL) isInvalidated;
- (NSArray*) modes;
@end
复制代码
那么,selector究竟是如何执行的么?看来必定是fire函数。
- (void) fire
{
GSRunLoopThreadInfo *threadInfo;
if (receiver == nil){
return; // Already fired!
}
threadInfo = GSRunLoopInfoForThread(GSCurrentThread());
[threadInfo->loop cancelPerformSelectorsWithTarget: self];
NS_DURING
{
[receiver performSelector: selector withObject: argument];
}
NS_HANDLER
{
ASSIGN(exception, localException);
if (nil == lock)
{
NSLog(@"*** NSRunLoop ignoring exception '%@' (reason '%@') "
@"raised during perform in other thread... with receiver %p (%s) "
@"and selector '%s'",
[localException name], [localException reason], receiver,
class_getName(object_getClass(receiver)),
sel_getName(selector));
}
}
NS_ENDHANDLER
DESTROY(receiver);
DESTROY(argument);
DESTROY(modes);
if (lock != nil)
{
NSConditionLock *l = lock;
[lock lock];
lock = nil;
[l unlockWithCondition: 1];
}
}
复制代码
fire函数中,先是调用了runloop的cancelPerformSelectorsWithTarget函数。而后就是直接调用 [receiver performSelector: selector withObject: argument]; ,即最终都是objc_msgSend。
将执行操做包装起来的,实际上就是一个try-catch操做。
# define NS_DURING @try {
# define NS_HANDLER } @catch (NSException * localException) {
# define NS_ENDHANDLER }
复制代码
因此,最终关键点在于,fire函数的触发时机是在哪里?来看一看GSRunLoopThreadInfo。
GSRunLoopThreadInfo的说明已经很是直观了,用于从当前线程向另外一个线程中执行events,其内部loop实例变量即指向当前线程的runloop。
/* Used to handle events performed in one thread from another.
*/
@interface GSRunLoopThreadInfo : NSObject
{
@public
NSRunLoop *loop;
NSLock *lock;
NSMutableArray *performers;
#ifdef _WIN32
HANDLE event;
#else
int inputFd;
int outputFd;
#endif
}
/* Add a performer to be run in the loop's thread. May be called from
* any thread.
*/
- (void) addPerformer: (id)performer;
/* Fire all pending performers in the current thread. May only be called
* from the runloop when the event/descriptor is triggered.
*/
- (void) fire;
/* Cancel all pending performers.
*/
- (void) invalidate;
@end
/* Return (and optionally create) GSRunLoopThreadInfo for the specified
* thread (or the current thread if aThread is nil).<br />
* If aThread is nil and no value is set for the current thread, create
* a GSRunLoopThreadInfo and set it for the current thread.
*/
GSRunLoopThreadInfo *
GSRunLoopInfoForThread(NSThread *aThread) GS_ATTRIB_PRIVATE;
复制代码
一般使用 *GSRunLoopThreadInfo info = GSRunLoopInfoForThread(aThread); 来从线程中获取runloop相关信息。
GSRunLoopInfoForThread函数以下:
GSRunLoopThreadInfo *
GSRunLoopInfoForThread(NSThread *aThread)
{
GSRunLoopThreadInfo *info;
if (aThread == nil) {
aThread = GSCurrentThread();
}
if (aThread->_runLoopInfo == nil) {
[gnustep_global_lock lock];
if (aThread->_runLoopInfo == nil) {
aThread->_runLoopInfo = [GSRunLoopThreadInfo new];
}
[gnustep_global_lock unlock];
}
info = aThread->_runLoopInfo;
return info;
}
复制代码
能够看出,NSThread中的_runLoopInfo实例变量即存储了runloop的信息。
GSRunLoopThreadInfo对象的performers数组用于保存GSPerformHolder对象,以上代码即调用了addPerformer方法添加performer对象。addPerformer方法中会对signalled变量进行判断。
if (YES == signalled)
{
[performers addObject: performer];
}
[lock unlock];
if (NO == signalled)
{
/* We failed to add the performer ... so we must invalidate it in
* case there is code waiting for it to complete.
*/
[performer invalidate];
}
复制代码
GSRunLoopThreadInfo对象的fire函数会触发执行全部的performer。
/* Fire all pending performers in the current thread. May only be called
* from the runloop when the event/descriptor is triggered.
*/
- (void) fire;
复制代码
该函数会遍历全部的GSPerformHolder对象,依次调用runloop对象的performSelector:方法。target即为GSPerformHolder对象。
for (i = 0; i < c; i++) {
GSPerformHolder *h = [toDo objectAtIndex: i];
[loop performSelector: @selector(fire)
target: h
argument: nil
order: 0
modes: [h modes]];
}
复制代码
而GSRunLoopThreadInfo的fire函数的触发时机在GSRunLoopCtxt的awakenedBefore函数中:首先获取到GSRunLoopThreadInfo对象,而后判断poll操做,符合条件则触发其fire函数。
+ (BOOL) awakenedBefore: (NSDate*)when
{
GSRunLoopThreadInfo *threadInfo = GSRunLoopInfoForThread(nil);
NSTimeInterval ti = (when == nil) ? 0.0 : [when timeIntervalSinceNow];
int milliseconds = (ti <= 0.0) ? 0 : (int)(ti*1000);
struct pollfd pollfds;
/* Watch for signals from other threads.
*/
pollfds.fd = threadInfo->inputFd;
pollfds.events = POLLIN;
pollfds.revents = 0;
if (poll(&pollfds, 1, milliseconds) == 1)
{
NSDebugMLLog(@"NSRunLoop", @"Fire perform on thread");
[threadInfo fire];
return YES;
}
return NO;
}
复制代码
因此,流程以下:
另外,在acceptInputForMode:beforeDate:中,也有线索。
- (void) acceptInputForMode: (NSString*)mode
beforeDate: (NSDate*)limit_date
{
......
done = [context pollUntil: timeout_ms within: _contextStack];
......
}
复制代码
/**
* Perform a poll for the specified runloop context.
* If the method has been called re-entrantly, the contexts stack
* will list all the contexts with polls in progress
* and this method must tell those outer contexts not to handle events
* which are handled by this context.
*/
- (BOOL) pollUntil: (int)milliseconds within: (NSArray*)contexts
{
GSRunLoopThreadInfo *threadInfo = GSRunLoopInfoForThread(nil);
......
if (fdIndex == threadInfo->inputFd)
{
NSDebugMLLog(@"NSRunLoop", @"Fire perform on thread");
[threadInfo fire];
watcher = nil;
}
......
}
复制代码
其中,会触发GSRunLoopThreadInfo对象的fire函数。
runloop中的block任务,就是经过CFRunLoopPerformBlock加进去的。
RunLoop.current.perform {
print("RunLoop.current.perform")
}
复制代码
public func perform(inModes modes: [RunLoop.Mode], block: @escaping () -> Void) {
CFRunLoopPerformBlock(getCFRunLoop(), (modes.map { $0._cfStringUniquingKnown })._cfObject, block)
}
public func perform(_ block: @escaping () -> Void) {
perform(inModes: [.default], block: block)
}
复制代码
performSelector方法仅仅至关于objc_msgSend而已,编译器认定其调用返回值是一个对象,而且不会对其进行引用计数管理。因此须要手动管理器引用计数。
若是是alloc、new、copy、mutableCopy,则方法调用会开辟一块内存空间,编译器依然不会对其retain和release,因此会存在内存泄漏。
SEL cop = @selector(copy);
NSString *a = @"aaa";
id se = [a performSelector:cop];
NSLog(@"se %@", se);
......
se = nil; // 手动置为nil释放内存,以避免存在内存泄漏
复制代码
因此,performSelector的执行代码会有一个存在内存泄漏风险的警告。若是肯定不会有泄漏,则添加#pragma消除警告便可。
SEL selector = @selector(onTestPerformSeletor);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self performSelector:selector];
#pragma clang diagnostic pop
复制代码