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