博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS 编写高质量Objective-C代码(六)
阅读量:5890 次
发布时间:2019-06-19

本文共 3652 字,大约阅读时间需要 12 分钟。

《编写高质量OC代码》已经顺利完成一二三四五六七篇!

附上链接:


本篇的主题是iOS中的 “Block的原理及应用”

先简单介绍一下今天的主角:block

  • block(块):是一种 “ 词法闭包 ”,通过block,开发者可将代码块像对象一样传递。

一、理解“block”的概念:

1. block的数据结构:

通过clang命令行工具(OC转C++),我们先来看一下block的内部数据结构大概是什么样子的?

struct Block_descriptor {    unsigned long int reserved;    unsigned long int size;    void (*copy)(void *dst, void *src);    void (*dispose)(void *);};struct Block_layout {    void *isa;    int flags;    int reserved;     void (*invoke)(void *, ...);    struct Block_descriptor *descriptor;    /* Imported variables. */};

解析:很显然,Block_layout是一个结构体:里面有一个isa指针,指向Class对象。还有一个函数指针,指向了块的实现代码。

block的数据结构


2. block的三种类型:全局块、栈块、堆块。

根据block在内存中的位置,block被分成三种类型:

类型 内存位置 介绍
__NSStackBlock__ 栈区 栈内有效,出栈后销毁。
__NSMallocBlock__ 堆区 copy到堆空间上。可以在定义的那个范围之外使用。
__NSGlobalBlock__ 全局区 不捕捉任何外部变量,全部信息在编译器就已确定。

  • 1. NSStackBlock 栈块:
    栈块保存于栈区,超出变量作用域,栈上的block以及声明的_block都会被销毁。

例如:

__block NSString *name = @"QiShare";void (^block)(void) = ^{    NSLog(@"%@ is an iOS team which loves to share technology.", name);};NSLog(@"block = %@", block);

小知识点:当block内部需要修改或访问外部变量时,外部变量需要额外用__block修饰。否则修改不了。

我们来看下打印:

ARC的场景

什么?居然是__NSMallocBlock__(堆块)?

那是因为ARC环境下,编译器自动帮我们加了copy操作。

这时我们关掉ARC:设置Objective-C Automatic Reference Counting = NO。再来看下打印:

MRC场景


  • 2. NSMallocBlock 堆块:
    堆block内存位于堆区,在变量作用域结束时依然可以使用。

通过上面的例子:

在ARC下,block会默认加上copy操作:变成__NSMallocBlock__


  • 3. NSGlobalBlock 全局块:
    块中无任何外界对象,所需的内存在编译时就可以确定,内存位于全局区。

类似于“单例”,copy是一个空操作。

例如:

void (^qiShare)(void) = ^{        NSLog(@"We love sharing.");};NSLog(@"%@",qiShare);

二、为常用的block类型创建typedef

为了增加代码的可读性可拓展性

需要为常用的block起个别名。

typedef为块起别名,也可令块变量用起来更加简单~

比如:

- (void)getDataWithToken:(NSString *)token success:(void (^)(id responseDic))success;//! 以上要改成下面这种typedef void (^SuccessBlock)(id responseDic);- (void)getDataWithToken:(NSString *)token success:(SuccessBlock)success;

三、用handler块降低代码分散程度

在我们iOS开发中,经常会异步执行一些任务,等待任务执行结束后再通知对象调用相关方法。

一般有两种做法:

  • 第一种:使用NSNotificationCenter:NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  • 第二种:使用委托协议:详情见[iOS 编写高质量Objective-C代码(四)]()。
  • 第三种:使用block回调:直接把block对象当做参数传给相关方法执行。

举个例子:AFNetworking的API设计及使用就是block回调

  • 接口设计:
- (NSURLSessionDataTask *)POST:(NSString *)URLString                    parameters:(id)parameters                       success:(void (^)(NSURLSessionDataTask *task, id responseObject))success                       failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure {    return [self POST:URLString parameters:parameters progress:nil success:success failure:failure];}
  • 使用:
AFHTTPSessionManager *manger =[AFHTTPSessionManager manager];    NSString *urlString = @"";    NSMutableDictionary *parameter= @{@"":@"",@"":@""};        [manger POST:urlString            parameters:parameter             success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {                NSLog(@"成功");            } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {                NSLog(@"%@",error);            }];}

四、用block引用其所属对象时避免出现循环引用

在我们日常开发中,如果block使用不当,很容易导致内存泄漏。

  • 理由:如果block被当前ViewController(self)持有,这时,如果block内部再持有ViewController(self),就会造成循环引用。
  • 解决方案:在block外部弱化self,再在block内部强化已经弱化的weakSelf

For Example:

__weak typeof(self) weakSelf = self;[self.operationQueue addOperationWithBlock:^{    __strong typeof(weakSelf) strongSelf = weakSelf;    if (completionHandler) {        KTVHCLogDataStorage(@"serial reader async end, %@", request.URLString);              completionHandler([strongSelf serialReaderWithRequest:request]);    }}];

当然,也不是所有block中使用到self都要先弱化成weakSelf,再强化成strongSelf

只要block没有被self所持有的,在block中就可以使用self
比如下面:

[QiNetwork requestBlock:^(id responsObject) {      NSLog(@"%@",self.name);  }];

另外,内存泄漏检测相关详情请看:

转载地址:http://lwwsx.baihongyu.com/

你可能感兴趣的文章
CSS行高——line-height
查看>>
WPF成长之路------翻转动画
查看>>
Html列表分页算法
查看>>
JS自学笔记03
查看>>
vim 加密(crypt)文本文档
查看>>
理解父类引用指向子类对象
查看>>
BZOJ-2763: [JLOI2011]飞行路线 (SPFA分层图)
查看>>
make、make clean、make install、make uninstall、make dist、make distcheck和make distclean
查看>>
华为内部面试题库---(5)
查看>>
JDK动态代理实现
查看>>
反射方法关闭窗体报错的解决方法
查看>>
NSInvocation 调用block clang代码转换c++
查看>>
计算字符串中每种字符出现的次数
查看>>
Python3学习笔记(十五):常用时间模块time和datetime
查看>>
SQL Server2005中使用XML-OPENXML
查看>>
Bingo·Leap Motion设备原理解密了
查看>>
ASP.NET多文件上传实例
查看>>
leetcode之Happy Number
查看>>
【转】Java泛型-类型擦除
查看>>
时间日期
查看>>