Objc Block

ref1

一、__block 的使用

  说明:

    在 block 内只能读取在同一个作用域的变数而且没有办法修改在 block 外定义的任何变数,此时若我们想要这些变数能够在 block 中被修改,就必须在前面加上 __block 的修饰词,否则编辑时就会产生错误。

    在编译的时候,同一作用域的变量的值会被 copy 下来,即使在接下的语句中修改变量的值,也不会对 block 里面有影响。如果需要 block 运行的时候取的是变量当前的值,则在变量声明的时候加上 __block。

 

  1) 基本类型局部变量, 基本类型静态变量,基本类型全局变量在 block 中的使用

    gobalVariable = 50;
    staticVariable = 100;
    NSInteger localVariable1 = 10;
    __block NSInteger localVariable2 = 20;
    
    void (^ABlock)(void) = ^(void) {
        // 50
        NSLog(@"src gobalVariable = %d", gobalVariable);
        
        // 100
        NSLog(@"src staticVariable = %d", staticVariable);
        
        // 10
        NSLog(@"src localVariable1 = %d", localVariable1);
        
        // 21
        NSLog(@"src localVariable2 = %d", localVariable2);

        
        ++gobalVariable; // 可读可写
        ++staticVariable; // 可读可写
        
//        ++localVariable1; // 只可读,所以这里尝试改变变量的值会发生编译错误
        ++localVariable2; // 可读可写
        
        // 51
        NSLog(@"des gobalVariable = %d", gobalVariable);
        
        // 101
        NSLog(@"des staticVariable = %d", staticVariable);
        
        // 10
        NSLog(@"des localVariable1 = %d", localVariable1);
        
        // 22
        NSLog(@"des localVariable2 = %d", localVariable2);
    };
    ++localVariable1; // 不会影响 block 中的值
    ++localVariable2; // 会影响 block 中的值
    
    ABlock();

  2) Object类型局部变量, Object类型静态变量,Object类型全局变量, block类型变量在 block 中的使用

    a> MRC 下

xxx.h
#import <Foundation/Foundation.h>

@interface BlockMemoryTestClass : NSObject {
    @private
        NSObject* instanceVariable;
}

- (void)testMethod;

@end


xxx.m
#import "BlockMemoryTestClass.h"

@implementation BlockMemoryTestClass

static NSObject* staticVariable = nil;
NSObject* gobalVariable = nil;

- (id)init {
    self = [super init];
    if (self) {
        instanceVariable = [[NSObject alloc] init];
        staticVariable = [[NSObject alloc] init];
        gobalVariable = [[NSObject alloc] init];
    }
    return self;
}

- (void)testMethod {
    NSObject* localVariable = [[NSObject alloc] init];
    __block NSObject* blockVariable = [[NSObject alloc] init];
    
    typedef void (^ABlock)(void);
    ABlock block = ^(void) {
        NSLog(@"instanceVariable %@", instanceVariable);
        NSLog(@"staticVariable %@", staticVariable);
        NSLog(@"gobalVariable %@", gobalVariable);
        NSLog(@"localVariable %@", localVariable);
        NSLog(@"blockVariable %@", blockVariable);
    };
    
//    block = [[block copy] autorelease];
    block();
    
    NSLog(@"instanceVariable %d", [instanceVariable retainCount]);
    NSLog(@"staticVariable %d", [staticVariable retainCount]);
    NSLog(@"gobalVariable %d", [gobalVariable retainCount]);
    NSLog(@"localVariable %d", [localVariable retainCount]);
    NSLog(@"blockVariable %d", [blockVariable retainCount]);
    NSLog(@"self %d", [self retainCount]);
}

- (void)dealloc {
    [super dealloc];
}

  输出结果:

/* 
 不含 block = [[block copy] autorelease];, 原因是不执行 [block copy],block 的类型是 NSStackBlock 存放在栈内存,执行了 [block copy] 变为 NSMallocBlock 存放在堆内存。
 instanceVariable 1
 staticVariable 1
 gobalVariable 1
 localVariable 1
 blockVariable 1
 self 1
 */

/* 
 含有 block = [[block copy] autorelease];
 instanceVariable 1
 staticVariable 1
 gobalVariable 1
 localVariable 2
 blockVariable 1
 self 2 (原因是 instanceVariable 的使用导致)
 */

  

    b> ARC 下

xxx.h
#import <Foundation/Foundation.h>

@interface BlockMemoryTestClass : NSObject {
    @private
        NSString* instanceString;
}

- (void)testInstanceVariable;
- (void)testGobalVariable;
- (void)testStaticVariable;
- (void)testLocalVariable;
- (void)testBlockVariable;
- (void)testWeakVariable;

@end

xxx.m
#import "BlockMemoryTestClass.h"

@implementation BlockMemoryTestClass

NSString* gobalString = nil;

- (void)testInstanceVariable {
    instanceString = @"123";
    
    NSLog(@"instanceString is %@", instanceString);
    NSLog(@"instanceString address %p", &instanceString);
    
    void(^ABlock)(void) = ^(void) {
        NSLog(@"instanceString is %@", instanceString);
        NSLog(@"instanceString address %p", &instanceString);
    };
    
    instanceString = nil;
    ABlock();
}

- (void)testGobalVariable {
    gobalString = @"123";
    
    NSLog(@"gobalString is :%@", gobalString);
    NSLog(@"gobalString address %p", &gobalString);
    
    void(^ABlock)(void) = ^(void) {
        NSLog(@"gobalString is :%@", gobalString);
        NSLog(@"gobalString address %p", &gobalString);
    };
    
    gobalString = nil;
    ABlock();
}

- (void)testStaticVariable {
    static NSString* staticString = nil;
    staticString = @"123";
    
    NSLog(@"staticString is %@", staticString);
    NSLog(@"staticString address %p", &staticString);
    
    void(^ABlock)(void) = ^(void) {
        NSLog(@"staticString is %@", staticString);
        NSLog(@"staticString address %p", &staticString);
    };
    
    staticString = nil;
    ABlock();
}

- (void)testLocalVariable {
    NSString* localString = @"123";
    
    NSLog(@"localString is %@", localString);
    NSLog(@"localString address %p", &localString);
    
    void(^ABlock)(void) = ^(void) {
        NSLog(@"localString is %@", localString);
        NSLog(@"localString address %p", &localString);
    };
    
    localString = nil;
    ABlock();
}

- (void)testBlockVariable {
    __block NSString* blockString = @"123";
    
    NSLog(@"blockString is %@", blockString);
    NSLog(@"blockString address %p", &blockString);
    
    void(^ABlock)(void) = ^(void) {
        NSLog(@"blockString is %@", blockString);
        NSLog(@"blockString address %p", &blockString);
    };
    
    blockString = nil;
    ABlock();
}

- (void)testWeakVariable {
    NSString* localString = @"123";
    __weak NSString* weakString = localString;
    
    NSLog(@"weakString is %@", weakString);
    NSLog(@"weakString address %p", &weakString);
    NSLog(@"weakString str address %p", weakString); // 注意这里的 weak 存放的是 localString 的地址
    
    void(^ABlock)(void) = ^(void) {
        NSLog(@"weakString is %@", weakString);
        NSLog(@"weakString address %p", &weakString);
        NSLog(@"weakString str address %p", weakString);
    };
    
    localString = nil;
    ABlock();
}

@end

  运行结果:

 

 


二、block 的声明

  1) 声明一个 block 函数   “返回值类型 (^block名字)(传入参数)”

// 无参数无返回的 block 声明
void (^ FirstBlock)(void); 

// 有参数又反回的 block 声明
int (^ SecondBlock)(int, char);

// 含有 10 个 block 的阵列 声明
void (^ BlockArray[10])(int);

  2) 声明一个参数是 block 类型的方法  “返回类型 (^)(传入参数))block形参名

- (void)methodWithBlock:(void (^)(void))block {
    
}

  3) 声明一个带 block 体的 block 变量  "返回值类型 (^block名字)(传入参数)= ^(传入参数) { };"

void (^BlockVariable)(NSInteger, NSInteger) = ^(NSInteger a, NSInteger b) {
        NSLog(@"a = %d", a);
        NSLog(@"b = %d", b);
    };

  

 


三、arc下的 block 的内存管理问题。

  1) 根据 block 在内存的位置分为 3 类:

    a> NSGobalBlock: block 实现体里面没有引用外部变量

    void (^BlockVariable)(NSInteger, NSInteger) = ^(NSInteger a, NSInteger b) {
        NSLog(@"a = %d", a);
        NSLog(@"b = %d", b);
    };

    NSLog(@"%@", BlockVariable);  // <__NSGlobalBlock__: 0x4060>

  

    b> NSStackBlock: 位于栈内存,当函数返回时 block 无效

    NSArray* localVariable = @[@(1), @(2), @(3)];
    
    NSLog(@"Block is %@", ^(void){
        NSLog(@"localVariable = %@", localVariable);
    });

  

    c> NSMallocBlock: 位于堆内存

    void (^BlockVariable)(void) = ^(void) {
        NSLog(@"localVariable = %@", localVariable);
    };
    
    NSLog(@"%@", BlockVariable);  // <__NSMallocBlock__: 0x8c55e20>

  

  2) block 的 copy,retain, release 操作

    a> NSStackBlock 进行 copy 操作即可变为 NSMallocBlock

    b> copy,retain, release 对 block 的 reatain count 没有影响,block 的 retain count 始终是 1.

    c> 对 NSGobalBlock 进行 copy, reatain, release 无效

    d> 对NSStackBlock 进行 retain, release 无效,

       EX:MRC 方法中如果返回 mutableArray,操作[mutableArray addObject:stackBlock]; 是错误的,原因是方法结束 stackBlock 会被释放,应该改为 [mutableArray addObject:[stackBlock copy]]; 先将 block 从栈内存复制到堆内存上, 这会 retain 其引用的外部变量。所以 block 中如果引用了他的宿主对象,那很可能引起循环引用。

          ARC 方法中如果返回 mutableArray,操作[mutableArray addObject:block]; 是没有问题的,因为 arc 下实例化的 block 是 NSMallocBlock 存放于堆内存。

    e> 对 NSMallocBlock 进行 retain,copy, release 是有效的。reetain count 始终是 1,但是 reference count 会改变。copy 不会生成新 block 对象。

    f> 尽量不要对 block 进行 retain 操作。

  3) 循环引用的问题 (arc 环境下)

    EX_1

xxx.h
#import <Foundation/Foundation.h>

typedef void (^ABlcok)(void);

@interface BlockCycleTestClass : NSObject

@property(nonatomic, strong) ABlcok aBlock;

@end


xxx.m
#import "BlockCycleTestClass.h"

@implementation BlockCycleTestClass

- (id)init {
    self = [super init];
    if (self) {
        
        // 循环引用: 前提是 self 必须是 strong 引用 block 否则不会造成 retain cycle, 如果 block 的修饰变为 assign 不会引起 retain cycle。
        self.aBlock = ^(void) {
            [self doSomething];
        };
    
        // 循环引用: 前提是 self 必须是 strong 引用 block 否则不会造成 retain cycle, 如果 block 的修饰变为 assign 不会引起 retain cycle。
        __block BlockCycleTestClass* blockSelf = self;
        self.aBlock = ^(void) {
            [blockSelf doSomething];
        };
    
        // 不会循环引用
        __weak BlockCycleTestClass* weakSelf = self;
        self.aBlock = ^(void) {
            [weakSelf doSomething];
        };

        // 不会循环引用
        ABlcok anotherBlock = ^(void) {
            [self doSomething];
        };
        anotherBlock();
        
    }
    return self;
}

- (void)doSomething {
    NSLog(@"do something");
}

- (void)dealloc {
    NSLog(@"no cycle retain...");
}

@end

    EX_2:

#import <UIKit/UIKit.h>

typedef void (^AnotherBlock)(void);

@interface AppDelegate : UIResponder <UIApplicationDelegate> {
    @private
        AnotherBlock instanceBlock;
}

@property (strong, nonatomic) UIWindow *window;

@end


#import "AppDelegate.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];

     NSLog(@"Self retain count is: %ld", CFGetRetainCount((__bridge CFTypeRef)self));
    [self test];
    NSLog(@"Self retain count is: %ld", CFGetRetainCount((__bridge CFTypeRef)self));

    [self.window makeKeyAndVisible];
    return YES;
}


-(void)test {
    // 输出结果: 1, 3, 2,3 的原因: self自身,stack block一份,malloc block一份。(循环引用)
    instanceBlock = ^(void) {
        NSLog(@"%p", self);
    };
    NSLog(@"Self retain count is: %ld", CFGetRetainCount((__bridge CFTypeRef)self));


    // 输出结果: 1, 1, 1
    __weak AppDelegate* weakSelf = self;
    instanceBlock = ^(void) {
        NSLog(@"%p", weakSelf);
    };

    // 输出结果: 1, 3, 1
     AnotherBlock block = ^(void) {
        NSLog(@"%p", self);
    };       
}

  


 

 四、MRC 下使用 block 需要注意的事项:

  1) 曾经遇到这种情况, 这里必须要设置 weakSelf = nil; 否则内存溢出。

- (void)method1 {
    __block __unsafe_unretained MyClassName* weakSelf = self;
   [self method2:^{
        NSLog(@"++++++++ %@   %d", weakSelf, weakSelf.retainCount);
        weakSelf = nil;
    }];
    
}

- (void)method2:(void(^)(void))completedBlock {
  ASIHTTPRequest* reqeust = ...;
  [request setCompletionBlock:^{    
        if (completedBlock) completedBlock();
    }];
}

  

 

 

 

  

posted on 2014-07-25 09:37  EileenLeung  阅读(340)  评论(0编辑  收藏  举报