网络销售网站设置,企业怎么在网站上做宣传,网站首图怎么做,网站维护一般多长时间目录 Block的基本使用Block的声明Block的实现Block的调用 Block作为形参使用Block作为属性使用给Block起别名Block的copy Block的捕获机制auto类型的局部变量__block浅析static类型的局部变量全局变量 其他问题 Block的基本使用
什么是Block#xff1f; Block #xff08;块… 目录 Block的基本使用Block的声明Block的实现Block的调用 Block作为形参使用Block作为属性使用给Block起别名Block的copy Block的捕获机制auto类型的局部变量__block浅析static类型的局部变量全局变量 其他问题 Block的基本使用
什么是Block Block 块封装了函数调用以及调用环境的 OC 对象Objective-C闭包可以在内部访问外部的值相当于C语言的函数指针把一个函数写在一个函数内部而OC并没有函数方法嵌套这一语法 Block的声明
void(^blockName)();
int(^blockName2)(int a, int b, int c);格式 返回值 (^block名称)(形参列表) ^代表块的符号
Block的实现
无参数无返回值
void(^blockName)(void) ^{};有参数无返回值
void(^blockName)(int a, int b) ^(int a, int b){};无参数有返回值
int(^blockName)(void) ^int{return 3;
};实现部分的返回值可以省略像这样
int(^blockName)(void) ^{return 3;
};有参数有返回值
int(^blockName)(int a, int b) ^int(int a, int b){return 3 a * b;
};实现部分的返回值int同样可以省略
Block的调用
//无参数无返回值
blockName();//有参数有返回值
int result blockName(7, 12);现在已声明的blockName代表一个块那么调用这个块既可以通过blockName(7, 12);也可以这样
int result ^(int a, int b) {return 3 a b;
}(7, 12);Block作为形参使用
Block作为形式参数在方法中的声明与上述格式略有不同块的名称在外面
现在Jaxon类和Jacky类中分别实现以下方法 Jaxon.h
- (void)askJackyForHelp: (void(^)(int num))blockName isOK: (void(^)(BOOL boolValue))completion;Jaxon.m
- (void)askJackyForHelp:(void (^)(int))blockName isOK:(void (^)(BOOL))completion {blockName(3);//传入completion块的参数非1即0completion(arc4random() % 2);
}Jacky.m
- (void)helpDoWith: (int)num {NSLog(帮忙做事%d次, num);
}接下来在main函数中调用
Jaxon* jaxon [[Jaxon alloc] init];
[jaxon askJackyForHelp:^(int num) {Jacky* jacky [[Jacky alloc] init];[jacky helpDoWith: num];} isOK:^(BOOL boolValue) {//成功和失败的概率各占一半if (boolValue) {NSLog(帮忙成功);} else {NSLog(帮忙失败);}}];运行结果 这样是不是可以起到代理的作用Jaxon委托Jacky帮忙做事Jaxon实现不了的委托Jacky实现因此Block块也可以用于界面传值或其他需要使用代理模式的程序设计中
Block作为属性使用
给Block起别名
文章开头也提到了块其实也是一种对象可以将ta理解为一种数据类型
那么也可以用typedef关键字给Block起别名看以下示例
typedef void(^Help)(int num);
typedef void(^Finish)(BOOL boolValue);上面的方法也就可以这样声明
- (void)askJackyForHelp:(Help)blockName isOK:(Finish)completion;块的属性关键字一般需要是是copy
interface Jaxon : NSObject//无别名
property (nonatomic, copy)void(^helpBlock)(int num);
//有别名
//property (nonatomic, copy)Help helpBlock;
- (void)askMyselfDo;endimplementation Jaxon- (void)askMyselfDo {self.helpBlock(5);
}endmain函数
Jaxon* jaxon [[Jaxon alloc] init];
jaxon.helpBlock ^(int num) {NSLog(我自己做%次, (num));
};
[jaxon askMyselfDo];运行结果
Block的copy
关于copy关键字编者也简单了解一下底层原理以后再加以详细的剖析 在ARC 环境下编译器会根据情况自动将栈上的 block 复制到堆上比如以下几种情况 手动调用 block 的copy方法时 block 作为函数返回值时Masonry 框架中用很多将 block 赋值给__strong指针时block 作为 Cocoa API 中方法名含有usingBlock的方法参数时block 作为 GCD API 的方法参数时。 block 作为属性的写法 ARC下写strong或者copy都会对 block 进行强引用都会自动将 block 从栈 copy 到堆上 建议都写成copy这样 MRC 和 ARC 下一致。 Block刚创建时存放在栈区使用时copy到堆区
Block的捕获机制
为保证Block内部能正常访问到外部的变量Block有一种变量捕获机制
auto类型的局部变量
auto变量正常定义出来的变量默认都是auto类型只是省略了
auto int age 20;auto类型的局部变量会被捕获到block块内部访问方式为值传递
int age 10;
NSLog(%d %p, age, age);
void(^blockName)(void) ^ {NSLog(%d %p, age, age);
};
age 20;
//可以打印出来说明block块是可以访问到外部信息的
blockName();
NSLog(%d %p, age, age);根据运行结果可以得出以下两点
当auto类型的局部变量被捕获到block块内部时block内部会自动生成一个相同的成员变量用来存储这个变量的值因此打印的block外部的age地址与内部age地址不一样由于值传递修改外部age变量的值不会影响到block内部的变量 __block浅析
Block内部只能调用外部变量不能修改 Block 默认情况下是使用被捕获的外部变量的只读拷贝因此在 Block 内部无法直接修改外部变量的值
解决办法如下
变量用static修饰原因捕获static类型的局部变量是指针传递可以访问到该变量的内存地址全局变量__block我们只希望临时用一下这个变量临时改一下而已而改为 static 变量和全局变量会一直在内存中
当变量被__block修饰时block可以修改外部全局变量
__block int age 10;
NSLog(%d %p, age, age);
void(^blockName)(void) ^ {age 30;NSLog(%d %p, age, age);
};
blockName();
NSLog(%d %p, age, age);static类型的局部变量
static类型的局部变量会被捕获到block内部访问方式指针传递
static int age 10;
NSLog(%d %p, age, age);
void(^blockName)(void) ^ {NSLog(%d %p, age, age);
};
age 20;
blockName();
NSLog(%d %p, age, age);当static类型的局部变量被捕获到block内部时block块内部会生成一个相同类型的指针指向捕获到内部的age变量的地址由于指针传递修改外部的age变量的值会影响到block内部的age变量
全局变量
全局变量不会被捕获到block内部访问方式为直接访问
int _age 10;
static int _height 175;int main(int argc, const char * argv[]) {autoreleasepool {NSLog(%d %p, _age, _age);void(^blockName)(void) ^ {_age 30;NSLog(%d %p, _age, _age);};blockName();_age 20;NSLog(%d %p, _age, _age);}return 0;
}其他问题
对于对象类型的局部变量block会连同ta的所有权修饰符一起捕获
为什么局部变量需要捕获全局变量不用捕获呢
作用域的原因全局变量哪里都可以直接访问所以不用捕获局部变量外部不能直接访问所以需要捕获auto 类型的局部变量可能会销毁其内存会消失block 将来执行代码的时候不可能再去访问那块内存所以捕获其值static 变量会一直保存在内存中 所以捕获其地址即可