本篇文章的主要内容

  • 了解何谓block。
  • 了解block的使用方法。

Block 是iOS在4.0版本之后新增的程序语法.

在iOS SDK 4.0之后,Block几乎出现在所有新版的API之中,换句话说,如果不了解Block这个概念就无法使用SDK 4.0版本以后的新功能,因此虽然Block本身的语法有点难度,但为了使用iOS的新功能我们还是得硬着头皮去了解这个新的程序概念。

一、看一看什么是Block

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

  接下来,我们通过一个例子来声明一个简单的Block变量.

int (^block)( int ) = NULL;

  我们一起来看一下声明Block变量的语法

  数据返回值类型  (^变量名) (参数列表) = NULL;

  接下来,我们为刚才的block变量进行赋值.

block = ^(int m){
return m * m;
};

  然后我们一起来使用一次这个block变量

//通过使用block变量,计算整型常量10的平方,并且打印在控制器输出
NSLog(@"10的平方是:%d",block());

  注意,到目前我们应该有发现block变量的使用步骤,有类似于函数的步骤

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

二、看一看如何直接使用block参数

  数组排序

    //声明数组变量
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithObjects:@"",@"",@"",@"",@"", nil]; //直接使用block进行数组升序排序
[mutableArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
//将两个参数转换为字符串的对象
NSString *value1 = (NSString *)obj1;
NSString *value2 = (NSString *)obj2; //value1与value2两个对象的比较结果直接返回
return [value1 compare:value2];
}]; //打印可变数组变量
NSLog(@"%@",mutableArray);

  简单的网络异步请求

    //声明网络地址对象
NSURL *url = [NSURL URLWithString:@"http://www.qq.com"]; //根据网络地址对象,声明网络请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url]; //直接使用block变量完成链接成功后的数据返回功能
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
//将二进制数据使用utf8编码转换成字符串对象
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; //打印链接完成后的结果
NSLog(@"%@",result); }];

  可以看出ios平台中的很多功能都已经集成了Block语法的处理方法.

三、看一看深入理解Block语法

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

  先来看一个例子。

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

  在上面的例子中,我们将intValue变量称为block执行过程中的外部变量,在block执行过程中可以直接使用该外部变量。

  再看一个例子。 

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

  在上面的例子中,我们编译程序后发现编译器会有红色错误,错误提示为

Variable is not assignable (missing __block type specifier)

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

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

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

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

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

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

  举例如下

//
// ViewController.m
//
// Created by lewis.
// #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 *note) {
NSLog(@"%@",self);
}];
} - (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} //当视图控制器对象销毁时,移除观察者
- (void)dealloc
{
if (observer) {
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}
} @end
在上面代码中,我们添加向通知中心注册了一个观察者,然后在 dealloc 时解除该注册,一切看起来正常。但这里有两个问题:
  • 在消息通知 block 中引用到了 self,在这里 self 对象被 block retain,而 observer 又 retain 该 block的一份拷贝,通知中心又持有 observer。因此只要 observer 对象还没有被解除注册,block 就会一直被通知中心持有,从而 self 就不会被释放,其 dealloc 就不会被调用。而我们却又期望在 dealloc 中通过 removeObserver 来解除注册以消除通知中心对 observer/block 的 retain。
  • 同时,observer 是在 self 所在类中定义赋值,因此是被 self retain 的,这样就形成了循环引用。

解决方式如下.

//
// ViewController.m
//
// Created by lewis.
// #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 *note){ //在block的执行过程中,使用强对象对弱对象进行引用
ViewController *bSelf = wSelf;
if (bSelf) {
NSLog(@"%@",bSelf);
}
}];
} - (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} //当视图控制器对象销毁时,移除观察者
- (void)dealloc
{
if (observer) {
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}
} @end 
  首先,在 block 之前定义对 self 的一个弱引用 wSelf,因为是弱引用,所以当 self 被释放时 wSelf 会变为 nil;然后在 block 中引用该弱应用,考虑到多线程情况,通过使用强引用 bSelf 来引用该弱引用,这时如果 self 不为 nil 就会 retain self,以防止在后面的使用过程中 self 被释放;然后在之后的 block 块中使用该强引用 sself,注意在使用前要对 bSelf 进行了 nil 检测,因为多线程环境下在用弱引用 wSelf 对强引用 bSelf 赋值时,弱引用 wSelf 可能已经为 nil 了。
 
   通过这种weak-strong手法,block 就不会持有 self 的引用,从而打破了循环引用。

OC语言的特性(二)-Block的更多相关文章

  1. iOS - OC 语言新特性

    前言 相对于 Java,OC 语言是一门古老的语言了,而它又是一门不断发展完善的语言.一些新的编译特性,为 OC 语言带来了许多新的活力.在 Xcode7 中,iOS9 的 SDK 已经全面兼容了 O ...

  2. OC 语言新特性

    前言 相对于 Java,OC 语言是一门古老的语言了,而它又是一门不断发展完善的语言.一些新的编译特性,为 OC 语言带来了许多新的活力. 在 Xcode7 中,iOS9 的 SDK 已经全面兼容了 ...

  3. OC语言的特性(一)-消息传递与调用函数的表现形式

    我们在初学Objective-C时,都会觉得ObjC中的消息传递和其他语言的调用函数差不多,只是在OC中,方法调用用消息传递这一概念来代替. 那么到底怎样区别OC中的消息传递与其他语言的调用函数呢. ...

  4. 黑马程序员——OC语言 三大特性之多态

    Java培训.Android培训.iOS培训..Net培训.期待与您交流! (以下内容是对黑马苹果入学视频的个人知识点总结) 三大特性之一的多态 (一)多态的基本概念 OC对象具有多态性体现在 Per ...

  5. 黑马程序员——OC语言 三大特性之继承

    Java培训.Android培训.iOS培训..Net培训.期待与您交流! (以下内容是对黑马苹果入学视频的个人知识点总结) 三大特性之继承 (一)继承的基本用法 先建立个Animal再用Dog继承前 ...

  6. OC语言(七)Block复习

    看下面一道Block的面试题: int i = 10; void(^myBlock)() = ^{ NSLog(@"%d",i); }; i = 100; myBlock(); 经 ...

  7. OC语言BLOCK和协议

    OC语言BLOCK和协议 一.BOLCK (一)简介 BLOCK是什么?苹果推荐的类型,效率高,在运行中保存代码.用来封装和保存代码,有点像函数,BLOCK可以在任何时候执行. BOLCK和函数的相似 ...

  8. 李洪强iOS开发之OC语言BLOCK和协议

    OC语言BLOCK和协议 一.BOLCK (一)简介 BLOCK是什么? 苹果推荐的类型,效率高,在运行中保存代码.用来封装和保存代码,有点像函数,BLOCK可以在任何时候执行. BOLCK和函数的相 ...

  9. oc语言--面向对象的三大特性

    一.封装 1.什么是封装 在程序上,隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别:将对象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的 ...

随机推荐

  1. Spring Boot 系列教程9-swagger-前后端分离后的标准

    前后端分离的必要 现在的趋势发展,需要把前后端开发和部署做到真正的分离 做前端的谁也不想用Maven或者Gradle作为构建工具 做后端的谁也不想要用Grunt或者Gulp作为构建工具 前后端需要通过 ...

  2. json的学习笔记

    json比较简单,所以先从json开始学起. 一 json的名称: json的全称是javascript object notation,中文名称为js 对象表示法. json的定义:json是一种轻 ...

  3. Android中的对话框AlertDialog使用技巧合集-转载

    Android中的对话框AlertDialog使用技巧合集     文章来自:http://blog.csdn.net/blue6626/article/details/6641105   今天我用自 ...

  4. Android :android.os.Process.myTid()与 Thread.currentThread().getId();

    这两种方式得到的ID并不是相同的,前者的返回值是int,后者是long. 个人猜测:应该是一个线程的两种得到的方式.类似于一个人有2个名字. 如有不对,请指正!

  5. BinTools 十六进制转换

    package de.rtner.misc; public class BinTools { public static final String hex = "0123456789ABCD ...

  6. GUI矩形、椭圆、线、框架

    所有的Swing组件必须由时间调度线程(event dispatch thread)进行配置,线程将鼠标点击和键盘敲击控制转移到用户接口组件.下面的代码片段是事件调度线程中的执行代码: EventQu ...

  7. UVALive 2053 Puzzlestan(深搜+技巧)

    这个题目的深搜形式,我也找出来了,dfs(i,j)表示第i个人选到了第j个物品,但是我却无限RE了,原因是我的viod型深搜太过暴力,我当时定义了一个计数器,来记录并限制递归的层数,发现它已经递归到了 ...

  8. HDU 2674 N!Again

    一开始以为有循环节,输出了前100项发现n>40都是0.因此,水题.... #include<cstdio> ]; ; int main() { a[]=; ; i<=; i+ ...

  9. C# 通过接口 post 请求

    /// <summary> /// 提交数据请求 方法一 /// </summary> /// <param name="POSTURL">请求 ...

  10. 剑指offer 二叉搜索树与双向链表

    html, body { font-size: 15px; } body { font-family: Helvetica, "Hiragino Sans GB", 微软雅黑, & ...