由浅到深的了解block

来源:互联网 发布:域名未授权 编辑:程序博客网 时间:2024/05/16 10:07

作为IOS开发者来说,你不会用block,真的就不能和其他小伙伴一块玩耍了,block运用随处可见,block这样的语法真的看起来很诡异,那么这个神奇的block(闭包)到底是何方神圣呢?各位看官,不要讲话,保留疑问,先看文章摘要,please静静的感受一下它的用法吧, go!!

本文章的概要 
1. block的基本应用 
2. block的typedef 
3. block的作用 
4. block运用过程中出现的问题以及如何规避 
5. 深入了解block


  1. block的基本应用 
    直观感受block跟函数很像,函数在调用前必须声名

void test3();


int main(int argc,constchar * argv[])

{

    @autoreleasepool {

        test3();

    }

    return0;

}


 同样的block 同样也需要调用前必须声明

/**

 定义了一个有返回值有参数的block

 */

void test3()

{

    // 定义一个block计算两个整数的和

    int (^sumBlock)(int,int) = ^(int num1,int num2) {

        return num1 + num2;

    };

    int c = sumBlock(10,9);

    

    // 定义一个block计算一个整数的平方

    int (^pinfangBlock)(int);

    pinfangBlock = ^(int num){

        return num * num;

    };

    

    pinfangBlock = ^(int num) {

        return num +2;

    };

    

    int d = pinfangBlock(10);

    NSLog(@"d is %d", d);

}


/**

 定义了一个没有返回值但有参数的block

 */

void test2()

{

    // 定义block变量,存储一段代码,这段代码的功能是能打印任意行数的****

    void (^logStarBlock)(int) = ^(int numberOfLines){

        for (int i =0; i<numberOfLines; i++) {

            NSLog(@"***************");

        }

    };

    logStarBlock(1);

}


/**

 定义了一个没有返回值没有参数的block

 */

void test()

{

    // 定义简单的block变量

    // block跟函数很像:返回值、参数

    void (^logStar)() = ^{

        NSLog(@"***************");

        NSLog(@"***************");

        NSLog(@"***************");

    };

    

    logStar();

}

  1. block的typedef 
    但是如果只是在函数体进行声明,调用,代码看起来不美观,感觉太乱了,所以需要typedef,这才是经常用的一种方式


// 定义一个叫做MyBlock的类型

// 利用MyBlock类型可以定义block变量

// 利用MyBlock类型定义出来的变量,存储的代码必须返回int必须接受2int类型的参数

typedef int (^MyBlock)(int,int);


int main(int argc,constchar * argv[])

{

    @autoreleasepool {

        MyBlock minusBlock = ^(int num1,int num2) {

            return num1 - num2;

        };

        

        MyBlock multiBlock = ^(int num1,int num2) {

            return num1 * num2;

        };

        

        multiBlock(10,1);

        

    }

    return0;

}


void test()

{

    // 定义一个block来计算2个整数的差

    int (^minusBlock)(int,int) = ^(int num1,int num2) {

        return num1 - num2;

    };

    

    // 定义一个block来计算2个整数的积

    int (^multiBlock)(int,int) = ^(int num1,int num2) {

        return num1 * num2;

    };

}


当然你也可以这样

typedef void(^tapBlock)(NSIndexPath*indexPath);

typedef void(^textBlock)(NSIndexPath*indexPath,NSString*text);


typedef  void(^pushTextViewBlock)(NSIndexPath*cellIndexPath,NSString*title,NSString*defaultText);

typedef  void(^showImageBlock)(NSIndexPath*indexPath,NSArray* imageArr,NSInteger imgIndex);

typedef  void(^deletPhotoBlock)(NSIndexPath*indexPath,NSArray*imgArr,NSInteger imgIndex);

typedef void(^scannerBlock)(NSString *scannerType);

typedef void (^SearchStaffBlock)(NSString *backTitle,NSArray *cidArray,int tag);


typedef void(^SearchCustomMobile)(NSString *cidTitle,NSString *title);

@interface CMDynomicCell : UITableViewCell


@property(nonatomic,strong)NSIndexPath* selectIndexPath;  //cell所在的indexPath;

@property(nonatomic,strong)textBlock SingleLineText;      //单行文本的回调

@property(nonatomic,strong)textBlock NumberText;          //数字文本的回调

@property(nonatomic,strong)pushTextViewBlock pushTextView;//多行文本的回调

@end

调用self.textBlock=^(NSIndexPath*indexPath,NSString*text){

};

空外一种方式直接在方法上直接使用,这样看起来更加灵活了

-(void)noticeTaskList:(int)userId noticeId:(int)noticeId taskId:(int)taskId success:(void(^)(id object))successBlock fail:(void(^)(NSString* msg))failBlock;


-(void)noticeTaskList:(int)userId noticeId:(int)noticeId taskId:(int)taskId success:(void(^)(id object))successBlock fail:(void(^)(NSString* msg))failBlock{

   if(successBlock) {

   successBlock(id);

  };

   if(failBlock) {

   failBlock(NSString*);

  }

}

调用方式:

        

[[CMLoginHttpServicesharedInstance]login:usernamecompanyUsername:companyUsernamephone:phonepassword:passwordimei:imeiversionNo:versionNomobileName:mobileNamemobileType:mobileType success:^(id object) {

            

        } fail:^(NSString *msg) {

           

        }];




使用方式总结 
这里写图片描述
3. block的作用

1>重构代码,解耦合

#import <Foundation/Foundation.h>


typedef void (^WorkBlock)();


void goToWork(WorkBlock workBlock)

{

    NSLog(@"起床");

    NSLog(@"刷牙");

    NSLog(@"穿衣服穿鞋");

    

    NSLog(@"出门");

    NSLog(@"搭公交");

    

    NSLog(@"抵达公司");

    

    // 实事

    // 调用block之前一定要做判断

    if (workBlock !=nil) {

        workBlock();

    }

    // 实事

    

    NSLog(@"叮咚叮咚下班了");

    NSLog(@"搭公交");

    NSLog(@"回家");

    NSLog(@"睡觉");

}


/**

 模拟星期一上班的具体情况

 */

void goToWorkInDay1()

{

    goToWork(^{

        NSLog(@"了解项目的需求");

    });

}


void goToWorkInDay2()

{

    // 实事

    goToWork(^{

        NSLog(@"熟悉公司以前的代码");

    });

    // 实事

}


void goToWorkInDay3()

{

    // 实事

    goToWork(^{

        NSLog(@"开始编写代码");

    });

    // 实事

}


void test(int (^myblock)(int num1,double num2, char num3))

{

    

}


int main(int argc,constchar * argv[])

{

    @autoreleasepool {

////        goToWorkInDay3();

//        test(^int(int num1, double num2, char num3) {

//            

//        });

        

//        WorkBlock block = ^{

//            NSLog(@"-------------------");

//        };

//        

//        goToWork(block);

    }

    return0;

}




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

- (void)testAccessVariable 

    NSInteger outsideVariable = 10; 
    //__block NSInteger outsideVariable = 10; 
    NSMutableArray * outsideArray = [[NSMutableArray alloc] init]; 
     
    void (^blockObject)(void) = ^(void){ 
        NSInteger insideVariable = 20; 
        KSLog(@"  > member variable = %d", self.memberVariable); 
        KSLog(@"  > outside variable = %d", outsideVariable); 
        KSLog(@"  > inside variable = %d", insideVariable); 
         
        [outsideArray addObject:@"AddedInsideBlock"]; 
    }; 
     
    outsideVariable = 30; 
    self.memberVariable = 30; 
 
    blockObject(); 
     
    KSLog(@"  > %d items in outsideArray", [outsideArray count]); 

输出结果: 
这里写图片描述

3>在使用之前最好判断是否为nil,否则极有可能引起崩溃

参考来源 
block注意事项及源代码 
http://blog.csdn.net/allison162004/article/details/22850749 
http://blog.csdn.net/fengsh998/article/details/38090205 
5.深入了解block 
以下这篇博客,写的这么这么深入也是没谁了!! 
http://blog.devtang.com/2013/07/28/a-look-inside-blocks/

  • A look inside blocks: Episode 1
  • A look inside blocks: Episode 2
  • A look inside blocks: Episode 3
  • 对 Objective-C 中 Block 的追探
  • LLVM 中 block 实现源码
  • objective-c-blocks-quiz
  • Blocks

__________________________________________________

6.面试的block问题

Block的定义:

    1> Block是OC中得一种数据类型,在iOS开发中被广泛使用.

    2> ^是Block的特有标记.

    3> Block的实现代码包含在{ }之间.

    4> 大多数情况下,以内联inline函数的方式被定义和使用.

    5> Block与C语言的指针有些相似,但使用更加灵活.

    内联函数从源代码层看,有函数的结构,而在编译后,却不具备函数的性质.编译时,类似宏替代,使用函数体替换调用处的函数名;

    Block封装了一段代码,可以在任何时候执行;

    Block可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或者返回值;

    苹果官方建议尽量多使用block.在多线程,异步任务,集合遍历,集合排序,动画转场用得很多.


1 什么是block

nt (^yxpBlock)(int, int) =^(int a ) {return a*a ;}; 

说明:返回值(^语句块名称)(传人参数类型)=^(传人参数){主体};


2 block 实现原理 
Objective-C是对C语言的扩展,block的实现是基于指针和函数指针。 
从计算语言的发展,最早的goto,高级语言的指针,到面向对象语言的block,从机器的思维,一步步接近人的思维,以方便开发人员更为高效、直接的描述出现实的逻辑(需求)。 
下面是两篇很好的介绍block实现的博文 
iOS中block实现的探究
谈Objective-C Block的实现


3 block的使用 
使用实例 
cocoaTouch框架下动画效果的Block的调用

使用typed声明block 
typedef void(^didFinishBlock) (NSObject *ob); 
这就声明了一个didFinishBlock类型的block, 
然后便可用 
@property (nonatomic,copy) didFinishBlock  finishBlock; 
声明一个blokc对象,注意对象属性设置为copy,接到block 参数时,便会自动复制一份。 

__block是一种特殊类型, 
使用该关键字声明的局部变量,可以被block所改变,并且其在原函数中的值会被改变。 


4 常见系列面试题 
面试时,面试官会先问一些,是否了解block,是否使用过block,这些问题相当于开场白,往往是下面一系列问题的开始,所以一定要如实根据自己的情况回答。 
1 使用block和使用delegate完成委托模式有什么优点? 
首先要了解什么是委托模式,委托模式在iOS中大量应用,其在设计模式中是适配器模式中的对象适配器,Objective-C中使用id类型指向一切对象,使委托模式更为简洁。了解委托模式的细节: 
 iOS设计模式----委托模式 
使用block实现委托模式,其优点是回调的block代码块定义在委托对象函数内部,使代码更为紧凑; 
适配对象不再需要实现具体某个protocol,代码更为简洁。 


2 多线程与block 
GCD与Block 
使用 dispatch_async 系列方法,可以以指定的方式执行block 
GCD编程实例

dispatch_async的完整定义 
   void dispatch_async( 
   dispatch_queue_t queue, 
   dispatch_block_t block); 
功能:在指定的队列里提交一个异步执行的block,不阻塞当前线程 

通过queue来控制block执行的线程。主线程执行前文定义的 finishBlock对象 
dispatch_async(dispatch_get_main_queue(),^(void){finishBlock();});  



使用代码块的例子

     第一个例子,假设我们创建一个纸牌游戏,需要展现纸牌被派发到玩家面前的动画效果。幸运的是通过UIKit框架可以很容易的实现一个动画效果。但是最终是什么样的动画是由你的程序决定的。你可以在代码块中指定动画的内容然后再将代码块传给animateWithDuration:animations:方法,像下面这样:

[UIView animateWithDuration:2.0

    animations:^ {

        self.cardView.alpha = 1.0;

        self.cardView.frame = CGRectMake(176.0, 258.0, 72.0, 96.0);

        self.cardView.transform = CGAffineTransformMakeRotation(M_PI);

    }
];

    当这个动画代码块执行时,我们的纸牌会展现三种方式的动画:改变它的alpha值从而淡入显示,改变它的位置到右下角(玩家的位置),以及自转180度(为了使其效果更好)。

第二个代码块的例子是迭代一个纸牌的集合,并打印其名字和在集合里的索引值。
你可以通过使用for循环来达到目的,但是在iOS4中NSArray类有一个使用了代码块的方便方法:enumerateObjectsUsingBlock:。下面是如何使用它:

NSArray *cards = [NSArray arrayWithObjects:@"Jack", @"Queen", @"King", @"Ace", nil];

[cards enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
    NSLog(@"%@ card at index %d", object, index);
}];

    这个代码块使用了三个参数:数组中的一个对象,该对象的索引,以及一个标识迭代是否结束的标志。我们稍候再对其进一步探讨。enumerateObjectsUsingBlock: 这个方法会将集合中的每一个元素传入相应的参数并调用代码块中的方法。 

0 0
原创粉丝点击