zoukankan      html  css  js  c++  java
  • Blocks学习笔记总结

      本文是对Apple的《Blocks Progromming Gude》学习的笔记总结。

      对象时C级别的语法和运行时特性。和标准C函数很类似,但除了可执行代码外,还可能包含了变量自动绑定(栈)或内存托管(堆)。所以一个block维护一个状态集(数据),可以在执行的时候用来影响程序行为。Block用来作为回调特别有用。

      你可以在MAC OS 10.6及其以后版本、IOS 4.0及其以后版本上使用Blocks.

      Blocks运行时是开源的,可以再LLVM's compiler-rt subproject repository(LLVM的RT编译器的子项目)里面找到他。

    1.声明一个Block

    1 int (^myBlock) (int) = ^(int num){
    2     return num * num; 
    3 }
    4 说明:
    5 int:返回值类型,如果没有返回值则为void
    6 (^myBlock):块定义需要有一个^标记,myBlock是块名称
    7 (int):参数类型列表,如果没有参数则为void
    8 ^(int num):以^开头的参数列表,如果没有则为void,也可以省略
    9 {}:block体(相当于函数体)

    2.使用block

    1 printf("%d",myBlock(7));

    3.很多情况下不需要对block进行生命,而是直接使用内联的block

    1 char *myCharacters[3] = {"TomJohn","George","Jim Green"};
    2 
    3 qsort_b(myCharacters,3,sizeof(char *),^(const void *l,const void *r){
    4     char *left = *(char **)l;
    5     char *right = *(char **)r;
    6     return strncmp(left,right,l);
    7 });

    4.在Cocoa frameworks中有一些方法使用block作为参数,通常不是执行对一个对象的集合的操作就是在操作完成的时候作为回调使用。例子中显示了如何通过NSArray的sortedArrayUsingComparator:使用block.

     1 -(void) sort{
     2     NSArray *stringArray = [NSArray arrayWithObjects:@"Java网络编程",@"Java开发实战经典",@"研磨设计模式",@"iPhone开发秘籍",@"自己动手写网络爬虫",@"Cocos2d 游戏开发实战",@"Oracle宝典",@"Java Web开发实战",nil];
     3     static NSStringCompareOptions options = NSCaseInsensitiveSearch |
     4                 NSNumericSearch |
     5                 NSWidthInsensitiveSearch |
     6                 NSForcedOrderingSearch;
     7     NSLocale *currentLocale = [NSLocale currentLocale];
     8     NSComparator sortBlock = ^(id string1,id string2){
     9         NSRange string1Range = NSMakeRange(0, [string1 length]);
    10         return [string1 compare:string2 options:options range:string1Range locale:currentLocale];
    11     };
    12     NSArray *sortedArray = [stringArray sortedArrayUsingComparator:sortBlock];
    13     NSLog(@"array:%@",sortedArray);
    14 }

    5、Blocks默认不能修改相同作用域范围内的变量,但是如果这个相同作用域的变量如果使用了__block关键字进行修饰,则可以通过blocks进行修改。

    6、一个block就是一个匿名的内联代码集合体:

      *和函数一样拥有参数类型
      *有推断和声明的返回值类型
      *可以捕获它的声明所在的相同作用域的状态,可以和其它定义在相同作用于范围的bolcks进行共享更改
      *在相同作用域范围被销毁后持续共享和更改相同作用域范围的状态

    7、Blocks通常代表一个很小、自包的代码段。因此他们作为封装的工作单元在并发执行或者一个集合项上或者当其他操作执行完毕时的回调的时候非常使用。

    Blocks作为传统的回调函数的一个实用的替代方法有以下两个原因:

      它们可以让你在调用的地方编写代码实现后面将要执行的操作。因此Blocks通常作为Frameworks 方法的参数

      它们允许你访问局部变量,而不是需要使用一个你想要执行操作时继承所有上下文信息的数据结构来进行回调。你可以直接简单的访问局部变量。

    8、blocks和一个函数指针很相似,除了使用^来代替*。下面的都是合法的block的声明:

    1 void (^blockReturningVoidWithVoidArgument)(void);
    2 int (^blockReturningIntWIthIntndCharArguments)(int,char);
    3 void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);
    4 
    5 Blocks还支持可变参数(...).一个没有使用任何参数的block必须在参数列表上用void标明。

    9、可以把一个bolcks强制转换成任意类型,反之亦然,但是不能通过*修饰符来解引用一个block,因此一个block的带笑傲是无法在编译的时候计算的。你同样可以创建block的类型,挡在多个地方使用同一个给定签名的block的时候,这通常被认为是最佳的方法:

    1 typedef float (^MyBlockType) (float,float);
    2 MyBlockType myFirstBlock = ...;
    3 MyBlockType mySecondBlock = ...;

    10、创建一个Block

    1 int (^oneFrom) (int);
    2 oneFrom = ^(int anInt){
    3     return anInt - 1;
    4 };
    5 如果咩有显式的给一个block表达式声明一个返回值,它会自动的从block内容推断出来。如果返回值是推断的,而且参数列表也是void,那么你同样可以省略参数列表的void。如果或者当出现多个返回状态的时候,它们必须是完全匹配的(如果有必要可以使用强制类型转换)

    11、全局Blocks

    在文件级别,可以把block作为全局标示符:

    1 #import <stdio.h>
    2 
    3 int GlobalInt  = 0;
    4 int (^getGaobalInt)(void) = ^{return GlobalInt;};

    12、在block的主体代码中,变量可以被使用五种方法来处理。可以引用三种标准类型的变量,就像在函数里面引用一样:

    全局变量:包括静态局部变量

    全局函数:在技术上说这不是变量

    封闭范围内的局部变量和参数

    *在函数界别是__block变量。这些在block里面是可变的,并且任何引用block的都被保存一份副本到堆里面。

    *引用const

    *最后在实现方法里面,blocks也许会引用Objective-C的实例变量。

    在Blocks里面使用变量遵循以下的规则:

    全局变量可以访问,包括在相同作用域范围内的静态变量

    传递给block的参数可以访问(和函数的参数一样)

    程序里面属于同一作用域范围的堆变量作为const变量(只读)。它们的值在程序里面的block表达式内使用。在嵌套block里面,该值在最近的封闭范围内被捕获。

    属于同一作用域内并被__block修饰符标识的变量作为引用传递因此是可变的。

    属于同一作用域范围内的block的变量就和函数的局部变量操作一样。

    example:使用本地非静态变量

    1 int multipiler = 7;
    2 int (^myBlock)(int) = ^(int num){
    3     return multipiler * 7;
    4 };
    5 如果在block体内修改multipiler的值将会发生错误。

    13、可以通过__block修饰符指定引入的变量是可以更改的,此种类型的变量保存在变量共享的作用域范围内,所有的blocks和block副本都声明或者创建在和变量的作用域相同的范围内。你可以指定引入一个变量为可更改的,即读-写的,通过应用__block存储类型修饰符。局部变量的__block的存储和register、auto、static等存储类型相似,但它们之间不兼容。 __block变量保存在变量共享的作用域范围内,所有的blocks和block副本都声明或创建在和变量的作用域相同范围内。所以,如果任何blocks副本声明在栈内并未超出栈的结束时,该存储会让栈帧免于被破坏(比如封装为以后执行)。同一作用域范围内给定的多个block可以同时使用一个共享变量。 作为一种优化,block存储在栈上面,就像blocks本身一样。如果使用Block_copy拷贝了block的一个副本(或者在Objective-C里面给block发送了一条copy消息),变量会被拷贝到堆上面。所以一个__block变量的地址可以随时间推移而被更改。

    使用__block的变量有两个限制:它们不能是可变长的数组,并且它们不能是包含有C99可变长度的数组变量的数据结构。

    1 __block int x = 123; // x lives in block storage
    2 void (^printXAndY)(int) = ^(int y) {
    3 x = x + y;
    4 printf("%d %d\n", x, y);
    5 };
    6 printXAndY(456);
     1 下面的例子显示了blocks和其他几个类型变量间的交互:
     2 extern NSInteger CounterGlobal;
     3 static NSInteger CounterStatic;
     4 {
     5 NSInteger localCounter = 42;
     6 __block char localCharacter;
     7 void (^aBlock)(void) = ^(void) {
     8 ++CounterGlobal;
     9 ++CounterStatic;
    10 CounterGlobal = localCounter; // localCounter fixed at block creation
    11 localCharacter = 'a'; // sets localCharacter in enclosing scope
    12 };
    13 ++localCounter; // unseen by the block
    14 localCharacter = 'b';
    15 aBlock(); // execute the block
    16 
    17 // localCharacter now 'a'
    18 }

    14、在引用计数的环境里面,默认情况下当你在block里面引用一个Objective-C对象的时候,该对象会被retain。当你简单的引用了一个对象的实例变量时,它同样被retain。但是被__block存储类型修饰符标记的对象变量不会被retain

    注意:在垃圾回收机制里面,如果你同时使用__weak和__block来标识一个变量,那么该block将不会保证它是一直是有效的。 如果你在实现方法的时候使用了block,对象的内存管理规则更微妙:
    如果你通过引用来访问一个实例变量,self会被retain。
    如果你通过值来访问一个实例变量,那么变量会被retain

    1 dispatch_async(queue, ^{
    2 // instanceVariable is used by reference, self is retained
    3 doSomethingWithObject(instanceVariable);
    4 });
    5 id localVariable = instanceVariable;
    6 dispatch_async(queue, ^{
    7 // localVariable is used by value, localVariable is retained (not self)
    8 doSomethingWithObject(localVariable);
    9 });

    15、通常你可以在block内使用C++的对象。在成员函数里面,通过隐式的导入this指针引用成员变量和函数,结果会很微妙。有两个条件可以让block被拷贝:

    如果你拥有__block存储的类,它本来是一个基于栈的C++对象,那么通常会使用copy的构造函数。

    如果你在block里面使用任何其他C++基于栈的对象,它必须包含一个const copy的构造函数。该C++对象使用该构造函数来拷贝。

     16、当你拷贝一个block时,任何在该block里面对其他blocks的引用都会在需要的时候被拷贝,即拷贝整个目录树(从顶部开始)。如果你有block变量并在该block里面引用其他的block,那么那个其他的block会被拷贝一份。 当你拷贝一个基于栈的block时,你会获得一个新的block。但是如果你拷贝一个基于堆的block,你只是简单的递增了该block的引用数,并把原始的block作为函数或方法的返回值。

    17、调用blocks

    1>可以向函数一样直接调用,如:blockName(arg1,arg2);

    2>内嵌的方式使用,Cocoa Frameworks中使用了很多的blocks,如:

    1 char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
    2 qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
    3 char *left = *(char **)l;
    4 char *right = *(char **)r;
    5 return strncmp(left, right, 1);
    6 });

    3>作为方法的参数:

     1 NSArray *array = [NSArray arrayWithObjects: @"A", @"B", @"C", @"A", @"B", @"Z",@"G", @"are", @"Q", nil];
     2 NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil];
     3 BOOL (^test)(id obj, NSUInteger idx, BOOL *stop);
     4 test = ^ (id obj, NSUInteger idx, BOOL *stop) {
     5 if (idx < 5) {
     6 if ([filterSet containsObject: obj]) {
     7 return YES;
     8 }
     9 }
    10 return NO;
    11 };
    12 NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];
    13 NSLog(@"indexes: %@", indexes);

    18、拷贝Blocks

    通常,你不需要copy(或retain)一个block.在你希望block在它被声明的作用域被销毁后继续使用的话,你需要做一份拷贝。拷贝会把block移到堆里面。 你可以使用C函数来copy和release一个block:
    Block_copy();
    Block_release();
    如果你使用Objective-C,你可以给一个block发送copy、retain和release(或autorelease)消息。 为了避免内存泄露,你必须总是平衡Block_copy()和Block_release()。你必须平衡copy或retain和release(或autorelease)--除非是在垃圾回收的环境里面。

    19、使用Blocks需要避免的模式:

    一个block的文本(通常是^{...})是一个代表block的本地栈数据结构地址。因此该本地栈数据结构的作用范围是封闭的复合状态,所以你应该避免下面例子显示的模式:

    1 void dontDoThis() {
    2 void (^blockArray[3])(void); // an array of 3 block references
    3 for (int i = 0; i < 3; ++i) {
    4 blockArray[i] = ^{ printf("hello, %d\n", i); };
    5 // WRONG: The block literal scope is the "for" loop
    6 }
    7 }
    8
    1 void dontDoThisEither() {
    2 void (^block)(void);
    3 int i = random():
    4 if (i > 1000) {
    5 block = ^{ printf("got i at: %d\n", i); };
    6 // WRONG: The block literal scope is the "then" clause
    7 }
    8 // ...
    9 }

    20、调试Blocks

    你可以在blocks里面设置断点并单步调试。你可以在一个GDB的对话里面使用invoke-block来调用一个block。如下面的例子所示:
    $ invoke-block myBlock 10 20
    如果你想传递一个C字符串,你必须用引号括这它。例如,为了传递this string给doSomethingWithString的block,你可以类似下面这样写:
    $ invoke-block doSomethingWithString "\"this string\""
    21、定义接受Blocks的函数和方法的区别:

     1 void useCodeBlock(NSComparisonResult (^theBlock)(NSString *str)){
     2     if(NSOrderedSame == theBlock(@"foo")){
     3         doSomeThingSame();
     4     }else{
     5         doSomethingElese();
     6     }
     7 }
     8 -(NSMutableArray*) useCodeBlock:(NSArray *) inArray withBlock:(BOOL (^)(NSInteger)) block{
     9     NSMutableArray *result = [NSMutableArray array];
    10     for(NSNumber *number in inArray){
    11         if(block([number integerValue])){
    12             [result addObject:number];
    13         }
    14     }
    15     return result;
    16 }

    22、所有标准的OC引用计数内存管理方法在代码块上都是用。但是块是在站上分配的,所以对传入代码块的独享需要使用-copy而不是-retain,如果需要保留它则需要在堆上得到一个副本。

    工作机制是,运行时会将代码块使用的任何外部变量和self对象都以敞亮的方式复制到堆上,这样你就可以访问那些变量以及所有的成员变量(代码块中创建的对象的成员变量)。任何通过__block指令标识的变量都会按位复制到堆上,代码块就需要负责与使用这些变量相关的其他内存管理工作。

    23、通过typedef提高代码快的可读性

    View Code
     1 #import <Foundation/Foundation.h>
     2 
     3 typedef void (^BlockWithCharArg) (char*);
     4 @interface Person : NSObject
     5 {
     6     BlockWithCharArg myBlock;
     7 }
     8 -(void) doSomethinggWithBlock;
     9 -(void) setMyBlock:(BlockWithCharArg) inBlock;
    10 
    11 @end
    12 
    13 
    14 #import "Person.h"
    15 
    16 
    17 @implementation Person
    18 
    19 -(void) doSomethinggWithBlock{
    20     myBlock("foo");
    21 }
    22 -(void) setMyBlock:(BlockWithCharArg) inBlock{
    23     myBlock = [inBlock copy];
    24 }
    25 -(void) dealloc{
    26     [myBlock release];
    27     [super release];
    28 }
    29 @end
  • 相关阅读:
    Android url中文编码问题
    android studio error configuration with name default not found
    Android studio 配置JNI环境
    Github获取仓库最新Release版本号API
    微信小程序开发——设置默认图片、错误加载图片
    微信小程序开发-rem转换rpx小工具
    微信小程序开发小技巧——单击事件传参、动态修改样式、轮播样式修改等
    解决vue-router嵌套路由(子路由)在history模式下刷新无法渲染页面的问题
    VUE图片懒加载-vue lazyload插件的简单使用
    vue项目微信分享之后路由链接被破坏怎么办
  • 原文地址:https://www.cnblogs.com/xinye/p/2941203.html
Copyright © 2011-2022 走看看