OC语言的特性(二)-Block

本篇文章的主要内容

  • 了解何谓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(10));

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

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

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

  数组排序

    //声明数组变量
    NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithObjects:@"5",@"2",@"3",@"9",@"7", 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 = 3;

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

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

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

  再看一个例子。 

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

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

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

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

Variable is not assignable (missing __block type specifier)

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

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

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

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

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

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

四、使用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,布布扣,bubuko.com

时间: 2024-10-25 16:46:04

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

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

我们在初学Objective-C时,都会觉得ObjC中的消息传递和其他语言的调用函数差不多,只是在OC中,方法调用用消息传递这一概念来代替. 那么到底怎样区别OC中的消息传递与其他语言的调用函数呢. 可以使用C语言与OC语言进行一下对比. C语言示例 //声明一个函数,用来获取两个整型值的中较大的值int max(int a,int b);  int main(int argc, const char * argv[]) {    //调用函数,获取4,6两个整型值的最大值    int res

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

OC语言BLOCK和协议 一.BOLCK (一)简介 BLOCK是什么? 苹果推荐的类型,效率高,在运行中保存代码.用来封装和保存代码,有点像函数,BLOCK可以在任何时候执行. BOLCK和函数的相似性: (1)可以保存代码(2)有返回值(3)有形参(4)调用方式一样. 标识符 ^ (二)基本使用 (1)定义BLOCK变量 Int (^SumBlock)(int,int);//有参数,返回值类型为int Void (^MyBlock)()://无参数,返回值类型为空 (2)利用block封装代

OC语言Block

OC语言Block 一.Block (一)简介  Block是什么?苹果推荐的比较特殊的数据类型,效率高,在运行中保存代码.用来封装和保存代码,有点像函数,BLOCK可以在任何时候执行. Block和函数的相似性:(1)可以保存代码(2)有返回值(3)有形参(4)调用方式一样. Block的应用:(1)动画(2)多线程(3)集合遍历(4)网络请求回调 标识符 ^ (二)基本使用 (1)定义Block变量 Int (^SumBlock)(int,int);//有参数,返回值类型为int Void 

黑马程序员—oc语言学习心得—block

黑马程序员—oc语言学习心得—block -------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 1,程序一般用typedef定义一个block类型 然后调用 typedef void(^xxx)(); 可使用助记符 inlineblock 2,Block内部可以访问外部变量:默认情况下,Block内部不能修改外部的局部变量 给局部变量加上__block关键字,则这个局部变量可以在block内部进行修改. 3,block的作用: 利用block封

oc语言学习之基础知识点介绍(二):类和对象的进一步介绍

一.类.对象在内存中的存储 /* 内存分区: 栈:局部变量 堆:程序员自己写代码申请开辟的 程序员自己维护,编译器现在帮我们自动优化了,它在合适的给我们加上了释放空间的语句,所以我们现在写的对象不会造成内存泄露 全局区:所有的全局变量和静态变量 常量区:所有的常量 代码区:程序编译后的指令集 类是模板,肯定需要存在内存里面,因为实例化对象的时候需要根据这个模板来创建,那么存在内存里面,存在哪呢?? 类模板存在:全局区! 存的是:类的描述,还有所有的方法实现 每个对象都会有一个系统给我们的isa指

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

一.封装 1.什么是封装 在程序上,隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别:将对象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员. 1> set方法 ① 作用:提供一个方法给外界设置成员变量值,实现对参数的相应过滤 ② 命名规范 *方法名必须以set开头 *set后面跟上成员变量的名称,成员变量名首字母必须大写 *返回值一定是void *一定要接收一个参数,而且参数

OC语言特性

Category 你用分类都做了哪些事情? 声明私有方法 分解体积庞大的类文件 把Framework的私有方法公开 特点 在运行时决议,也就是在编译时并没有把Category中声明的内容添加到宿主类中,而是在运行的时候通过runtime将添加的方法添加到宿主类上面 可以为系统添加分类 分类中可以添加哪些内容? 实例方法 类方法 协议 属性 Category结构 struct category_t { const char *name; //分类名称 classref_t cls; //分类所属的

1,OC语言的前世今生 ,2,OC语言入门,3,OC语言与C的差异,4,面向对象,5,类和对象的抽象关系,6,类的代码创建,7,类的成员组成及访问

1,OC语言的前世今生 , 一, 在20世纪80年代早期,布莱德.麦克(Brad Cox)设计了OC语言,它在C语言的基础上增加了一层,这意味着对C进行了扩展,从而创造出一门新的程序设计语言,支持对象的创建和操作. 二,1985年,被赶出苹果公司的乔帮主成立了Next公司; 三, 1988年,Next计算机公司获得了OC语言的授权,并发展了OC语言库和一个开发环境,1994年,Next计算机公司(同年更名为Next软件公司)和Sun公司针对NEXTSTEP系统联合发布了一个标准规范,名为OPEN

李洪强iOS开发之OC语言description方法和sel

OC语言description方法和sel 一.description方法 Description方法包括类方法和对象方法.(NSObject类所包含) (一)基本知识 -description(对象方法) 使用NSLog和@%输出某个对象时,会调用对象的description方法,并拿到返回值进行输出. +description(类方法) 使用NSLog和@%输出某个对象时,会调用类对象的description方法,并拿到返回值进行输出,把整个对象一次性打印出来,打印对象使用%@. 使用@%打