IOS开发 block(代码块)基本使用
2015-04-22 22:45
253 查看
1. block基本概念:
(开篇废话)Block是C级别的语法和运行时特性。Block比较类似C函数,但是Block比之C函数,其灵活性体现在栈内存、堆内存的引用。
Block是苹果推荐的类型,效率高,可以帮助我们组织独立的代码段,并提高复用性和可读性。主要是用来在运行中封装代码和保存代码用的。
Block可以在任何时候被执行。
和c语言的比较:
1、可以保存代码。 2、有返回值。 3、有参数 4、调用方式一样 (函数和函数是同级的关系,函数里面不能定义行数,但是block可以定义在程序的任何地方,只要遵循一条原则:代码是从上到下执行的,先定义后使用)
最简单地理解:block就是一个用来保存代码的变量,可以在你需要的使用的时候通过block 来使用你保存的代码,通常用来做并发任务、遍历、以及回调。
格式说明:
(返回类型)(^块名称)(参数类型列表) = ^(形参列表) {代码实现}; 如果没有参数,等号后面参数列表的()可以省略
2. block 在开发或者系统架构中的使用
从 xcode 4.0 开始,系统类库中的函数越来越多的开始使用 block 作为参数,以下是在系统函数中使用代码块的部分情况 a. 遍历数组和字典 b. 排序 c. 视图动画 d. 结束回调 e. 错误处理 f. 多线程等
3. 必须了解的东西
废话说完后来点重点: a. block(代码块) 是 oc中的一种数据类型,可以被当做参数传递,可以有返回值,是一个能工作的代码单元,可以在任何需要的时候被执行,就像调用函数一样调用。 在 ios 开发中广泛使用。 b. ^ 是 block 的特有的标记。 c. block 熟练了解block 的定义(块代码的定义),记得实现代码包含在 {} 之间。 d. block 是以内联 inline 函数的方式被定义使用。 e. 本质上是轻量级的匿名函数。 c. 块代码的使用注意点 i. 默认情况下,不允许在块代码内部修改外部的变量的数值 ii. __block,让外部的变量能够在block中修改。 iii. 循环引用的问题 __weak (ios5.0以下的版本使用__unsafe_unretained(废话)) iv. 默认情况下,block 外部的变量,在 block 中是只读的。 v. 块代码与代理的区别
4、block的定义
1、block定义和指向函数的指针的对比
定义除了一个符号发生了改变基本是一样的:指向函数的指针和block定义的代码对比:
#import <Foundation/Foundation.h> // 测试函数 void test(){ NSLog(@"%s",__func__); } /* 对于指针不理解的:函数名就是指向函数的指针 */ int main(int argc, const char * argv[]) { @autoreleasepool { // 这个是定义了一个函数的指针并赋值 void (*Mytest)() = test; // 调用函数 Mytest(); // 定义一个没有返回值的block void(^Myblock)() = ^{ NSLog(@"这个是Myblock"); }; // 调用block Myblock(); /* void (*Mytest)(); void(^Myblock)(); 1、指向函数的指针和block的定义的对比,基本只有一个符号的区别。 2、使用函数的指针和block的使用基本是一样的。需要调用才会执行。 指向函数的指针是通过函数名的指向的地址调用函数。 block是直接执行一段保存的代码。 */ } return 0; } 如果要纠结啥子有返回值没有返回值有参数没有参数的block和指向函数的指针的对比那就自己去玩去。 打印结果: 2015-04-22 23:03:18.965 block[4274:1360943] test 2015-04-22 23:03:18.966 block[4274:1360943] 这个是Myblock
2、block定义—— 没有参数没有返回值的block的定义
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { // 定义block ————> 没有参数没有返回值的 /* 定义的时候,将block当成数据类型 特点: 1、类型比函数多了一个 ^ 2、设置数值,有一个^ ,内容是{}括起来的一段代码 最简单的方式:不带返回值不带参数。 void (^Myblock)() = ^ { // 要保存的代码实现; }; */ // 定义一个block 并保存一段代码 void (^Myblock)() = ^{ NSLog(@"Myblock"); // 这个是保存的代码 }; // 这个; 号是不能少的 // 调用block Myblock(); // 像调用函数一样调用block } return 0; } 打印结果: 2015-04-22 23:22:04.593 block[4306:1412796] Myblock
3、block定义—— 有参数没有返回值的block的定义
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { // 定义block ————> 有参数没有返回值的 void (^Myblock)(int) = ^(int num){ // 当有参数的时候 ^(int num){ 中间的()是不能少的 NSLog(@"Myblock,传入的参数是:%d",num ); }; // 调用block Myblock(10); } return 0; } 打印的结果: 2015-04-22 23:28:48.488 block[4317:1439967] Myblock,传入的参数是:10
4、block定义—— 有参数有返回值的block的定义
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { // 定义block ————> 有参数有返回值的 int (^Myblock)(int) = ^(int num){ NSLog(@"Myblock,传入的参数是:%d",num ); return num; //当定义的block 是有返回值的时候一定要返回要不就会报错 }; // 调用block Myblock(10); // block有返回值并不一定要接收 NSLog(@"%d", Myblock(20)); } return 0; } 打印的结果: 2015-04-22 23:34:34.298 block[4354:1462221] Myblock,传入的参数是:10 2015-04-22 23:34:34.301 block[4354:1462221] Myblock,传入的参数是:20 2015-04-22 23:34:34.301 block[4354:1462221] 20
5、block定义—— 有参数有返回值的block的定义可能看见的还有一种写法
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { // 定义block ————> 有参数有返回值的 int (^Myblock)(int,int) = ^int(int num1 ,int num2){ NSLog(@"Myblock,传入的参数num1是:%d num2是%d",num1,num2); return num1 + num2; //当定义的block 是有返回值的时候一定要返回要不就会报错 }; // 调用block Myblock(10,20); // block有返回值并不一定要接收 NSLog(@"%d", Myblock(20,30)); } return 0; } ^int( 这个中间的int时返回值类型可以省略不写 打印的结果: 2015-04-22 23:43:48.958 block[4378:1501255] Myblock,传入的参数num1是:10 num2是20 2015-04-22 23:43:48.959 block[4378:1501255] Myblock,传入的参数num1是:20 num2是30 2015-04-22 23:43:48.959 block[4378:1501255] 50
其他的什么数据类型参数和返回值的排列组合就不一一列之举,想玩的自己去试试。
6、block的秘籍——block的书写是我们最蛋疼的事,一招解决所有
使用inlineblock这个速记符号可以快速的敲出block的基本结构注意:block的书写一定要熟记。
inlineblock这个速记符号只能用来辅助记忆。
(如果你不知道填空大话,我也是醉了)
5、关于block几个疑惑和容易出错的问题
1、block在内存中的位置
栈里面的东西是不需要我们程序员管理的,内存中唯一一个需要程序员管理的是堆,我们在写代码的时候new出来的东西都在堆中。block默认情况下是在栈中的,是不需要我们程序员管理的。如果多block进行了一次copy操作,就会将block转移到堆中。
注意点:
如果block 在栈里,那么block中用到了外界的对象,不用我么管理。
但是如果block在堆中,那么block中如果用到外界对象,会对对象进行一次retain操作,也就是会进行强引用。
如果想让堆中的block不对使用到的外界对象进行retain,那么就只需要在外界对象的前面加__block.
2、关于block引用外部变量操作的问题
block 使用,如果引用了外部变量,就会对外部变量做一个copy的操作。记录住定义block时候的值。如果后续再修改外部变量的值,不会影响block内部的数值的变化!外部变量本来是在栈区中,block引用的那一刻,就将外部变量copy 到堆中了,block里面使用的时copy 后堆中的变量的值。 所有在block 调用之前修改外部变量的值,不会影响block里面值的原因。 如果要验证:可以通过打印地址值的方式来验证,栈区是高位地址值,相对于栈,堆在低位地址。 通过打印地址发现,block里面的变量的地址值比block外面的地址值要小很多。
示例程序: #import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { int x = 10; void (^Myblock)() = ^{ NSLog(@"%d",x); // 引用外部变量 (已经copy,记录了外部变量的值) }; x = 20; // 修改外部变量的值 Myblock(); } return 0; } 打印结果: 2015-04-23 00:37:15.045 block[4470:1683462] 10
3、关于在block内部修改外部变量的值的问题
在默认的情况下,是不允许在block内部修改外部变量的值。原因是:会破坏代码的可读性,不易于维护。
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { int x = 10; void (^Myblock)() = ^{ x = 80; // 这样是不能修改的,这样写直接报错 }; Myblock(); } return 0; }
如果我们一定要再block的内部修改外部变量的值,必须在外部变量的前面添加 _ _block ,这样才会允许修改。
使用__block,说明不在关系外部变量数值的具体变化。
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { __block int x = 10; // 必须在前面加 __block 才可以在block 中修改外部变量的值 void (^Myblock)() = ^{ x = 80; }; Myblock(); } return 0; }
为什么使用__block 会达到这个效果(可以通过跟踪地址值发现问题)?
在定义block时,如果引用了外部变量使用了__block的变量。block定义之后,外部变量同样会被copy到堆中,不同的是栈中的那一份没有了,只保留了堆中的那一份。在block 中修改的那一份和 保留的那一份是同一份。所以可以修改。
3、关于在block内部修改外部变量的值 —— 一个蛋疼的问题
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { // 指针记录的是地址 NSMutableString *strM = [NSMutableString stringWithString:@"zhangsan"]; NSLog(@"定义前 %p %p", strM, &strM); void (^myBlock)() = ^ { // 修改strM指针指向的内容 [strM setString:@"lisi"]; NSLog(@"inblock %p %p", strM, &strM); // 这句代码是修改strM指针指向的地址 // strM = [NSMutableString stringWithString:@"wangwu"]; }; NSLog(@"定义后 %p %p", strM, &strM); myBlock(); NSLog(@"%@", strM); } return 0; } 打印的结果: 2015-04-23 01:30:30.900 block[4566:1798733] 定义前 0x100406b30 0x7fff5fbff7d8 2015-04-23 01:30:30.901 block[4566:1798733] 定义后 0x100406b30 0x7fff5fbff7d8 2015-04-23 01:30:30.902 block[4566:1798733] inblock 0x100406b30 0x1004074f0 2015-04-23 01:30:30.902 block[4566:1798733] lisi
block copy的知识指针没有copy变量的地址。在block修改的是变量。所以结果会变。
4、关于在block在MRC 中的使用注意(重要的面试题(在ARC开发的时代基本没有主要是为了测试功底))
(小白略过)#import <Foundation/Foundation.h> // 块代码可以当作参数,也可以当作返回值 typedef void(^eBlock)(); /** 问 -以下代码在ARC中有问题吗?=》没有问题 -在MRC中有问题吗?存在内存隐患,i和b都是局部变量,出了作用域就会被释放 解决问题: -返回前使用 Block_copy -使用后,使用 Block_release 网上错误答案 return [b copy]; ********* Product - Analyze (静态分析) 从代码结构上分析是否存在缺陷!本身并不会运行程序!并不能够检测到真正的内存泄漏! 但是:只要是静态分析工具发现的问题,通常都是需要提升的代码! 静态分析工具,是MRC开发时的利器!提前发现内存隐患! 另外,在ARC开发时,如果程序要上架之前,建议使用静态分析工具检测一下,通常可以发现一些不注意的警告,有助于提升代码质量!尤其在使用到C语言框架的代码! */ eBlock myBlock() { int i = 10; eBlock b = ^ { NSLog(@"hello %d", i); }; // 利用Block_copy将block以及内部的变量拷贝到堆中 return Block_copy(b); } int main(int argc, const char * argv[]) { @autoreleasepool { eBlock bb = myBlock(); bb(); // 释放堆中block Block_release(bb); } return 0; }
6、block循环引用问题的解决
循环引用的结果就是导致对象无法释放。我们测试的最好的办法是在对象中重写dellac方法,看这个方法是否被调用。没有调用说明存在循环引用。
在我们的IOS开发当中,什么时候会出现循环引用:
在我们使用block的时候,如果block中使用到了self ,这个时候就需要关心循环引用的问题。
解决方案:__weak typeof(self) weakSelf = self;
// 示例代码SDWebImage 框架使用的使用用的代码: __weak typeof(self) weakSelf = self; SDWebImageManager *manage = [SDWebImageManager sharedManager]; [manage downloadImageWithURL:url options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { if (finished) { // 这里是用 weakSelf 代替self 避免循环引用 [weakSelf setupImageFrame: image]; } }];
注意点:并不是所有的block中使用self都会有循环引用的问题。为了避免循环引用的问题,遇到block中用到self 。我们都这么写,就可以避免循环引用的问题。
7、代理和block在使用的时候我们是怎么选择的。
委托和block是IOS上实现回调的两种机制。Block基本可以代替委托的功能,而且实现起来比较简洁,比较推荐能用block的地方不要用委托。单就编程过程而言,block对开发者处理逻辑,编程效率,代码阅读都有积极影响。
代理是一种很金典的模式,我们很多人都已经习惯了这种模式,若果对block的回调传值的过程不是很理解的话,建议使用代理。可以达到同样地效果。
一下是还未完善的区域,我会持续的更新的
8、 block 在 IOS 开发的实际运用
在实际的开发中block的使用基本就是传值和回调。这个也是难点和重点。1、 block 在 IOS 开发的实际运用
3、asdfa
1、
相关文章推荐
- iOS开发-(^)代码块(block)的使用
- iOS开发中block的声明、创建、传参的基本使用
- IOS开发之纯代码界面--基本控件使用篇
- IOS开发之纯代码界面----基本控件使用篇1
- IOS开发之纯代码界面----基本控件使用篇2
- IOS开发之纯代码界面----基本控件使用篇3
- IOS开发之纯代码界面----基本控件使用篇4
- IOS开发之纯代码界面--基本控件使用篇 ┊
- IOS开发之纯代码界面--基本控件使用篇 Demos
- iOS开发中block基本使用
- iOS开发block传值基本使用方法
- IOS开发之纯代码界面--基本控件使用篇
- IOS开发之纯代码界面--基本控件使用篇
- iOS开发-Block使用及循环引用的解决
- iOS开发 编译工程师 遇到once.h beng'kuispatch_once(predicate,block)重新拉取代码后解决
- iOS开发 使用命令行从Git拉取代码详解
- iOS开发通过代码方式使用AutoLayout (NSLayoutConstraint + Masonry)
- IOS 视屏开发之AVPlayer的基本使用
- iOS开发通过代码方式使用AutoLayout (NSLayoutConstraint + Masonry)
- iOS开发:Block作为参数使用(常见于各框架)