Block基础看它就够了

作者: 聆听者JYZ | 来源:发表于2016-07-30 18:06 被阅读363次

Block基本用法

"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下判断原则

"Block语法"

造成循环引用的实例

@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

"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/

相关文章

网友评论

    本文标题:Block基础看它就够了

    本文链接:https://www.haomeiwen.com/subject/tkdtsttx.html