Objective-C block、weakself、strongself

举报
SHQ5785 发表于 2024/05/14 08:46:07 2024/05/14
【摘要】 一、前言Block是OC语言中的一种数据类型,它是预先准备好的代码,在需要的时候可以直接执行。可以做为参数传递,也可以做为返回值,一般在实际开发中前者用的比较多。在多线程和网络场景中使用得也相当频繁,一般都是作为回调使用!其形式类似于C语言中指向函数的指针,在开发中也是反向传值的一种方式。在OC中使用^操作符声明一个block变量,^也是标示着一段block代码块的开始。block的实体包...

一、前言

Block是OC语言中的一种数据类型,它是预先准备好的代码,在需要的时候可以直接执行。可以做为参数传递,也可以做为返回值,一般在实际开发中前者用的比较多。在多线程和网络场景中使用得也相当频繁,一般都是作为回调使用!其形式类似于C语言中指向函数的指针,在开发中也是反向传值的一种方式。

在OC中使用^操作符声明一个block变量,^也是标示着一段block代码块的开始。block的实体包含在{}中。

比如作为参数传递:

NSURL *url = [NSURL URLWithString:alertView.message];
[IJKVideoViewController presentFromViewController:self withTitle:[NSString stringWithFormat:@"URL: %@", url] URL:url completion:^{
[self.navigationController popViewControllerAnimated:NO];
}];

二、Block 基本用法

Block常见的4种形式如下:

  1. 无返回值,无参数:void(^myBlock)() = ^{};

  2. 无返回值,有参数:void(^myBlock)(int ,int) =^(int ,int ) {};

  3. 有返回值,无参数:int(^myBlock)()= ^{};

  4. 有返回值,有参数: int (^myBlock)(int,int) = ^(int,int){};

Block代码块不好记,苹果提供快捷键在实现文件的方法体中敲 inl 直接回车,就可以出现一个block的基本块。

语法如下:

returnType(^blockName)(parameterTypes) = ^(parameters) {
	statements
}

在使用block时会写出如下代码:

- (void)function{
  __weak typeof(self) weakself = self; //创建一个指向当前对象的弱引用
  [teseObject callFunc:^{
    __strong typeof(self) strongself = weakself; //block内部定义一个指向当前对象的强引用
    [strongself callFunc_1:....];
    [strongself callFunc_2:....];
    ....
}];
}

针对上面block块代码,

  1. 可不可以直接使用self
    不可以。因为这样block会强持有self对象,造成循环引用,从而导致内存泄露

  2. 可不可以直接使用weakself?
    看情况。由于weakself不会持有对象,因此不会造成循环引用的问题,但是使用weakself却会造成block执行不一致问题,试想一下上面的代码,当调用“callFunc_1”的时候weakself是有效的,但是当调用“callFunc_2”的时候weakself可能已经是nil了,这样就造成了block内执行不一致从而导致意想不到的结果。

  3. 循环引用是不是都是问题?
    答案是否定的。当block开始执行的时候,strongself会去取self对象的值,如果此时self已经为nil,那么整个block执行期间strongself都是nil,如果self有效,那么strongself就是利用了循环引用的特性保证了在block执行的时候self对象不会被析构(内存管理),保证block执行的一致性。其实我们在编写业务代码的时候(很多第三方开源类库)中会利用到循环引用的这一特性来保证block中引用的对象在block执行的时候依然有效,但是切忌使用黑魔法的时候要在block执行结束后打破循环引用。由于strongselfblock内部定义的变量,在block执行结束会由系统回收从而打破循环引用。

三、weakself 与 strongself

在OC的block中如果使用self的话会引起循环引用,也就是说,在block中直接使用self的话会被强引用,解决方案是使用#define WEAKSELF typeof(self) __weak weakSelf = self;,这样在block结束释放的时候,weakself因为是弱引用,也会被释放掉。一般情况下是没问题,但是,以下情况会有例外:

    WeakSelf(weakSelf)
    [self myBlock:^(NSString *str) {
        /**
         *  第一次调用的时候weakSelf还没有被释放
         */
        [weakSelf doSomething];
        /**
         *  第二次调用的时候weakSelf有可能就被释放掉了
         */
        [weakSelf doOtherThing];
    }];

为解决weakself被释放的问题,这个时候就要用到strongSelf,如下

WeakSelf(weakSelf)
[self myBlock:^(NSString *str) {
    /**
     * 在block中需要多次使用weakself时要转成strongSelf,这样确保在block执行完成之前不被释放掉
     */
    __strong __typeof(self) strongSelf = weakSelf;

    [strongSelf doSomething];

    [strongSelf doOtherThing];
}];

对于经常使用Block传值的应用场景,为防止循环引用可通过定义宏变量方式应用WEAKSELFSTRONGSELF

#define WEAKSELF typeof(self) __weak weakSelf = self;
#define STRONGSELF typeof(self) __strong strongSelf = self;

使用时直接在要在使用地方应用即可:

- (void)checkPermissionWithCompletion:(void (^)(BOOL isRecordable, NSString *recordingUrl, NSString *errorInfo))handler {
    WEAKSELF
    [[NemoSDK sharedInstance] checkPermissionWithMeetingNumber:self.meetingNumber completion:^(BOOL isRecordable, NSString *recordingUrl, NSString *errorInfo) {
        weakSelf.recordingUrl = recordingUrl;
        handler(isRecordable,recordingUrl, errorInfo);
    }];
}

四、实战讲解

// 初始化网关
__weak ViewController *weakSelf = self;
[self initialGateway:^{
    __strong ViewController *strongSelf = weakSelf;
    [strongSelf gotoH5View];
}];
# pragma mark - 初始化网关
// completeHandler 为块代码,可控制其在initialGateway中的执行位置
- (void)initialGateway:(void (^)(void))completeHandler
{
  [[EMMProxyUtils sharedInstance] startProxyServerWithCompletion:^(NSError *error) {
    if (error) {
      // 初始化网关失败
      [self showAlert:@"初始化网关失败." handler:^(UIAlertAction *action) {
        [self initialGateway:completeHandler];
      } cancelHandler:^(UIAlertAction *action) {
        exit(0);
      }];
    } else {
      // 本地代理服务启动成功
      NSUInteger proxyServerPort = [[EMMProxyUtils sharedInstance] portForBusinessServerHost:kTargetHost port:kTargetPort];
      if (proxyServerPort == 0) {
        // 获取本地代理端口失败.
        [self showAlert:@"获取本地代理端口失败." handler:^(UIAlertAction *action) {
          [self initialGateway:completeHandler];
        } cancelHandler:^(UIAlertAction *action) {
          exit(0);
        }];
        return;
      } else {
        completeHandler();
      }
    }
  }];
}
- (void)gotoH5View {
    
    [BPGeneralContainerSDK initSDKWithPluginXMLPath:[[NSBundle mainBundle] pathForResource:@"DemoCordovaConfig" ofType:@"xml"]];
    DemoConfig *config = [[DemoConfig alloc] init];
    self.webPageVC = [BPGeneralContainerSDK controllerWithConfigModel:config];
    self.webPageVC.bpWKWebviewStatusBlock = ^void(BPWKWebviewStatusType type, WKNavigation *navigation, NSError *error){
        NSLog(@"----type ----- %ld", type);
        if(type == BPWKWebviewDidFinishNavigation) {
            
        }
    };
    [self.navigationController pushViewController:self.webPageVC animated:YES];
    self.navigationController.navigationBarHidden = YES;
//    self.navigationController.interactivePopGestureRecognizer.delegate = self;
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        NSLog(@"-------shq5785-------");
        self.navigationController.interactivePopGestureRecognizer.enabled = YES;
    }
     
    UIScreenEdgePanGestureRecognizer *edgeGesture = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgeGesture:)];
    edgeGesture.edges = UIRectEdgeLeft;
    edgeGesture.delegate = self;
    [self.webPageVC.view addGestureRecognizer:edgeGesture];
    
}

五、拓展阅读

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。