iOS之Block詳解

iOS Block實例https://my.oschina.net/Jacedy/blog/842167macos

 

1、Block定義閉包

閉包是一個函數(或指向函數的指針),再加上該函數執行的外部的上下文變量(有時候也稱做自由變量)。函數

block 實際上就是 Objective-C 語言對於閉包的實現。優化

 

2、Block原理atom

// main.m

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
//        static int age = 20;
        __block int age = 20;
        
        void (^blcok)(void) = ^ {  
            age = 21;
            NSLog(@"%d", age);
        };
        
        age = 22;
        NSLog(@"%d", age);
        
        blcok();
        NSLog(@"%@", blcok);
    }
}

    將上述代碼使用命令:$ clang -rewrite-objc main.m 編譯後,截取C++代碼以下:.net

.......

__attribute__((visibility("default"))) __attribute__((availability(macosx,introduced=10_8)))

#ifndef _REWRITER_typedef_NSXPCListenerEndpoint
#define _REWRITER_typedef_NSXPCListenerEndpoint
typedef struct objc_object NSXPCListenerEndpoint;
typedef struct {} _objc_exc_NSXPCListenerEndpoint;
#endif

struct NSXPCListenerEndpoint_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	void *_internal;
};

/* @end */


struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age;
};

// __main_block_impl_0(命名:main函數下,第0個名叫block的函數(impl))
// __main_block_impl_0:包含三個成員變量和一個構造函數
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_age_0 *age; // by ref
    // 定義一個__main_block_impl_0構造函數並初始化
    // 參數列表有__main_block_impl_0的一個成員變量作形參傳進去初始化, age(_age)構造函數給age賦值 _age
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_age_0 *age = __cself->age; // bound by ref


            (age->__forwarding->age) = 21;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_c3_j1lbjks553g1cm01rczynmhw0000gn_T_main_dc1432_mi_0, (age->__forwarding->age));
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} /* 定義一個__main_block_desc_0_DATA成員變量,並初始化*/ __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 


        __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 20};

        // 等號左邊的(*blcok)(void)是一個函數指針,等號右邊的(void (*)())表示返回一個函數指針**IMP,block函數名就是函數指針首地址
        // __main_block_impl_0接收前兩個形參,flags=0形參自動設置
        // __main_block_impl_0結構體中定義的fp指針來調用__main_block_func_0函數
        // __main_block_impl_0結構體中定義的desc指針接收__main_block_desc_0_DATA地址來初始化desc,並賦值給Desc
        // 傳入age的地址&,使block能動態獲取age的值變化
        void (*blcok)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));

        (age.__forwarding->age) = 22;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_c3_j1lbjks553g1cm01rczynmhw0000gn_T_main_dc1432_mi_1, (age.__forwarding->age));

        // 解引用__block_impl中的FunPtr成員變量,即解引用fp,也就是調用__main_block_func_0函數,入參爲(__block_impl *)類型的block
        ((void (*)(__block_impl *))((__block_impl *)blcok)->FuncPtr)((__block_impl *)blcok);
    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

 

3、Block存儲指針

        block的存儲形態有三種:_NSStackBlock(棧)、_NSGlobalBlock(全局)、_NSMallocBlock(堆)code

 要點一:當block在函數內部,且定義的時候就使用了函數內部的變量,那麼這個  block是存儲在棧上的。對象

 要點二:當block定義在函數體外面,或者定義在函數體內部且當時函數執行的時候,block體中並無須要使用函數內部的局部變量時,也就是block在函數執行的時候只是靜靜地待在一邊定義了一下而不使用函數體的內容,那麼block將會被編譯器存儲爲全局block。blog

 要點三:全局block存儲在堆中,對全局block使用copy操做會返回原函數指針;而對棧中的block使用copy操做,會產生兩個不一樣的block地址,也就是兩個匿名函數的入口地址。

#import <Foundation/Foundation.h>

@interface BlockTest : NSObject

@property (nonatomic, copy) void (^copyBlock)();

@property (nonatomic, weak) void (^weakBlock)();

- (void)run;

@end
typedef int (^blockSave)(void);

typedef void (^typedefBlock)(void);

void (^outFuncBlock)(void) = ^{
    NSLog(@"someBlock");
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {

#pragma mark - ARC機制優化會將stack(棧)的block,轉爲heap(堆)的block進行調用。
        
        __block int age = 20;
        int *ptr = &age;
        blockSave x = ^{
            NSLog(@"(++age):%d", ++age);
            return age;
        };
        blockSave y = [x copy];
        y();
        NSLog(@"(*ptr):%d", *ptr);
        /**
         ARC下:(++age):21   (*ptr):21
         MRC下:(++age):21   (*ptr):20
         */
        
#pragma mark - copyBlock (使用函數內變量)
        
        BlockTest *test = [[BlockTest alloc] init];
        
        test.copyBlock = ^{
            [test run];
        };
        NSLog(@"%@", test.copyBlock);
        
#pragma mark - copyBlock(未使用函數內變量)
        
        test.copyBlock = ^{
            NSLog(@"copyBlock");
        };
        NSLog(@"%@", test.copyBlock);
        
#pragma mark - weakBlock(未使用函數內變量)
        
        test.weakBlock = ^{
            NSLog(@"weakBlock");
        };
        NSLog(@"%@", test.weakBlock);
 
#pragma mark - weakBlock(使用函數內變量)
        
        test.weakBlock = ^{
            [test run];
        };
        NSLog(@"%@", test.weakBlock);
        
#pragma mark - someBlock(定義在函數體外)
        
        NSLog(@"%@", outFuncBlock);
        
#pragma mark - typedefBlock(函數體外自定義的Block)
        
        typedefBlock b = ^{
            NSLog(@"typedefBlock");
        };
    }
    return 0;
}
// 運行結果:

(++age):21
(*ptr):21
<__NSMallocBlock__: 0x100105960>
<__NSGlobalBlock__: 0x100002190>
<__NSGlobalBlock__: 0x1000021d0>
<__NSStackBlock__: 0x7fff5fbff6b8>
<__NSGlobalBlock__: 0x1000020c0>

 

4、Block解決循環引用作法

     1)若是沒有對block進行copy操做,block就存儲於棧空間;

     2)若是對block進行copy操做,block就存儲於堆空間;

     3)若是block存儲於棧空間,不會對block內部所用到的對象產生強引用;

     4)若是block存儲於堆空間,就會對block內部所用到的對象產生強引用.

一、ARC下:

@property (nonatomic, copy) void (^testBlock)();
Person *p = [[Person alloc] init];
__weak typeof (p) weakP = p;        // 或:   __unsafe_unretained typeof(p) weakP = p;
p.testBlock = ^{
    [weakP run];
};

二、MRC下:

@property (nonatomic, copy) void (^testBlock)();
Person *p = [[Person alloc] init];
__block typeof (p) weakP = p;
p.testBlock = ^{
    [weakP run];
};
    
[p release];
相關文章
相關標籤/搜索