zoukankan      html  css  js  c++  java
  • Objective-C编程

    1. 首先,我们快速过一下,什么是Block?

    Block是一段代码,它在OC中以^开头,可以有返回值,和参数列表,但就是没有名字。

    所以,你可以把它认为是匿名函数。

    事实上,它和Swift中的闭包(Closure)是一样的。

    或者,学过.NET的童鞋知道委托吧,它和委托也差不多概念。

    都是可以在一个方法中传入它,作为参数的方法。

    无参无返回值的Block:

    [MyObject myMethodParam1: xx param2: ^{

      ...

    }];

    有参有返回值的Block:

    [MyObject myMethodParam1: xx param2: ^BOOL(id param1, id param2) {

      ...

    }];

    好了,说了那么多,我们来看个例子:

    myDict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
        NSLog(@"Key - %@: Value - %@", key, value);
        if ([@"END" isEqualToString:key]) {
            *stop = YES;
        }
    }];

    上面这段代码枚举一个字典的键值对,知道遇到END键值退出循环,否则枚举所有的键值对。

    2. 在Block中我们可不可以使用Block范围以外声明的变量呢?

    答案是可以的,但是它是只读的,你如果要修改这个变量,会编译错误。

    我们还是来看上面那段代码的例子,在循环中,我们增加了一个外部变量,想要让Block提早结束

    BOOL stopEarly = NO;
    double stopValue = 100.2;
    [myDict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
        NSLog(@"Key - %@: Value - %@", key, value);
        if ([@"END" isEqualToString:key] || [value doubleValue] == stopValue) {
            *stop = YES;
            stopEarly = YES; // 这段代码编译会出错!!
        }
    }];

    上面的代码stopValue变量的Block内读取没有任何问题,但是当我们企图在Block内修改stopEarly变量的值时,编译出错了!

    那么如果,我们执意要修改Block外面的变量,是不是可以呢?

    答案是可以的。

    我们要使用__block关键字,原理上是通过使用该关键字,我们可以把block外的变量从栈中移到堆中,这样就可以在Block内部使用了。

    当Block结束时,变量又回到栈中。

    还是上面的代码,我们作下修改,如下:

    __block BOOL stopEarly = NO;
    double stopValue = 100.2;
    [myDict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
        NSLog(@"Key - %@: Value - %@", key, value);
        if ([@"END" isEqualToString:key] || [value doubleValue] == stopValue) {
            *stop = YES;
            stopEarly = YES; 
        }
    }];
    
    if (stopEarly)
        NSLog(@"Block提前终止了!");

    3. 当我们向Block内的对象发送消息时,系统会创建一个指向该对象的强指针

    该强指针会一直保留到Block超出自己的范围,不存在。

    4. Block数组

    property (nonatomic, strong) NSMutableArray *blockArray;
    
    ...
    - (void) someMethod {
        [self.blockArray addObject:^{
            ...
        }];
    }
    
    // 获取数组中的block并调用
    void (^doit)(void) = self.blockArray[0];
    doit();

    5. 常见的Block中导致循环引用(Memory Cycle)的场景

    前面我们曾经提到:

    当我们向Block内的对象发送消息时,系统会创建一个指向该对象的强指针

    该强指针会一直保留到Block超出自己的范围。

    换句话说,Block内的所有对象,都会在堆上保留自己的内存,也就是说,Block会保留

    指向这些对象的强指针(strong pointer)。

    我们来看下面的代码:

    @property (nonatomic, strong) NSMutableArray *blockArray;
    
    ...
    
    // in the blockArray a strong pointer has been kept to the self object
    // while self holds another strong pointer to blockArray
    - (void) someMethod {
        [self.blockArray addObject:^{
            [self doSomething];
        }];
    }

    上面的代码的问题是:blockArray保留了一个指向类对象的强引用指针,而类对象,也就是self也保留了

    一个指向blockArray的强引用指针。

    大家都有强指针指向对方, 它们都无法从栈中释放,这就导致了内存的循环引用。

    解决方案:
    为了打破循环引用,我们要使用一个弱类型的局部变量。
    什么意思?
    由于局部变量都是强指针类型,都保留指向堆中的强指针,所以我们要想办法创建一个弱指针类型的局部变量来
    打破循环引用。
    可以使用关键字:
    __weak
    比如:
    __weak MyClass *weakSelf = self;
    这样即使self本身是强指针类型的,而我们创建的weakSelf却变成了弱指针类型。

    于是,上面有问题的代码我们可以进行如下的修改:

    @property (nonatomic, strong) NSMutableArray *blockArray;
    
    ...
    
    - (void) someMethod {
        __weak MyClass *weakSelf = self;
    
        [self.blockArray addObject:^{
            [weakSelf doSomething];
        }];
    }

    这样block这边保留的对于对象的指针就变成了弱类型的指针,循环引用被打破了!
    这是一个常用的解决方案,必须要记住!!

  • 相关阅读:
    一次网络IO优化的讨论
    服务器框架回顾
    一个小工具:DebugFile
    TPO-23 C2 Advice on choosing courses
    TPO-23 C1 Post a student announcement
    TPO-22 C2 Revise a music history paper
    TPO-22 C1 Complain about a biased article
    TPO-21 C2 Which elective courses to take
    TPO-20-Apply for the undergraduate research fund
    TPO-21 C1 Find a building for orientation
  • 原文地址:https://www.cnblogs.com/davidgu/p/6079496.html
Copyright © 2011-2022 走看看