Objective-C中能否在将Block传入方法后再进行定义?
嘿,这个问题我之前在项目里踩过坑!你现在的写法之所以失效,核心原因是你传递的是Block变量的当前值(nil),而不是变量本身的引用——后续给变量赋值并不会改变之前已经传进去的那个nil值,方法里拿到的始终是最初的空指针。
问题根源拆解
你当前的代码执行顺序逻辑是:
- 声明Block变量
doStuffBlock,默认值为nil - 将这个
nil值传递给prepareToDoStuffWithCompletion:方法 - 后续给
doStuffBlock赋值,但此时方法内部保存的还是之前传入的nil,两者完全没有关联
下面给你两种可行的解决方案,你可以根据代码场景选择:
方案1:传递Block的指针(原生C指针思路)
修改方法的参数类型,让它接受一个指向Block的指针,这样后续对Block变量的赋值就能被方法内部感知到。
首先调整方法的声明:
- (void)prepareToDoStuffWithCompletion:(void(^__autoreleasing *)(void))completionPtr;
调用时传递Block变量的地址:
// 声明Block变量 void (^doStuffBlock)(void); // 传入Block的指针(注意&符号) [self prepareToDoStuffWithCompletion:&doStuffBlock]; // 后续定义Block doStuffBlock = ^void() { NSLog(@"执行Block内的操作"); };
在方法内部,需要通过指针调用Block(记得先判空避免崩溃):
- (void)prepareToDoStuffWithCompletion:(void(^__autoreleasing *)(void))completionPtr { // 模拟异步执行场景,比如延迟1秒调用Block dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ if (*completionPtr) { // 先判断指针指向的Block是否为空 (*completionPtr)(); // 通过指针调用Block } }); }
方案2:用容器类包裹Block(OC风格的间接方式)
如果不想修改原有方法的参数类型,可以用NSMutableDictionary或NSMutableArray这类容器包裹Block——因为容器是引用类型,传递的是内存地址,后续修改容器内的Block会被方法内部感知到。
示例代码:
// 用可变字典包裹Block,初始值用NSNull占位 NSMutableDictionary *blockWrapper = [@{@"completion": [NSNull null]} mutableCopy]; // 传入容器对象 [self prepareToDoStuffWithCompletionWrapper:blockWrapper]; // 后续给容器内的Block赋值 blockWrapper[@"completion"] = ^void() { NSLog(@"执行Block内的操作"); };
方法内部的实现:
- (void)prepareToDoStuffWithCompletionWrapper:(NSMutableDictionary *)wrapper { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ void(^completion)(void) = wrapper[@"completion"]; if (completion && ![completion isKindOfClass:[NSNull class]]) { completion(); } }); }
额外注意事项
- 如果是在异步场景下使用,要确保Block变量或容器对象不会被提前释放,必要时用
__strong修饰符持有对象,避免野指针崩溃 - 方案1更偏向底层C语言逻辑,执行效率更高;方案2更符合OC的编码习惯,代码可读性更好,你可以根据自己的项目需求选择
内容的提问来源于stack exchange,提问作者richy
相关产品推荐
相关产品推荐

