iOS开发 -------- Block技术中的weak - strong

一 Block是什么?



我们使用^运算符来声明一个Block变量,而且在声明完一个Block变量后要像声明普通变量一样,后面要加;

  • 声明Block变量
int (^block)(int) = NULL;

Block变量的语法

数据返回值类型 (^变量名)(参数列表) = NULL
  • 赋值Block变量
block = ^(int m) {
        return m * m;
};
  • 使用Block变量
// 通过使用block变量,计算整型常量10的平方,并且打印在控制器输出
    NSLog(@"10的平方是:%d",block(10));

示例代码:

//
//  ViewController.m
//  BlockDemo
//
//  Created by lovestarfish on 15/11/16.
//  Copyright © 2015年 S&G. All rights reserved.
//

//不是block内部不能用self.也不是内部用了self就循环引用
#import "ViewController.h"

//对block类型重命名,也就是把NSString*(^)(NSString*,NSString*)重命名
typedef NSString*(^YouBlock4)(NSString*,NSString*);

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self learnBlock];
}

- (void)learnBlock {
    // ^代表就是block,代码块
    int a;
    a = 10;

    //block很像一个函数,可以有参数,可以有返回值
    //需要调用的时候,我们就去调用

    //1. 无参数无返回值
    void(^myBlock1)() = ^{
        NSLog(@"无参数无返回值的block");
    };
    //调用block
    myBlock1();

    //2. 有参数无返回值
    void(^myBlock2)(int,int) = ^(int a,int b){
        NSLog(@"%d",a + b);
    };
    //调用block
    myBlock2(10,9);

    //3. 无参数有返回值
    NSString* (^myBlock3)() = ^{
        return @"无参数有返回值的block";
    };
    //调用block
    NSString *result = myBlock3();
    NSLog(@"%@",result);

    //4. 有参数有返回值
    NSString* (^myBlock4)(NSString *,NSString*) = ^(NSString *str1,NSString *str2){
        return [str1 stringByAppendingString:str2];
    };
    //调用block
    NSString *result2 = myBlock4(@"hell",@"o");
    NSLog(@"%@",result2);

    //5. block类型重命名
    YouBlock4 bb = ^(NSString *str1,NSString *str2){
        return [str1 stringByAppendingFormat:@"-----%@",str2];
    };
    NSLog(@"%@",bb(@"ni",@"hao"));
}

@end

通过以上代码可以得知,Block变量的使用步骤,类似于函数的步骤

  • 首先都要声明(声明函数,声明block变量);
  • 然后都要进行实现(实现函数,为block变量赋值实现过程);
  • 最后都要进行调用才能实现具体功能 

二 如何直接使用Block参数


  • 数组排序
    // 1.1 声明数组变量
    NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithObjects:@"5",@"2",@"3",@"9",@"7", nil];

    // 1.2 直接用block进行数组升序排序
    [mutableArray sortUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        // 将两个参数转换为字符串的对象
        NSString *value1 = (NSString *)obj1;
        NSString *value2 = (NSString *)obj2;

        // value1与value2两个对象的比较结果直接返回
        return [value1 compare:value2];
    }];

    // 1.3 打印可变数组变量
    NSLog(@"%@",mutableArray);
  • 简单的网络异步请求
    // 2.1 声明网络地址对象
    NSURL *url = [NSURL URLWithString:@"http://m.kuaidi100.com/query?type=quanfengkuaidi&postid=720140702702"];

    // 2.2 根据网络地址对象,声明网络请求对象
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    // 2.3 直接使用block变量完成链接成功后的数据返回功能
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        // 将二进制数据使用utf8编码转换成相应类型的对象
        NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];

        // 打印链接完成后的结果
        NSLog(@"%@",dic);
    }];

三 深入理解Block语法



在本节,主要去介绍的是使用 __block 修饰的变量能够完成的作用

先看一个例子

    // 1.声明一个局部整型变量
    int intValue = 3;

    // 2.声明一个返回值为int,一个int参数的block变量
    int (^block)(int) = ^(int m) {
        return m * intValue;
    };

    // 3.调用block变量,5作为参数之后的结果
    NSLog(@"block(5) = %d",block(5));

在本例中,intValue 变量称为block执行过程中的外部变量,在block执行过程中可以直接使用该外部变量.

再看一个例子

    // 1.声明一个局部整型变量
    int intValue = 3;

    // 2.声明一个返回值为int,一个int参数的block变量
    int (^block)(int) = ^(int m) {
        intValue++;
        return m * intValue;
    };

    // 3.调用block变量,5作为参数之后的结果
    NSLog(@"block(5) = %d",block(5));

在这个例子中,编译器编译后发现有红色错误,错误如下图:

为什么会出现不能被赋值的错误提示呢?

  • Block在实现时就会对它引用到的它所在方法中定义的栈变量进行一次只读拷贝
  • 在block块内使用只读拷贝

为了避免上述错误,就要使用__block修饰符来修饰外部变量,用来通知编译器该外部变量 intValue 与 block 中的 intValue 指的是同一块内存地址,而不需要内存拷贝

如下例:

    // 1.将intValue局部整型变量使用__block修饰符进行修饰
    __block int intValue = 3;

    // 2.声明一个返回值为int,一个int参数的block变量
    int (^block)(int) = ^(int m) {
        intValue++;
        return m * intValue;
    };

    // 3.调用block变量,5作为参数之后的结果
    NSLog(@"block(5) = %d",block(5));

四 使用Block要注意的内存问题

使用 weak-strong dance技术来避免循环引用

举例如下:

//
//  ViewController.m
//  BlockDemo
//
//  Created by lovestarfish on 15/11/16.
//  Copyright © 2015年 S&G. All rights reserved.
//

//不是block内部不能用self.也不是内部用了self就循环引用
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

{
    id observer;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // 添加观察者,观察主题修改消息通知,并且在收到消息通知后,打印视图控制器对象
    observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"kThemeChangeNotification" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
        NSLog(@"%@",self);
    }];
}

/**
 *  当视图控制器对象销毁时,移除观察者
 */
- (void)dealloc {
    if (observer) {
        [[NSNotificationCenter defaultCenter] removeObserver:observer];
    }
}

@end

在上面代码中,我们向通知中心注册了一个观察者,然后在dealloc时解除该注册,一切看起来正常.但是里面有两个问题:

在消息通知 block 中引用了 self, 在这里 self 对象被 block 保留一次,而 observer 又 retain 该 block 的一份拷贝,通知中心又持有 observer . 因此只要 observer 对象还没有被解除注册, block 就会一直被通知中心持有,从而 self 就不会被释放, 其 dealloc 就不会被调用.而我们却又期望在dealloc中通过 removeObserver 来解除注册以消除通知中心对 observer/block 的保留次数

同时,observer 是在 self 所在类中定义赋值,因此是被 self retain 的,这样就形成了循环引用

// // ViewController.m // BlockDemo // // Created by lovestarfish on 15/11/16. // Copyright © 2015年 S&G. All rights reserved. // //不是block内部不能用self.也不是内部用了self就循环引用 #import "ViewController.h" @interface ViewController () @end @implementation ViewController { id observer; } - (void)viewDidLoad { [super viewDidLoad]; // 先声明一个weak弱对象 __weak ViewController *wSelf = self; // 添加观察者,观察主题修改消息通知,并且在收到消息通知后,打印视图控制器对象 observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"kThemeChangeNotification" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { // 在block的执行过程中,使用强对象对弱对象进行引用 ViewController *bSelf = wSelf; if (bSelf) { NSLog(@"%@",bSelf); } }]; } /** * 当视图控制器对象销毁时,移除观察者 */ - (void)dealloc { if (observer) { [[NSNotificationCenter defaultCenter] removeObserver:observer]; } } @end

  • 在 block 之前定义对 self 的一个弱引用 wSelf, 因为是弱引用,所以当 self 被释放时 wSelf 会变为 nil ;
  • 在 block 中引用该弱引用,考虑到多线程情况,通过使用强引用 bSelf 来引用该弱引用,这时如果 self 不为 nil 就会 retain self,以防止在后面的使用过程中self 被释放
  • 在之后的 block 块中使用该强引用 bSelf, 注意在使用前要对 bSelf 进行了 nil 检测,因为多线程环境下在用弱引用 wSelf 对强引用 bSelf 赋值时,弱引用 wSelf 可能已经为 nil 了

通过这种 weak-strong 手法,block就不会持有self 的引用,从而打破了循环引用

时间: 2024-10-10 12:27:14

iOS开发 -------- Block技术中的weak - strong的相关文章

Swift中的Weak Strong Dance

亲爱的博客园的关注着博主文章的朋友们告诉你们一个很不幸的消息哦, 这篇文章将会是博主在博客园发表的最后一篇文章咯, 因为之后的文章博主只会发布到这里哦 http://daiweilai.github.io/ 新博客排版布局更好,阅读体验更佳,欢迎吐槽.留言.订阅哟 马上又要过年了,诶,再也不能像当初那样无耻地逗利是了(我们广东的方言讨红包的意思) 图1 图2 看来今年没利了 谁让哥已经工作了呢. 公司今年的开发任务算是完结了,苹果又极不负(hǎo)责(yàng)任(de)地放圣诞不审核了,所以这

iOS 开发 关于应用中使用拨打电话那点事

一.利用openURL(tel) 特点: 直接拨打, 不弹出提示. 并且, 拨打完以后, 留在通讯录中, 不返回到原来的应用. - (void)callPhone:(NSString *)phoneNumber {     //phoneNumber = "18369......"     NSMutableString * str=[[NSMutableString alloc] initWithFormat:@"tel:%@",phoneNumber];    

【iOS开发系列】XIB IBOutlets use strong or weak ?

有人问.在ARC下,IBOutlets究竟应该定义成strong 还是 weak ?支持这个答案的人最多.答案仅是摘自官方文档的一个片段: From a practical perspective, in iOS and OS X outlets should be defined as declared properties. Outlets should generally be weak, except for those from File's Owner to top-level ob

iOS开发——Block引起循环引用的解决方案

内存问题始终是软件开发中的头等大事,iOS开发中也不例外,在面试中也是必问的问题.今天我们主要来讲讲Block中涉及的循环引用问题.当我们自己一开始写代码的时候,可能会大量在block中使用self,但是当看到别人优秀的代码的时候,发现别人常常不是用self,而使用weakSelf. 为什么呢?本文的示例代码上传至 https://github.com/chenyufeng1991/Block_WeakSelf . 首先我先来说说内存管理的原则: 1.默认使用strong,可选weak.stro

iOS开发实战——CollectionView中cell的间距设置

我在前面多篇博客中详细讲解了CollectionView的使用与自定义CollectionViewCell的设计,可以参考<iOS开发实战--CollectionView点击事件与键盘隐藏结合案例><iOS高级开发--CollectionView修改cell的文本及模型重构>这几篇博客.但是今天还是需要来讲讲CollectionView实现中的一个小小的坑,这是我最近在网上浏览时发现很多开发者经常犯的错,所以我觉得有必要来好好谈一谈. 一个CollectionView控件中,两个c

ios开发 Block(一) 代码块

一.什么是Blocks      Block是一个C级别的语法以及运行时的一个特性,和标准C中的函数(函数指针)类似,但是其运行需要编译器和运行时支持,从ios4.0开始就很好的支持Block. 二.在ios开发中,什么情况下使用Block      Block除了能够定义参数列表.返回类型外,还能够获取被定义时的词法范围内的状态(比如局部变量),并且在一定条件下(比如使用__block变量)能够修改这些状态.此外,这些可修改的状态在相同词法范围内的多个block之间是共享的,即便出了该词法范围

iOS开发-Block回调

关于Block之前有一篇文章已经写过一篇文章Object-C-代码块Block回顾,不过写的比较浅显,不能体现出Block在实际开发中的重要性,关于Block的基础知识,可以参考之前的博客.在实际开发中Block在回调过程中的是非常适合开发使用,不管是苹果的官方的接口还是一些第三方库的接口中都用到了Block回调.很多情况下Block和GCD一起使用,最常见的场景的就是App去后台取数据的过程中是需要时间,数据取成功之后我们才能更新UI页面,这就是最常见的回调的方式,也可以通过Notificat

iOS开发——高级技术OC篇&amp;运行时(Runtime)机制

运行时(Runtime)机制 本文将会以笔者个人的小小研究为例总结一下关于iOS开发中运行时的使用和常用方法的介绍,关于跟多运行时相关技术请查看笔者之前写的运行时高级用法及相关语法或者查看响应官方文档. 下面就来看看什么是运行时,我们要怎么在iOS开发中去使用它. 官方介绍: 这里我们主要关注的是最后一种! 下面来看看Runtime的相关总结 #pragma mark 获取属性成员 /********************************************************

iOS开发——高级技术&amp;内购服务

内购服务 大家都知道做iOS开发本身的收入有三种来源:出售应用.内购和广告.国内用户通常很少直接 购买应用,因此对于开发者而言(特别是个人开发者),内购和广告收入就成了主要的收入来源.内购营销模式,通常软件本身是不收费的,但是要获得某些特权就 必须购买一些道具,而内购的过程是由苹果官方统一来管理的,所以和Game Center一样,在开发内购程序之前要做一些准备工作(下面的准备工作主要是针对真机的,模拟器省略Provisioning Profile配置过程): 前四步和Game Center基本