Block基本用法
1.作为方法时
- (void)testGlobalBlock:(NSString*) url
success:(void(^)(BOOLisSuccess))success
failure:(void(^)(BOOLisSuccess))failure;
2.作为成员变量进行声明时
typedefvoid(^SUCCESSBLOCK)(BOOLisSuccess);
typedefvoid(^FAILEDBLOCK)(BOOLisSuccess);
@interfaceXXEngine()
@property(nonatomic,copy) SUCCESSBLOCK successBlock;
@property(nonatomic,copy) FAILEDBLOCK failedBlock;
@end
3.写在函数内部
void(^_block)() = ^{
_a =10;
};
内存五个区认识
这里先普及一个基本的5个概念,内存的5个区。否则在具体讲堆栈的时候怕大家会不理解。
作用名称
栈区(stack)由编译器自动分配释放,存放函数的参数值,局部变量的值等
堆区(heap)一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收
全局区(静态区)(static)全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放
文字常量区常量字符串就是放在这里的。程序结束后由系统释放
程序代码区存放函数体的二进制代码
使用场景
任务完成时回调处理
消息监听回调处理
错误回调处理
枚举回调
视图动画、变换
排序
Block原理
Block三种类型
作用名称
NSConcreteGlobalBlock全局静态,不访问外部变量的时候就是NSConcreteGlobalBlock
NSConcreteStackBlock保存在栈中,出花括号会被销毁
NSConcreteMallocBlock保存在堆中的block,当引用计数为0的时候会被销毁
ARC下判断原则
造成循环引用的实例
@implementationPerson
{
int_a;
void(^_block)();
}
- (void)test
{
void(^_block)() = ^{
_a =10;
};}
@end
此时_a在用clang编译后可以很明显看到有会self的身影,所以此时我们可以将其理解成self.a(便于记忆),此时Person持有block,block持有self,造成了循环引用
解决方式
- (void)test
{
__weaktypeof(self) weakSelf =self;
void(^_block)() = ^{
weakSelf->a =10;
};}
经验谈之GCD/UIView的系统动画的Block中,为何可以写self?
因为self并没有对其进行持有,循环引用的原理是两个对象之间相互有持有关系,现在仅仅是GCD持有self,但是self并没有持有GCD,所以是没有问题的。(当GCD对象为其成员变量时才具有持有的关系)
结论
我们观察Block_byref_a_0结构体,这个结构体中含有isa指针,所以也是一个对象,它是用来包装局部变量a的。当block被copy到堆中时,Persontest_block_impl_0的拷贝辅助函数Persontest_block_copy_0会将Block_byref_a_0拷贝至堆中,所以即使局部变量所在堆被销毁,block依然能对堆中的局部变量进行操作。其中Block_byref_a_0成员指针forwarding用来指向它在堆中的拷贝
利用对象做Block
现在有RequestObject和XXNetEngine两个文件.
1.RequestObject
#import
typedefvoid(^ReqSuccessBlock)(BOOLisSuccess);
typedefvoid(^FailedBlock)(BOOLisSuccess);
@interfaceRequestObject:NSObject
@property(nonatomic,copy) ReqSuccessBlock successBlock;
@property(nonatomic,copy) FailedBlock failedBlock;
@property(nonatomic,assign)NSIntegerCMD;
@property(nonatomic,assign)NSIntegerseq;
@end
2.XXNetEngine
#import"XXNetEngine.h"
#import"RequestObject.h"
@interfaceXXNetEngine()
@property(nonatomic,strong)NSMutableDictionary*requestDict;
@end
@implementationXXNetEngine
-(instancetype)init
{
if(self= [superinit]){
self.requestDict = [NSMutableDictionarydictionary];
}
returnself;
}
+(instancetype) sharedInstance
{
staticXXNetEngine* instance =nil;
staticdispatch_once_tonceToken;
dispatch_once(&onceToken, ^{
instance = [[XXNetEngine alloc] init];
});
returninstance;
}
-(void)requestData:(RequestObject*) reqObject
successBlock:(void(^)(BOOLisSuccess))successBlock
failureBlock:(void(^)(BOOLisSuccess))failureBlock
{
if( reqObject && successBlock && failureBlock ) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0), ^{
//requestSeq是一个随机数
intrequestSeq = arc4random() %100;;
//网络操作
reqObject.successBlock = successBlock;
reqObject.failedBlock = failureBlock;
reqObject.seq = requestSeq;
[selfaddRequestItem:reqObject seq:requestSeq];
//模拟一个2秒的网络操作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[selfdidReceiveData:reqObject.seq];
});});}}
- (void) didReceiveData:(NSInteger) seq
{
RequestObject *request = [selfgetRequestItem:seq];
if( request ) {
[selfremoveRequestItem:seq];
dispatch_async(dispatch_get_main_queue(), ^{
if(NULL!= request.successBlock ) {
request.successBlock(YES);
}});}}
//用命令字做参数传递
- (RequestObject *) getRequestItem:(NSInteger)seq
{
RequestObject *request =nil;
@synchronized(self) {
NSNumber*numSeq = [NSNumbernumberWithInteger:seq];
request = [self.requestDict objectForKey:numSeq];
}
returnrequest;
}
- (void) addRequestItem:(RequestObject *)request seq:(NSInteger)seq
{
@synchronized(self) {
if( request && (0!=seq) ) {
NSNumber*numSeq = [NSNumbernumberWithInteger:seq];
[self.requestDict setObject:request forKey:numSeq];
}}}
- (void) removeRequestItem:(NSInteger)seq
{
@synchronized(self) {
NSNumber*numSeq = [NSNumbernumberWithInteger:seq];
[self.requestDict removeObjectForKey:numSeq];
}}
@end
Delegate、Notification、Block的比较
名称优点缺点
Notification1.使用简单,代码精简。2.解决了同时向多个对象监听相应的问题。3.传值方便快捷,Context自身携带相应的内容。1.使用完毕后,要时刻记得注销通知,否则将出现不可预见的crash。2.key不够安全,编译器不会监测是否被通知中心正确处理。3.调试的时候动作的跟踪将很难进行。4.当使用者向通知中心发送通知的时候,并不能获得任何反馈信息。5.需要一个第三方的对象来做监听者与被监听者的中介。
Delegate1.减少代码的耦合性,使事件监听和事件处理相分离。2.清晰的语法定义,减少维护成本,较强的代码可读性。3.不需要创建第三方来监听事件和传输数据。1.实现委托的代码过程比较繁琐。2.当实现跨层传值监听的时候将加大代码的耦合性,并且程序的层次结构将变的混乱。3.当对多个对象同时传值响应的时候,委托的易用性将大大降低。
Block1.语法简洁,实现回调不需要显示的调用方法,代码更为紧凑。2.增强代码的可读性和可维护性。3.配合GCD优秀的解决多线程问题。1.Block中得代码将自动进行一次retain操作,容易造成内存泄露2.Block内默认引用为强引用,容易造成循环引用。
使用原则
如果方法过多,一般大于3个的时候,用delegate,因为此时用block,代码将很难维护。比如UITableViewDelegate等等UI组建,都是用的delegate。其他方式用block。
Notification能不用的时候尽量不用,缺点太多明显,增加调试难度。在工程大的情况下,极其难以维护。当然有些情况下也是必不可少的,比如观察者模式.
感谢:向晨宇Block入门
Block测试题:http://blog.parse.com/learn/engineering/objective-c-blocks-quiz/
网友评论