iOS Block
2016-04-07 09:29:11 ccflying88 阅读数 259
  • iOS开发之Objective-C(基础篇)

    该系列课程是iOS开发之Objective-C基础入门视频。课程中会详细的讲解OC语法特点,面向对象的使用,循环引用的问题,block的特点及使用,枚举与结构体,Foundation框架等。是针对初级的入门iOS开发的优质视频。

    809 人正在学习 去看看 李飞

block语句,形式上看上去和函数没区别,本质上和宏差不多;

block格式:返回值类型 (^block名称) (参数列表);

block有以下几种定义方式:
1.直接定义在方法中,或者也可以直接定义成一个方法;

void(^testBlock)() = ^{
    printf("testBlock\n");
};

testBlock();  // testBlock;

2.通过typedef定义成一个类型;

typedef int (^SumBlock)(int, int);
// 调用
SumBlock sum = ^(int num1, int num2){
    return num1 + num2;
};
int result = sum(10, 20);  // result = 30;

在xcodec中,输入inlineBlock快速插入一条block语句;

3.可以通过typedef定义一个block类型,然后把该类型作为类的一个属性来保存类的一些操作;

typedef void(^OptionBlock)();

@interface XxObject:NSObject
{
    @property (nonatomic, copy) OptionBlock option;  // 定义属性保存辅助操作;
}

注意:
在block语句中,如果有需要用到宿主(如Controller)的地方,比如一个代理delegate,不能直接传self,这样会造成循环引用;

解决办法:定义一个weak的变量来保存对当前对象的引用;

__weak_unretained XXObject *obj = self;  // 或者
__weak XXObject *obj = self;  // _weak 当对象释放之后会自动设置为nil,而上一种方式则不会,通常用此方式;

通过 __weak 这种方式定义的一个变量,不能再调用它的属性,比如obj.age = 10; // 此行代码相当于self->_age = 10;

上面第二种方式通常书写成:__weak typeof(self) unsafeSelf = self;

2019-04-10 15:00:01 weixin_39950838 阅读数 408
  • iOS开发之Objective-C(基础篇)

    该系列课程是iOS开发之Objective-C基础入门视频。课程中会详细的讲解OC语法特点,面向对象的使用,循环引用的问题,block的特点及使用,枚举与结构体,Foundation框架等。是针对初级的入门iOS开发的优质视频。

    809 人正在学习 去看看 李飞

概述

Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block也是一种数据类型,它可以定义变量、做参数、作为返回值, Block是代码块,它可以保存一段代码,并且在需要的时候再调用.在iOS开发中,Block常被用于GCD、动画、排序、传值等的各类回调.

1.定义一个Block
	typedef 返回值类型(^block名称)(参数列表);
	//例如:
	//无返回值无参数
	void(^block)(void);
	//无返回值有参数
	void(^block)(NSString *params);
	//有返回值有参数
	int(^block)(NSString *params1,NSString *params2);//形参也可省略
2.赋值和调用
//声明
typedef void(^block)(NSString *params);
//属性
@property (nonatomic ,copy) block blockname;
//赋值
self.blockname = ^(NSString *params) {
    NSLog(@"%@",params);
};

//调用
self.blockname(@"我是Block");

Block赋值只是保存了这样一段代码,只有我们调用Block之后他才会执行保存的这样一段代码,通常block的声明和调用在一个类,赋值是在另一个类,然后完成两个类之间的传值或者其他逻辑关系.

3.Block内部访问变量
  • 1.Block访问外部变量的问题:

    1.Block代码块内部可以声明一个与外部变量名称相同的变量,在Block内部访问时,遵循就近原则.
    2.Block代码块内,可以取到全局变量和局部变量的值;
    3.在Block代码块内可以修改全局变量的值,但是不能修改局部变量的值;
    4.如果需要修改Block代码块外的局部变量的值,就要给这个局部变量使用__block修饰符.
    5.__block的其实就是将局部变量, 拷贝到了堆区, 所以作用域结束, 实际上block修改的只是堆区的备份.

    为什么系统不给Block内部修改栈区局部变量

    Block执行的时机并不仅仅是在局部变量生命周期的那段作用域中, 因为Block也是一个数据类型, 他可以被当做参数传递, 所以执行时, 可能局部变量已经不存在了, 因为栈区的数据, 在过其作用域就被销毁了.

4.block的存储位置

Block内部的代码决定了Block代码存在的区域

  • 不访问外部变量:存储在全局区(执行的代码存储在代码段);

  • 访问外部变量:

      MRC下: 栈区
      MAR+copy或ARC下:都存储在堆区(连执行的代码都存储在堆区,不存储在代码段)
    
    block为什么用copy, 因为在MRC下, 不用copy, 拥有局部变量的代码, 存储在栈区, 当作用域结束, block也因为作用域的原因, 被弹栈了, 也就是被销毁了. 如果使用了copy, 那么block就被copy 到堆区了, 所以MRC下使用copy, 可以解决此问题, ARC下自动copy到堆区, 不使用copy也行, 所以也可以使用strong修饰.
5.block的循环引用

block作为属性时,被类强持有,在block其内部又使用持有它的类时会造成循环引用,内存泄露.

//造成循环引用
self.blockname = ^(NSString *params) {
    NSLog(@"我是block-----%@",self);
};
//解决:
//1.ARC下使用 __weak
__weak typeof(self) weakSelf = self;
self.blockname = ^(NSString *params) {
	NSLog(@"我是block-----%@",weakSelf);
};

//2.MRC下使用 __block
__block typeof(self) blockSelf = self;
self.someBlock = ^(NSString *params) {
	NSLog(@"我是block-----%@",blockSelf);
};

block底层原理

IOS block
2014-10-27 09:22:48 investzhu 阅读数 372
  • iOS开发之Objective-C(基础篇)

    该系列课程是iOS开发之Objective-C基础入门视频。课程中会详细的讲解OC语法特点,面向对象的使用,循环引用的问题,block的特点及使用,枚举与结构体,Foundation框架等。是针对初级的入门iOS开发的优质视频。

    809 人正在学习 去看看 李飞
block 是一种数据类型: 封装了一段代码  (简单的可以认为)

block 在iOS 中的使用场景

1  多线程
2  网络请求的回调
3  动画
4  遍历集合 (数组,字典)
———————————————————————————————————————————————————————



当 block 用copy定义的时候, 会从栈内存移到堆内存里面, 会对对象形成循环引用, 当P消失时, 对象无法释放
 


只要Block 没有copy  就一直在栈内存中的(栈里面的内容会随着大括号的消失时而消失)




ARC中block的循环引用



MRC中的block的循环引用








用 _ _block 定义以后, 传入的是地址 @age, 所以打印的是 20

2017-09-10 18:04:02 Mo_Mo123 阅读数 1569
  • iOS开发之Objective-C(基础篇)

    该系列课程是iOS开发之Objective-C基础入门视频。课程中会详细的讲解OC语法特点,面向对象的使用,循环引用的问题,block的特点及使用,枚举与结构体,Foundation框架等。是针对初级的入门iOS开发的优质视频。

    809 人正在学习 去看看 李飞

前言

先说明一下,因为ARC下系统会对block做一些拷贝和释放操作,对深入于理解block无益,所以本篇文章所提到的栗子编译环境均为MRC。

首先引用《Objective-C高级编程》Blocks章节中的第一句话:Blocks是对C语言的扩充功能。而且OC是建立在C语言基础上之上,添加了面向对象机制的一门编程语言。

所以不要再说block的实现原理是C++的函数指针了,正确答案是:block的实现原理是C语言的函数指针。
函数指针即函数在内存中的地址,通过这个地址可以达到调用函数的目的。

Block是NSObject的子类,拥有NSObject的所有属性,所以block对象也有自己的生命周期,生存期间也会被持有和释放。


block有三种:

NSGlobalBlock 静态区(全局区)block,这是一种特殊的bloclk,因为不引用外部变量而存在。另外,作为静态区的对象,它的释放是有操作系统控制的,这一点我们最后再聊。
NSStackBlock 栈区block,位于内存的栈区,一般作为函数的参数出现。
NSMallocBlock 堆区block,位于内存的堆区,一般作为对象的property出现。

如果一个blcok引用了外部变量是栈block,则其不引用外部变量就成为了静态blcok。
如果一个block引用了外部变量是堆block,则其不引用外部变量就成为了静态block。


NSStackBlock 栈区block

说到栈操作,大家都明白,出栈和入栈,函数只有入栈后才能执行,出栈后就释放了。
栈block一般在函数内部定义,并在函数内部调用;或者在函数外部定义,作为函数的一个参数在函数内部调用。函数出栈时和其他变量或参数一起释放。

栈区block形式有2,如下:

栈区block形式1

- (void)xxx
{
    __block int i = 0;
    void (^block1)() = ^{
        //此处若不引用外部变量i,则block1是静态block,若引用,则为栈block
        i++;
        NSLog(@"i = %d",i);
    };

    block1();
}

栈区block形式2:

- (void)saveFile:(NSDictionary *)dic complete:(void(^)(BOOL success))complete
{
    //dic写入本地近作demo使用,实际开发要判断路径是否存在,以及dic中是否存在null值等
    NSString    * path = [NSHomeDirectory() stringByAppendingFormat:@"/Caches/dicInfo"];
    BOOL suc = [dic writeToFile:path atomically:YES];

    if (complete) {
        //若complete中不引用外部变量suc,则complete是静态block,此处complete为栈block
        complete (suc);
    }
}

NSMallocBlock 堆区block

堆区是内存的常驻区域,也叫永久存储区,block一般在函数中定义,最多是个栈block,什么时候才能打怪升级成为堆block呢?
在MRC时代你需要使用Block_copy()方法,才可以将blcok复制到堆中。

然而复制到堆中有何用处呢?
首先,作为一个对象,把它复制到堆中,想要使用它肯定要有一个指针指向它,而指向它的指针是作为property或静态变量出现的(如果不被引用也就没有了常驻于堆区的意义),而实际开发中多使用property引用。

在MRC中,如果一个类的block属性是使用copy修饰的,则不需要手动调用Block_copy将其复制到堆中。如果是用strong修饰的,则必须使用Block_copy()将其复制到堆中,并在释放时调用Block_release()方法将其释放,否则会因野指针导致程序崩溃。

@interface TestV : UIView

@property (nonatomic, strong) void(^block1)();
@property (nonatomic, copy) void(^block2)();

@end
- (void)testFunc
{
    __block int i = 0;

    void (^block1)() = ^{
        //此处若不引用外部变量i,则block1是静态block,若引用,则为堆block
        i++;
        NSLog(@"block1 -- i = %d",i);
    };
    void (^block2)() = ^{
        i --;
        NSLog(@"block2 -- i = %d",i);
    };

    TestV * view = [[TestV alloc] initWithFrame:CGRectMake(100, 100, kScreenWidth - 200, 40)];
    [self.view addSubview:view];

    view.block1 = Block_copy(block1);
    view.block2 = block2;

    NSLog(@"block1 -- %@",block1);
    NSLog(@"block2 -- %@",block2);
}

因此在MRC中,基本上会用copy关键字修饰block,既优雅又省代码,还避免了因为未调用Block_release而造成内存泄露,这大概是很多人在ARC项目中依旧使用copy修饰block的历史原因。

其实在ARC下使用strong或copy修饰block没啥区别,亲测有效,可能会遇到一些开发人员,他们看到ARC工程中出现strong修饰的block就会提出质疑,那只能说明他们没有亲自写两行代码测试过是否真的有区别。


NSGlobalBlock 静态block

既然存在于静态区,则只有当进程被杀死,进程所占用的内存空间被释放后,静态区的对象才会被释放。静态block并不依附于某个对象而存在,也并不为某个类对象单独享有,而是所有的该类对象都共同拥有这个block,都有这个block的使用权,不管有多少个类对象,在程序运行期间这个block始终只有一份,直到进程被杀死,内存被释放。

以下代码片段将有助于你的理解:

//在一个成员方法中定义一个静态block,然后打印出self的地址和block的地址
- (void)blockAction
{
    void(^block1)() = ^{
        //未引用外部变量,则为静态block
        NSLog(@"block1");
    };

    NSLog(@" -- self : %p",self);
    NSLog(@" -- %@ --",block1);
}
//初始化了两个对象,同时调用各自的成员方法
    [_v1 blockAction];
    [_v2 blockAction];
//打印结果如下:两个对象地址不同,两个block地址相同
xxx BlockDemo[22106:1969807]  -- self : 0x7f81da513f20
xxx BlockDemo[22106:1969807]  -- <__NSGlobalBlock__: 0x1057ca1a0> --
xxx BlockDemo[22106:1969807]  -- self : 0x7f81da512770
xxx BlockDemo[22106:1969807]  -- <__NSGlobalBlock__: 0x1057ca1a0> --

下一篇博文《block之循环引用》,我们将会讲block与循环引用。

以上观点均为个人研究、理解,如有不妥之处,欢迎指正,谢谢!

2015-07-14 07:24:31 Judy_luo 阅读数 315
  • iOS开发之Objective-C(基础篇)

    该系列课程是iOS开发之Objective-C基础入门视频。课程中会详细的讲解OC语法特点,面向对象的使用,循环引用的问题,block的特点及使用,枚举与结构体,Foundation框架等。是针对初级的入门iOS开发的优质视频。

    809 人正在学习 去看看 李飞

IOS中Block用法介绍


1 什么是block

Block iOS4.0之后新增的程式语法,严格来说block的概念并不算是基础程式设计的范围.BlockC级别的语法和运行时特性。Block比较类似C函数,但是Block比之C函数,其灵活性体现在栈内存、堆内存的引用,我们甚至可以将一个Block作为参数传给其他的函数或者Block

引用网上例子:

  1: int multiplier = 7 ;

   2: int (^myBlock)( int ) = ^( int num)

   3: {

   4:     return num * multiplier;

   5: };


语法介绍:

我们使用「^」运算子来宣告一个block变数,而且在block的定义最后面要加上「;」来表示一个完整的述句.

我们宣告一个「myBlock」变数,用「^」符号来表示这是一个block

这是block的完整定义,这个定义将会指定给「myBlock」变数。

表示「myBlock」是一个回传值为整数(int)的block

它有一个参数,型态也是整数。

这个参数的名字叫做「num」。

这是block的内容。


  1. typedef void (^BoolBlock)(BOOL);//一个只接受一个BOOL参数,没有返回值的block 
  2. typedef int (^IntBlock)(void);//一个没有参数,返回intblock 
  3. typedef BoolBlock (^HugeBlock)(IntBlock);//看看,这个HugeBlock的参数和返回值都是block 

  1. - (void)someMethod 
  2.     BoolBlock ablock = ^(BOOL bValue) { 
  3.         NSLog(@"Bool block!"); 
  4.     }; 
  5.     ablock(); 


 __block 变量:

block内只能读取在同一个作用域的变数而且没有办法修改在block外定义的任何变数,此时若我们想要这些变数能够在block中被修改,就必须在前面挂上__block的修饰词,


1: __block int multiplier = 7 ;

   2: int (^myBlock)( int ) = ^( int num)

   3:                         {

   4:                             if (num > 5 )

   5:                             {

   6:                                   multiplier = 7 ;

   7:                             }

   8:                             else

   9:                             {

  10:                                   multiplier = 10 ;

  11:                             }

  12:                             return num * multiplier;

  13:                         };

 

Block 概要

 

Block 提供我们一种能够将函数程式码内嵌在一般述句中的方法,在其他语言中也有类似的概念称做「closure」,但是为了配合Objective-C的贯例,我们一律将这种用法称为「block

Block 的功能

Block 是一种具有匿名功能的内嵌函数,它的特性如下:

如一般的函数般能拥有带有型态的参数。

拥有回传值。

可以撷取被定义的词法作用域(lexical scope)状态。

可以选择性地修改词法作用域的状态。

注:词法作用域(lexical scope)可以想像成是某个函数两个大括号中间的区块,这个区块在程式执行时,系统会将这个区块放入堆叠记忆体中,在这个区块中的宣告的变数就像是我们常听到的区域变数,当我们说block可以撷取同一词法作用域的状态时可以想像block变数和其他区域变数是同一个层级的区域变数(位于同一层的堆叠里),而block的内容可以读取到和他同一层级的其他区域变数。

我们可以拷贝一个block,也可以将它丢到其他的执行绪中使用,基本上虽然blockiOS程式开发中可以使用在C/C++开发的程式片段,也可以在Objective-C中使用,不过在系统的定义上,block永远会被视为是一个Objective-C的物件。


Block 的使用时机

Block 一般是用来表示、简化一小段的程式码,它特别适合用来建立一些同步执行的程式片段、封装一些小型的工作或是用来做为某一个工作完成时的回传呼叫(callback

在新的iOS APIblock被大量用来取代传统的delegatecallback,而新的API会大量使用block主要是基于以下两个原因:

可以直接在程式码中撰写等会要接着执行的程式,直接将程式码变成函数的参数传入函数中,这是新API最常使用block的地方。

可以存取区域变数,在传统的callback实作时,若想要存取区域变数得将变数封装成结构才能使用,而block则是可以很方便地直接存取区域变数。


宣告和建立Block

 

宣告Block的参考(Reference

Block 变数储存的是一个block的参考,我们使用类似宣告指标的方式来宣告,不同的是这时block变数指到的地方是一个函数,而指标使用的是「*」,block则是使用「^」来宣告,下面是一些合法的block宣告:

   1: /* 回传void ,参数也是void 的block*/

    2: void (^blockReturningVoidWithVoidArgument)( void );

   3: /* 回传整数,两个参数分别是整数和字元型态的block*/

   4: int   (^blockReturningIntWithIntAndCharArguments)( int , char );

   5: /* 回传void ,含有10 个block 的阵列,每个block 都有一个型态为整数的参数*/

   6: void (^arrayOfTenBlocksReturningVoidWinIntArgument[ 10 ])( int );

   7: X.3.2 建立一个Block 

   9: 我们使用「^」来开始一个block,并在最后使用「;」来表示结束,下面的范例示范了一个block变数,然后再定义一个block把它指定给block变数: 

  10:  

  11: int (^oneFrom)( int ); /* 宣告block 变数*/

  12:     /* 定义block 的内容并指定给上面宣告的变数*/

  13:     oneFrom = ^(int anInt)

  14:                 {

  15:                     return anInt = - 1 ; 

  16:                 };



Block当作函数的参数

我们可以像使用一般函数使用参数的方式,将block以函数参数的型式传入函数中,在这种情况下,大多数我们使用block的方式将不会倾向宣告block而是直接以内嵌的方式来将block传入,这也是目前新版SDK中主流的做法,我们将补充前面章节的例子来说明:

   1: char *myCharacters[ 3 ] = { "TomJohn" , "George" , "Charles Condomine" };

   2: qsort_b (myCharacters, 3 , sizeof ( char *),

   3:             ^( const void *l, const void *r)

   4:             {

   5: char *left = *( char **)l;

   6: char *right = *( char **)r;

   7: return strncmp (left, right, 1 );

   8:             } // 这里是block 的终点。

   9:             );

  10: // 最后的结果为:{"Charles Condomine", "George", "TomJohn"}

在上面的例子中,block本身就是函数参数的一部分,在下一个例子中dispatch_apply函数中使用blockdispatch_apply的定义如下:

    1: void

   2: dispatch_apply( size_t iterations, dispatch_queue_t queue, void (^block)( size_t ));

   3: 这个函数将一个block提交到发送伫列(dispatch queue)中来执行多重的呼叫,只有当伫列中的工作都执行完成后才会回传,这个函数拥有三个变数,而最后一个参数就是block ,请参考下面的范例: 

   4:  

   5: size_t count = 10 ;

   6: dispatch_queue_t queue =

   7: dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 );

   8: dispatch_apply (count, queue, ^( size_t i) {

   9: printf ( "%u\n" , i);

  10:     });

Block当作方法的参数

SDK中提供了许多使用block的方法,我们可以像传递一般参数的方式来传递block,下面这个范例示范如何在一个阵列的前5笔资料中取出我们想要的资料的索引值:

   1: // 所有的资料

   2: NSArray *array = [ NSArray arrayWithObjects : @"A" , @"B" , @"C" , @"A" , @"B" , @"Z" , @"G" , @"are" , @" Q" ,nil ];   

   3: // 我们只要这个集合内的资料

   4: NSSet *filterSet = [ NSSet setWithObjects : @"A" , @"B" , @"Z" , @"Q" , nil ];

   5: BOOL (^test)( id obj, NSUInteger idx, BOOL *stop);

   6: test = ^ ( id obj, NSUInteger idx, BOOL *stop) {

   7: // 只对前5 笔资料做检查

   8: if (idx < 5 ) {

   9: if ([filterSet containsObject : obj]) {

  10: return YES ;

  11:               }

  12:       }

  13: return NO ;

  14: };

  15: NSIndexSet *indexes = [array indexesOfObjectsPassingTest :test];

  16: NSLog ( @"indexes: %@" , indexes);   

  17: // 结果:indexes: <NSIndexSet: 0x6101ff0>[number of indexes: 4 (in 2 ranges), indexes: (0-1 3-4)]

  18: // 前5笔资料中,有4笔符合条件,它们的索引值分别是0-1, 3-4




iOS BLOCK

阅读数 528

iOS block浅析

阅读数 349

iOS block详解学习

阅读数 146

iOS block

博文 来自: mylijianke
没有更多推荐了,返回首页