performSelector方法
- performSelector在運行時,調用方去找目標方法selector,在編譯時不做校驗;
延遲執行 -- 與RunLoop有關
- 調用performSelector:withObject:afterDelay方法實現延遲執行,底層的本質是會創建NSTimer定時器去執行目標方法selector;
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:@selector(test) withObject:nil afterDelay:3];
}
- (void)test {
NSLog(@"%s",__func__);
NSLog(@"%@",[NSThread currentThread]);
}
@end
- 在主線程中,延遲3秒后執行test方法,可以執行成功;
- 若將performSelector:withObject:afterDelay方法 放在子線程中調用,如下:
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self performSelector:@selector(test) withObject:nil afterDelay:3];
});
}
- (void)test {
NSLog(@"%s",__func__);
NSLog(@"%@",[NSThread currentThread]);
}
@end
- 在子線程中調用performSelector:withObject:afterDelay方法 是不會執行test方法的,因為NSTimer定時器依賴于RunLoop才能執行,必須開啟子線程的RunLoop,做如下修改:
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self performSelector:@selector(test) withObject:nil afterDelay:3];
[[NSRunLoop currentRunLoop] run];
});
}
- (void)test {
NSLog(@"%s",__func__);
NSLog(@"%@",[NSThread currentThread]);
}
@end
開啟子線程執行任務 -- 與多線程有關
- performSelector: onThread:withObject: waitUntilDone: 可指定線程執行目標方法任務;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"11111");
[self performSelector:@selector(test) onThread:[NSThread currentThread] withObject:nil waitUntilDone:YES];
NSLog(@"22222");
});
}
- (void)test {
NSLog(@"%s",__func__);
NSLog(@"%@",[NSThread currentThread]);
}
@end
- 控制臺的調試結果如下:
image.png
- performSelector發送消息與消息的執行是處于
同一個線程
的; - waitUntilDone參數為Yes,表示test方法必須執行完成,才會執行之后的打印2222,即會阻塞當前線程的繼續執行;
performSelector:方法傳遞多參數的實現方案
- 第一種方案:將所有參數放到字典或者數組中,再傳遞集合即可;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary *params = @{
@"name":@"yanzi",
@"age":@"30"
};
[self performSelector:@selector(test:) withObject:params];
}
- (void)test:(NSDictionary *)params {
NSLog(@"%@--%@",params[@"name"],params[@"age"]);
}
@end
- 第二種方案:利用objc_msgSend()進行傳遞,其可以傳遞多個參數;
#import "ViewController.h"
#import <objc/message.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
((void (*)(id,SEL,NSString *, NSString *, NSString *))objc_msgSend)(self, @selector(testWithParam:param2:param3:),@"111",@"222",@"333");
}
//有三個參數的方法
- (void)testWithParam:(NSString *)param1 param2:(NSString *)param2 param3:(NSString *)param3 {
NSLog(@"param1:%@, param2:%@, param3:%@",param1, param2, param3);
}
@end
- 第三種方案:利用NSInvocation進行傳遞
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//調用方法
NSArray *paramArray = @[@"112",@[@2,@13],@12];
[self performSelector:@selector(testFunctionWithParam:param2:param3:) withObjects:paramArray];
}
//可以傳多個參數的方法
- (id)performSelector:(SEL)selector withObjects:(NSArray *)objects{
// 方法簽名(方法的描述)
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:selector];
if (signature == nil) {
//可以拋出異常也可以不操作。
}
//NSInvocation: 利用一個NSInvocation對象包裝一次方法調用(方法調用者、方法名、方法參數、方法返回值)
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self;
invocation.selector = selector;
//設置參數
NSInteger paramsCount = signature.numberOfArguments - 2; // 除self、_cmd以外的參數個數
paramsCount = MIN(paramsCount, objects.count);
for (NSInteger i = 0; i < paramsCount; i++) {
id object = objects[i];
if ([object isKindOfClass:[NSNull class]]) continue;
[invocation setArgument:&object atIndex:i + 2];
}
//調用方法
[invocation invoke];
//獲取返回值
id returnValue = nil;
if (signature.methodReturnLength) { // 有返回值類型,才去獲得返回值
[invocation getReturnValue:&returnValue];
}
return returnValue;
}
//要調用的方法
- (void)testFunctionWithParam:(NSString *)param1 param2:(NSArray *)param2 param3:(NSInteger)param3 {
NSLog(@"param1:%@, param2:%@, param3:%ld",param1, param2, param3);
}
@end