局部Block及面试点

一、定义:

在 Objective-C 中,一个 block 是一种可以捕获周围上下文变量的匿名函数。当你想要定义一个只在当前作用域内使用的 block 时,你可以创建一个局部 block。以下是一个例子,展示如何用 Objective-C 写一个局部 block:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 定义一个局部 block
        void (^myLocalBlock)(NSString *) = ^(NSString *name) {
            NSLog(@"Hello, %@", name);
        };

        // 调用这个局部 block
        myLocalBlock(@"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";
        
        // 定义一个局部 block
        void (^myLocalBlock)(NSString *) = ^(NSString *name) {
            NSLog(@"%@, %@", greeting, name);
        };

        // 调用这个局部 block
        myLocalBlock(@"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;

        // 定义一个局部 block
        void (^incrementCounterBlock)(void) = ^{
            counter += 1;
            NSLog(@"Counter: %ld", (long)counter);
        };

        // 调用这个局部 block
        incrementCounterBlock(); // Counter: 1
        incrementCounterBlock(); // 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 都会从栈上移除,因此不存在循环引用。

循环引用通常发生在以下场景:

  1. 当一个对象持有一个 block 的强引用。
  2. 该 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 是存在的。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值