IOS开发入门iOS Blocks 编程(一)
白羽 2019-04-16 来源 :网络 阅读 451 评论 0

摘要:本文将带你了解IOS开发入门iOS Blocks 编程(一),希望本文对大家学物联网有所帮助。

    本文将带你了解IOS开发入门iOS Blocks 编程(一),希望本文对大家学物联网有所帮助。


IOS开发入门iOS Blocks 编程(一)

先瞧瞧几个例子:
   Example 1(Block在动画方面的应用):
   daysView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);[UIView  animateWithDuration:0.2f delay:0.0f options:UIViewAnimationCurveEaseInOut  animations:^{     daysView.alpha = 1.0f;    daysView.transform  = CGAffineTransformMakeScale(1.05f, 1.05f);     }  completion:^(BOOL finished)  {         [UIView  animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut  animations:^{        daysView.transform  =  CGAffineTransformIdentity;        daysView.frame  = CGRectMake(point.x - 150.0/2, point.y + 10.0, 150,  100);     } completion:nil];}];
   Example 2(Block在多线程方面的应用):
   dispatch_queue_t concurrentQueue =  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,  0);dispatch_async(concurrentQueue, ^{     __block  UIImage *image =  nil;     dispatch_sync(concurrentQueue,  ^{         /* Download the image  here */     });    dispatch_sync(dispatch_get_main_queue(),  ^{         /* Show the image to  the user here on the main queue*/     });});
   Example 3(Block在单例方面的应用):
   +  (id)sharedInstance{      static id  sharedInstance;    static dispatch_once_t  once;     dispatch_once(&once,  ^{         sharedInstance =  [[[self class] alloc]  init];     });     return  sharedInstance;}
   Example 4(Block在网络请求方面的应用):
   __block MKNetworkOperation *op = [engine operationWithURLString:urlString  params:dic httpMethod:@"POST"];[op  addCompletionHandler:^(MKNetworkOperation *completedOperation)  {     NSString *responseString = [completedOperation  responseString];    NSLog(@"communication  requestFinished");    completeCallBack(responseString);     }  errorHandler:^(MKNetworkOperation *completedOperation, NSError *error)  {     NSLog(@"error:%@",error);    completeCallBack(kHttpRequestError); }]; [engine  enqueueOperation:op];
   What
   iOS4引入了一个新特性,block(也称块代码)。
   block可以简单看做是一组可执行的代码,block能够捕捉到已声明的同一作用域的变量。
   block也是闭包,在代码块声明时就将使用的变量包含到代码块范围内。
   block还是Objective-C对象,我们可以像对象一样传递它们。
   Block可以有返回值,可以接受参数。还可以内敛定义,当做独立代码块,与传统C函数类似。
   Why
   block普遍被认为是实现Callback的一种更为方便的模式,相比Delegate+Protocol需要声明和实现一大推方法而言,block表现得更为灵活。
   它们可以让你在调用的地方编写代码实现后面将要执行的操作。因此blocks通常作为框架方法的参数。
   它们允许你访问局部变量。而不需要集成所有上下文的信息的数据结构来进行回调,你可以简单的访问局部变量。
   Block编写函数表达式,这些表达式可以作为API使用,或可选的存储,或被多个线程使用。
   iOS4在UIKit中引入了该特性,此后越来越多的Apple API都使用了block,所以我们应该了解这一知识。
   How
   1、Block的写法
   假设你在Objective-C中有个方法,既能接受NSInteger类的2个整数值,又能通过两个相减返回两者的差值作为NSInteger(可以这么写):
   ?1234-  (NSInteger) subtract:(NSInteger)paramValue  from:(NSInteger)paramFrom{     return paramFrom -  paramValue; }
   把Objective-C代码翻译成纯粹C函数变成:
   ?1234NSInteger subtract(NSInteger paramValue, NSInteger paramFrom){       return paramFrom - paramValue; }
   如果是代码块又该如何编码?
   ?1234NSInteger (^subtract)(NSInteger, NSInteger) = ^(NSInteger paramValue,  NSInteger paramFrom){      return paramFrom -  paramValue; };
   
   2、各种不同情况的Block
   普通情况下使用block
   NSInteger count = 10;NSInteger (^theBlock)(NSInteger) = ^(NSInteger  num){     return count * num;}; NSLog(@"count  * num: %d",theBlock(10));
   结果输出:BlockTest[1037:11303] count * num: 100
   那么,
   1、block可以使用相同作用域范围内定义的变量。
   2、如果你声明一个block作为变量,可以把它当成函数来使用。
   一个内联(inline)的block
   dispatch_queue_t mainQueue = dispatch_get_main_queue();dispatch_async(mainQueue,  ^(void) {     [[[[UIAlertView alloc]  initWithTitle:@"Inline-Block"                                message:@"Block  is  amazing!"                               delegate:nil                      cancelButtonTitle:@"OK"                      otherButtonTitles:nil,nil]  autorelease] show];});
   那么,
   1、很多情况下,你不需要去声明一个block变量,可以简单的写一个内联block。
   2、可以看到dispatch_async函数的最后一个参数为block。
   在Cocoa frameworks下的block
   NSDictionary *femaleStars = [NSDictionary  dictionaryWithObjectsAndKeys:@"Fan  BB",@"First",@"Liu  SS",@"second",@"Liu Yifei",@"Third",@"Lin  Zhiling",@"fourth", nil]; [femaleStars  enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop)  {     NSLog(@"key : %@ , value :  %@",key,obj);}];
   结果输出:
   BlockTest[1760:11303] key : First , value : Fan  BBBlockTest[1760:11303] key : second , value : Liu SSBlockTest[1760:11303]  key : Third , value : Liu YifeiBlockTest[1760:11303] key : fourth , value :  Lin Zhiling
   那么,
   1、在 Cocoa frameworks 里面有部分方法使用 block 作为参数
   2、一般是在操作完成的时候作为回调使用
   3、声明和创建block
   Block的声明和函数指针的声明类似,block使用^修饰符代替*修饰符,block类型可完全操作其他C系统类型:
   void (^blockReturningVoidWithVoidArgument)(void); int  (^blockReturningIntWithIntAndCharArguments)(int, char); void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);
   Blocks被设计成类型安全的,你可以把一个block引用强制转换成任意类型的指针。
   在文件级别,你可以把 block 作为全局标示符:
   #import  int GlobalInt = 0; int  (^getGlobalInt)(void) = ^{ return GlobalInt; };
   ———
   + (void)doSomethingWithIndex:(NSInteger)index  block:(NSInteger(^)(NSInteger,NSInteger))block;
   第一眼看上去,不容易理解。
   所以为了提高可读性和避免在.h和.m中出现重复,使用typedef修改。
   typedef NSInteger(^MyJob)(NSInteger,NSInteger);
   + (void)doSomethingWithIndex:(NSInteger)index block:(MyJob)block;
   类方法的具体实现:
   + (void)doSomethingWithIndex:(NSInteger)index block:(MyJob)block{
       NSInteger result =  0;    for (int i = 0 ; i < index; i++)  {         result =  block(i,pow(10, i)) +  result;    }    NSLog(@"result:%d",result);}
   Block的创建和实现:
   [NDBlockTest doSomethingWithIndex:3 block:^(NSInteger num1, NSInteger  num2) {     return num1 + num2;}];
   4、Block与变量的关系
   全局变量可访问,包括在相同作用域范围内的静态变量。
   传递给 block 的参数可访问。
   属于同一作用域范围的堆(非静态的)变量作为const变量(即只读)。
   属于同一作用域范围内并被__block 存储修饰符标识的变量作为引用传递是可变的。
   同一作用域范围内给定的多个 block 可以同时使用一个共享变量。
   在独立的块代码中,你不能直接访问self,如果想访问,把Object作为参数传递给块代码。
   在独立的块代码中,你不能使用dot notatiion(点表达式)读写一个已声明属性。
   在内联block中,有一条重要规则:内联block在其方法作用域范围内会为这些变量复制值。
   全局变量可访问,包括在相同作用域范围内的静态变量。
   传递给 block 的参数可访问。
   const int number = 10; +(void)blockTest4  {     int x = 123;    void  (^printXAndY)(int) = ^(int y)  {         printf("result :  %d %d %d\n", x, y,  number);    };    printXAndY(100);}
   输出结果:result : 123 100 10
   属于同一作用域范围的堆(非静态的)变量作为const变量(即只读)。
   属于同一作用域范围内并被__block 存储修饰符标识的变量作为引用传递是可变的。
   同一作用域范 围内给定的多个 block 可以同时使用一个共享变量。
  int x = 123;void (^printXAndY)(int) = ^(int y)  {     x = x +  y;//error    printf("result : %d %d %d\n", x,  y, number);};printXAndY(100);
   ———
   __block int x = 123;void (^printXAndY)(int) = ^(int y)  {     x = x +  y;//true    printf("result : %d %d %d\n", x, y,  number);};printXAndY(100);
   在独立的块代码中,你不能直接访问self,如果想访问,把Object作为参数传递给块代码。
   在独立的块代码中,你不能使用点表达式读写一个已声明属性。
   void (^viewTest1)(NSInteger) = ^(NSInteger num)  {     self.editing = YES;//error}; void  (^viewTest2)(id) = ^(id self) {     [self.view  setBackgroundColor:[UIColor redColor]];//error}; void (^viewTest3)(id) =  ^(id self) {     [[self view]  setBackgroundColor:[UIColor redColor]];//true};
   在内联block中,有一条重要规则:内联block在其方法作用域范围内会为这些变量复制值。
   - (void)scopeTest {
       NSUInteger integerValue =  10;    BlockWithNoParams myBlock =  ^{        NSLog(@"Integer value  inside the block = %lu", (unsigned long)integerValue);    };    integerValue  =  20;    myBlock();    NSLog(@"Integer  value outside the block = %lu",(unsigned long)integerValue);}
   输出结果为:
   ?12Integer value inside the block = 10Integer value outside the block =  20
   除非用存储类__block 前缀限定,否则Block Object 只会把方法作用域的局部变量传作为只读变量传递给Block  Object。
   5、Block的使用
   如果你声明一个block作为变量,可以把它当成函数来使用。
   你可以把一个 block 作为函数的参数就像其他任何参数那样。然而在很多情况下, 你不需要声明  blocks;相反你只要简单在需要它们作为参数的地方内联实现它们。
   Cocoa 提供了一系列使用 block 的方法。你可以把一个block 作为方法的参数就像其他参数那样。
   copy相关
   作为一种优化,block 存储在栈上面,就像blocks 本身一样。如果使用Block_copy拷贝了 block 的一个副本(或者在  Objective-C 里面给 block 发送了一条 copy 消息), 变量会被拷贝到堆上面。所以一个__block 变量的地址可以随时间推移而被更改。
   当你拷贝一个 block 时,任何在该 block 里面对其他 blocks 的引用都会在需要的时候被拷贝,即拷贝整个目录树(从顶部开始)。如果你有  block 变量A并在该 block 变量A里面引用其他的 block 变量B,那么那个 block 变量B会被拷贝一份。
   当你拷贝一个基于栈的 block 时,你会获得一个新的 block 。但是如果你拷贝一个基于堆的 block ,你只是简单的递增了该 block  的引用数,并把原始的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) —— 除非是在垃圾回收的环境里面。
   编写返回代码块的方法
   有时我们会需要编写一个返回块代码的方法,如:
  + (ComputationBlock)raisedToPower:(int)y  {    ComputationBlock block = ^(int x)  {        return (int)pow(x,  y);    };    return block;  //  Don't do this!}
   但是当我们运行它时,会得到运行时错误”EXC_BAD_ACCESS”。
   解决这个问题的关键是了解代码块是怎么分配内存的。代码块的生命周期是在栈中开始的,因为在栈中分配内存是比较快的。是栈变量也就意味着它从栈中弹出后就会被销毁。方法返回结果就会发生这样的情况。解决办法是在返回之前将代码块从栈中移到堆中。
   + (ComputationBlock)raisedToPower:(int)y  {    ComputationBlock block = ^(int x)  {        return (int)pow(x,  y);    };    return [[block copy]  autorelease];}
   6、使用Block简化回调
   使用 Block 最大的便利就是简化的回调过程。以前使用UIView 的动画,进程要控制动画结束后进行相应的处理。iOS 4.0 之后,UIView  新增了对 Block 的支持,现在只要使用简单的一个Block代码就可以在写动画的代码部分直接添加动画结束后的操作。还有就是在使用  Notification 时候 Block 也非常有帮助。
   Block 代替delegate的简单实现:
   //www.cocoachina.com/bbs/job.php?action=download&aid=51512
   具体可以看github的一个demo,
   https://github.com/zhangxigithub/BlockUI
   7、Block内存相关
   在引用计数的环境里面,
   1、默认情况下当你在Block里面引用一个Objective-C对象的时候,该对象会被retain;
   2、当你简单的引用了一个对象的实例变量时,它也会被retain。
   因为这样,你经常会添加__block存储类型修饰符标识,这样做对象的变量才不会被retain。
   如果你在实现方法的时候使用了block,则你必须遵循:
   1、如果你通过引用来访问一个实例变量,self会被retain。
   2、如果你通过值来访问一个实例变量,那么变量会被retain。
   最后,按照喵神的说法就是,如果你使用ARC,那就不要管内存的情况了。   


以上就介绍了物联网的相关知识,希望对物联网有兴趣的朋友有所帮助。了解更多内容,请关注职坐标人工智能之物联网频道!

本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程