一、定义:
在 Objective-C 中,一个 block 是一种可以捕获周围上下文变量的匿名函数。当你想要定义一个只在当前作用域内使用的 block 时,你可以创建一个局部 block。以下是一个例子,展示如何用 Objective-C 写一个局部 block:
#import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {@autoreleasepool {// 定义一个局部 blockvoid (^myLocalBlock)(NSString *) = ^(NSString *name) {NSLog(@"Hello, %@", name);};// 调用这个局部 blockmyLocalBlock(@"World");}return 0;
}
在这个例子中,`myLocalBlock` 是一个局部 block,它接收一个 `NSString` 类型的参数,并输出一条欢迎信息。这个 block 被定义在 main 函数的局部作用域内,只能在这个作用域内被调用。
Block 也可以捕获外部变量。如果你想在 block 内部访问外部定义的变量,你需要在 block 外部定义这些变量,并确保它们有正确的存储类型。例如:
#import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {@autoreleasepool {NSString *greeting = @"Hello";// 定义一个局部 blockvoid (^myLocalBlock)(NSString *) = ^(NSString *name) {NSLog(@"%@, %@", greeting, name);};// 调用这个局部 blockmyLocalBlock(@"World");}return 0;
}
在这个例子中,`greeting` 是一个局部变量,它在 block 外部定义,并在 block 内部被捕获和使用。注意,block 默认是捕获变量的值,而不是引用,除非你使用 `__block` 存储类型修饰符。如果你打算在 block 内部修改外部变量,你应该使用 `__block`,如下所示:
#import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {@autoreleasepool {__block NSInteger counter = 0;// 定义一个局部 blockvoid (^incrementCounterBlock)(void) = ^{counter += 1;NSLog(@"Counter: %ld", (long)counter);};// 调用这个局部 blockincrementCounterBlock(); // Counter: 1incrementCounterBlock(); // Counter: 2}return 0;
}
在这个例子中,`counter` 变量被 `__block` 修饰符修饰,这允许 block 在被调用时修改 `counter` 的值。
二、循环引用:
在 Objective-C 中,局部 block 本身不会产生循环引用,因为它们通常是栈上的局部变量,并且在声明它们的作用域结束时会被销毁。循环引用(或称为保留(retain)循环)产生的情况通常涉及 block 捕获了它外部的对象,特别是当这些对象又持有这个 block 的强引用时。
下面是一个不会产生循环引用的局部 block 的例子:
void someFunction() {int localVariable = 42;void (^myBlock)(void) = ^{NSLog(@"Local variable: %d", localVariable);};myBlock(); // 此处调用 block,不会产生循环引用
}
在上面的代码中,myBlock
是一个局部 block,它捕获了 localVariable
。当 someFunction
函数执行完毕后,myBlock
和 localVariable
都会从栈上移除,因此不存在循环引用。
循环引用通常发生在以下场景:
- 当一个对象持有一个 block 的强引用。
- 该 block 又捕获了这个对象,并持有其强引用。
例如:
@interface MyClass : NSObject
@property (nonatomic, copy) void (^myBlock)(void);
@end@implementation MyClass
- (void)configureBlock {__weak typeof(self) weakSelf = self; // 使用弱引用来打破循环引用self.myBlock = ^{__strong typeof(weakSelf) strongSelf = weakSelf; // 在 block 内部将弱引用升级为强引用以保证执行期间 self 不会被释放[strongSelf doSomething];};
}- (void)doSomething {// 做一些事情
}
@end
在上面的例子中,MyClass
的实例持有一个名为 myBlock
的 block。如果 myBlock
直接捕获 self
而没有使用弱引用 weakSelf
,那么当 MyClass
的实例持有这个 block 时,就会产生循环引用。因为 self
持有 myBlock
,而 myBlock
又捕获了 self
,这样它们就相互持有对方的强引用。
为了打破这个循环,可以使用 __weak
关键字创建一个 self
的弱引用 weakSelf
,然后在 block 内部使用这个弱引用。如果需要在 block 内部保证 self
在执行期间不会被释放,可以临时将 weakSelf
升级为强引用 strongSelf
,如上面的例子所示。这种方式既防止了循环引用,又确保了在 block 执行期间 self
是存在的。