核心思想
NSThread 的初始化方法 - (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument; 中,object 参数就是我们传递参数的入口。

(图片来源网络,侵删)
target: 将要执行任务的对象(通常是self)。selector: 目标对象上要执行的方法(这个方法必须有且只有一个参数)。object: 传递给selector方法的参数,它必须是id类型,也就是任何 Objective-C 对象。
关键在于如何设计你的 selector 方法来接收这个 object 参数。
直接传递简单参数(最直接)
这是最简单直接的方式,适用于传递简单的、不可变的对象,如 NSString, NSNumber, NSArray, NSDictionary 等。
步骤:
- 定义一个方法:这个方法接收一个
id参数。 - 创建并启动线程:使用
initWithTarget:selector:object:将参数通过object传递。
代码示例:

(图片来源网络,侵删)
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1. 准备要传递的参数
NSString *message = @"Hello from a new thread!";
NSNumber *count = @100;
// 2. 创建线程,并传递 message 作为参数
// 注意:这里的 object 参数会作为 - (void)myTask:(id)param 方法的参数
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(myTask:) object:message];
[thread1 setName:@"MyFirstThread"];
[thread1 start];
// 3. 再创建一个线程,传递 count 作为参数
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(myTask:) object:count];
[thread2 setName:@"MySecondThread"];
[thread2 start];
}
// 4. 定义一个接收 id 类型参数的方法
- (void)myTask:(id)param {
// 获取当前线程信息
NSLog(@"%@ - Task started with param: %@", [NSThread currentThread], param);
// 模拟耗时操作
[NSThread sleepForTimeInterval:2.0];
NSLog(@"%@ - Task finished", [NSThread currentThread]);
}
@end
控制台输出:
2025-10-27 10:30:00.123 MyFirstThread[12345:67890] <NSThread: 0x600002d8c000>{number = 3, name = MyFirstThread} - Task started with param: Hello from a new thread!
2025-10-27 10:30:00.124 MySecondThread[12345:67891] <NSThread: 0x600002d8d000>{number = 4, name = MySecondThread} - Task started with param: 100
2025-10-27 10:30:02.125 MyFirstThread[12345:67890] <NSThread: 0x600002d8c000>{number = 3, name = MyFirstThread} - Task finished
2025-10-27 10:30:02.125 MySecondThread[12345:67891] <NSThread: 0x600002d8d000>{number = 4, name = MySecondThread} - Task finished
优点:
- 简单、直观,易于理解。
缺点:
- 只能传递一个参数,如果你的任务需要多个参数,这种方法就不适用了。
- 参数必须是对象,不能直接传递基本数据类型(如
int,float),你需要将它们包装成NSNumber对象。
传递包含多个参数的字典(最常用)
当你需要传递多个参数时,创建一个 NSDictionary 来容纳所有参数是最佳实践。

(图片来源网络,侵删)
步骤:
- 创建一个字典:将所有需要传递的参数以
key-value的形式存入字典。 - 创建并启动线程:将整个字典作为
object参数传递。 - 在任务方法中:从字典中取出对应的参数。
代码示例:
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1. 创建一个参数字典
NSDictionary *params = @{
@"username": @"Alice",
@"age": @30,
@"taskID": @101
};
// 2. 创建线程,传递整个字典作为参数
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(processUserData:) object:params];
[thread start];
}
// 3. 定义一个接收字典参数的方法
- (void)processUserData:(NSDictionary *)params {
NSLog(@"%@ - Processing user data started.", [NSThread currentThread]);
// 4. 从字典中取出参数
NSString *username = params[@"username"];
NSInteger age = [params[@"age"] integerValue];
NSInteger taskID = [params[@"taskID"] integerValue];
NSLog(@"Username: %@, Age: %ld, TaskID: %ld", username, (long)age, (long)taskID);
// 模拟耗时操作
[NSThread sleepForTimeInterval:1.5];
NSLog(@"%@ - Processing user data finished.", [NSThread currentThread]);
}
@end
控制台输出:
2025-10-27 10:35:00.123 Thread[12345:67890] <NSThread: 0x600002d8c000>{number = 3, name = (null)} - Processing user data started.
2025-10-27 10:35:00.124 Thread[12345:67890] <NSThread: 0x600002d8c000>{number = 3, name = (null)} - Username: Alice, Age: 30, TaskID: 101
2025-10-27 10:35:01.624 Thread[12345:67890] <NSThread: 0x600002d8c000>{number = 3, name = (null)} - Processing user data finished.
优点:
- 可以轻松传递任意数量的参数。
- 代码结构清晰,参数有明确的
key,不易出错。
缺点:
- 需要手动管理
key,key拼写错误,会导致运行时错误(nil值)。
使用 Block(现代、灵活、推荐)
这是目前最推荐、最现代的方式,它将数据和代码封装在一起,避免了 target-action 模式的分离,代码更具可读性和可维护性。
步骤:
- 创建一个 Block,在 Block 内部定义你的任务逻辑和参数。
- 使用
performSelector:inThread:withObject:waitUntilDone:方法,将 Block 作为参数传递给新线程。
代码示例:
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1. 创建一个新的线程对象(但不立即启动)
NSThread *thread = [[NSThread alloc] initWithTarget:nil selector:nil object:nil];
[thread setName:@"MyBlockThread"];
// 2. 定义一个包含参数和逻辑的 Block
// 注意:Block 本身也是一个对象
void (^myTaskBlock)(void) = ^{
NSLog(@"%@ - Block task started.", [NSThread currentThread]);
// 在这里可以直接使用 Block 作用域内的变量
NSString *message = @"This is a block-based task.";
NSInteger iterations = 5;
for (int i = 0; i < iterations; i++) {
NSLog(@"%@ - Iteration %d", [NSThread currentThread], i);
[NSThread sleepForTimeInterval:0.5];
}
NSLog(@"%@ - Block task finished.", [NSThread currentThread]);
};
// 3. 启动线程,并将 Block 作为参数传递
// withObject: 可以传递任何对象,这里我们传递 Block
[thread performSelector:@selector(start) inThread:thread withObject:nil waitUntilDone:NO];
// 或者,如果你想在主线程上执行一个任务在新线程中:
[self performSelector:@selector(runTaskOnThread:) onThread:thread withObject:myTaskBlock waitUntilDone:NO];
}
// 这个方法只是用来接收 Block 并在新线程中执行它
- (void)runTaskOnThread:(void (^)(void))taskBlock {
if (taskBlock) {
taskBlock(); // 执行 Block
}
}
@end
控制台输出:
2025-10-27 10:40:00.123 MyBlockThread[12345:67890] <NSThread: 0x600002d8c000>{number = 3, name = MyBlockThread} - Block task started.
2025-10-27 10:40:00.123 MyBlockThread[12345:67890] <NSThread: 0x600002d8c000>{number = 3, name = MyBlockThread} - Iteration 0
2025-10-27 10:40:00.624 MyBlockThread[12345:67890] <NSThread: 0x600002d8c000>{number = 3, name = MyBlockThread} - Iteration 1
2025-10-27 10:40:01.125 MyBlockThread[12345:67890] <NSThread: 0x600002d8c000>{number = 3, name = MyBlockThread} - Iteration 2
2025-10-27 10:40:01.625 MyBlockThread[12345:67890] <NSThread: 0x600002d8c000>{number = 3, name = MyBlockThread} - Iteration 3
2025-10-27 10:40:02.126 MyBlockThread[12345:67890] <NSThread: 0x600002d8c000>{number = 3, name = MyBlockThread} - Iteration 4
2025-10-27 10:40:02.626 MyBlockThread[12345:67890] <NSThread: 0x600002d8c000>{number = 3, name = MyBlockThread} - Block task finished.
优点:
- 代码内聚:数据和操作逻辑在一起,非常清晰。
- 支持多个参数:无需包装,Block 可以直接访问其外部的局部变量。
- 现代、灵活:是苹果推荐的开发模式之一。
缺点:
- 相比
target-action,语法稍微复杂一点,需要理解 Block 的概念。
总结与最佳实践
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 直接传递简单参数 | 简单直接 | 只能传一个参数,必须是对象 | 传递单个、简单的数据 |
| 传递参数字典 | 可传递多个参数,结构清晰 | 需要管理 key,拼写错误易导致 bug | 需要传递多个不同类型参数的复杂任务 |
| 使用 Block | 现代、灵活、可读性高、支持多参数 | 语法稍复杂,需理解 Block | 强烈推荐,几乎所有需要传参的场景都适用 |
重要提醒:线程安全
在任何多线程环境中,当你在线程间共享数据(修改一个实例变量)时,都必须考虑线程安全问题,如果多个线程同时读写同一个变量,可能会导致数据错乱或程序崩溃。
解决线程安全问题的最简单方法是使用 @synchronized 块:
// 假设 self.sharedData 是一个实例变量
@synchronized(self) {
// 在这里执行对 sharedData 的读写操作
self.sharedData = @"New Value";
}
对于现代 iOS 开发,除非有特殊需求(如兼容旧代码或需要精细控制线程生命周期),否则 更推荐使用 GCD (Grand Central Dispatch),GCD 的语法更简洁,性能更好,并且内置了强大的线程安全机制(如 dispatch_barrier_async)。
用 GCD 实现上面的 Block 示例会非常简单:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 这里的代码就在一个后台线程中执行
// 可以直接访问外部变量
NSString *message = @"This is a GCD task.";
NSLog(@"%@ - %@", [NSThread currentThread], message);
});
