一 Block是什么?


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

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

Block变量的语法

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

示例代码:

//
// 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 = ; //block很像一个函数,可以有参数,可以有返回值
//需要调用的时候,我们就去调用 //1. 无参数无返回值
void(^myBlock1)() = ^{
NSLog(@"无参数无返回值的block");
};
//调用block
myBlock1(); //2. 有参数无返回值
void(^myBlock2)(int,int) = ^(int a,int b){
NSLog(@"%d",a + b);
};
//调用block
myBlock2(,); //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:@"",@"",@"",@"",@"", 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 = ; // 2.声明一个返回值为int,一个int参数的block变量
int (^block)(int) = ^(int m) {
return m * intValue;
}; // 3.调用block变量,5作为参数之后的结果
NSLog(@"block(5) = %d",block());

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

再看一个例子

    // 1.声明一个局部整型变量
int intValue = ; // 2.声明一个返回值为int,一个int参数的block变量
int (^block)(int) = ^(int m) {
intValue++;
return m * intValue;
}; // 3.调用block变量,5作为参数之后的结果
NSLog(@"block(5) = %d",block());

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

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

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

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

如下例:

    // 1.将intValue局部整型变量使用__block修饰符进行修饰
__block int intValue = ; // 2.声明一个返回值为int,一个int参数的block变量
int (^block)(int) = ^(int m) {
intValue++;
return m * intValue;
}; // 3.调用block变量,5作为参数之后的结果
NSLog(@"block(5) = %d",block());

四 使用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 的引用,从而打破了循环引用

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

  1. Swift中的Weak Strong Dance

    亲爱的博客园的关注着博主文章的朋友们告诉你们一个很不幸的消息哦, 这篇文章将会是博主在博客园发表的最后一篇文章咯, 因为之后的文章博主只会发布到这里哦 http://daiweilai.github. ...

  2. iOS开发--Block

    iOS开发--Block 1.什么是Block,block 的作用 ui开发和网络常见功能实现回调,按钮的事件处理方法是回调方法以及网络下载后的回调处理 (1)按钮 target-action   一 ...

  3. ios开发 block语句块

    ios开发 block语句块 1.block 理解为匿名函数 2.block变量的定义 //定义block变量,^表示定义block //技巧:函数名左右加括号,在函数名前面在加^ void (^bl ...

  4. iOS开发——Block详解

    iOS开发--Block详解 1. Block是什么 代码块 匿名函数 闭包--能够读取其他函数内部变量的函数 函数变量 实现基于指针和函数指针 实现回调的机制 Block是一个非常有特色的语法,它可 ...

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

    有人问.在ARC下,IBOutlets究竟应该定义成strong 还是 weak ?支持这个答案的人最多.答案仅是摘自官方文档的一个片段: From a practical perspective, ...

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

    运行时(Runtime)机制 本文将会以笔者个人的小小研究为例总结一下关于iOS开发中运行时的使用和常用方法的介绍,关于跟多运行时相关技术请查看笔者之前写的运行时高级用法及相关语法或者查看响应官方文档 ...

  7. iOS开发——高级技术&内购服务

    内购服务 大家都知道做iOS开发本身的收入有三种来源:出售应用.内购和广告.国内用户通常很少直接 购买应用,因此对于开发者而言(特别是个人开发者),内购和广告收入就成了主要的收入来源.内购营销模式,通 ...

  8. iOS开发-Block回调

    关于Block之前有一篇文章已经写过一篇文章Object-C-代码块Block回顾,不过写的比较浅显,不能体现出Block在实际开发中的重要性,关于Block的基础知识,可以参考之前的博客.在实际开发 ...

  9. iOS开发——高级技术精选OC篇&Runtime之字典转模型实战

    Runtime之字典转模型实战 如果您还不知道什么是runtime,那么请先看看这几篇文章: http://www.cnblogs.com/iCocos/p/4734687.html http://w ...

随机推荐

  1. pymysql.err.InterfaceError: (0, '')解决办法

    导致这个错误的原因是通过pymysql连接MySQL,没有关闭连接的操作,所以短时间内不会出问题,长时间保持这个连接会出现连接混乱.虽然看着自己的代码没错,还是会报 pymysql.err.Inter ...

  2. Nestjs 上传文件

    Docs: https://docs.nestjs.com/techniques/file-upload 上传单文件 @Post('upload') @UseInterceptors(FileInte ...

  3. Mac OSX取消Apache(httpd)开机启动(转载)

    启动服务时提示Apache启动失败,80端口被占用.查看进程发现存在几个httpd. OS X自带Apache,可是默认是没有启动的.我也没有开启Web共享,怎么就开机启动了呢? 不知道是不是因为安装 ...

  4. python __all__

    它不仅在第一时间展现了模块的内容大纲,而且也更清晰的提供了外部访问接口. 若__all__的list中未定义,即便有实现也会找不到.

  5. Dubbo 分布式服务框架入门

    要想了解 Dubbo 是什么,我们不防先了解它有什么用.使用场景:比如我想开发一个网上商城项目,这个网上商城呢,比较复杂,分为 pc 端 web 管理后台,微信端销售公众号,那么我们分成四个项目,pc ...

  6. 个人小爱好:Operating System: three easy pieces第6章第5节——总结

    总结 我们讨论了实现CPU虚拟化的部分底层机制,及我们统称为直接执行(direct execution)的一组技术.基本的思想十分简单明了:直接在CPU上运行你想运行的代码,但是你先得确保将硬件设置好 ...

  7. poj2480(利用欧拉函数的积性求解)

    题目链接: http://poj.org/problem?id=2480 题意:∑gcd(i, N) 1<=i <=N,就这个公式,给你一个n,让你求sum=gcd(1,n)+gcd(2, ...

  8. RoR - Advanced Querying

    Seeding the Database: db/seed.rb 可以提供预设data rake db:seed #seeds.rb Person.create! [ {first_name : &q ...

  9. Spring框架源码阅读之Springs-beans(一)容器的基本实现概述(待续)

    去年通过实际框架代码的阅读,以及结合<Spring源码深度解析>和<Spring技术内幕>的阅读,对Spring框架内Bean模块有了一个整体性的认识.对此进行的总结性整理和回 ...

  10. Error: Cannot find module 'babel-helpers'

    cnpm install babel-core babel-loader babel-plugin-transform-runtime -D cnpm install babel-preset-env ...