zoukankan      html  css  js  c++  java
  • 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();
        }];
    }
    

      

      

  • 相关阅读:
    javascript时间戳和日期字符串相互转换
    jquery两稳定版本比较~~
    原生的强大DOM选择器querySelector
    分享一个自定义的 console 类,让你不再纠结JS中的调试代码的兼容
    基于Mesos运行Spark
    chrome插件 postman 可以调用restful服务
    cassandra优秀博客集
    Cassandra监控
    Cassandra
    SecureCRT中文显示乱码的解决方法
  • 原文地址:https://www.cnblogs.com/eileenleung/p/3716341.html
Copyright © 2011-2022 走看看