2013-08-10 37 views
5

Do sau (thủ công tham khảo đếm):không dispatch_async sao chép khối nội

void (^block)(void) =^{ 
    NSLog(@"wuttup"); 
} 

void (^async_block)(void) =^{ 
    block(); 
} 

dispatch_async(dispatch_get_main_queue(), async_block); 

sẽ "block" được sao chép chứ không phải ném ra khỏi ngăn xếp và phá hủy?

Trả lời

8

Tôi tin rằng, câu trả lời là Có.

Khối ngoài sẽ được gửi đi không đồng bộ làm cho thời gian chạy tạo bản sao trên heap cho khối này. Và như hình dưới đây, và được mô tả trong các Block Implementation Specification - Clang 3.4 Documentation, các biến nhập của khối bên trong cũng được sao chép vào heap.

Trong ví dụ của OP, chúng tôi có "bản sao const đã nhập của tham chiếu Chặn".

Tôi đang sử dụng ví dụ trong Quy cách:

void (^existingBlock)(void) = ...; 
void (^vv)(void) = ^{ existingBlock(); } 
vv(); 

Các Đặc điểm kỹ thuật nói rằng copy_helperdispose_helper chức năng là cần thiết:

Chức năng copy_helper thông qua cả hai con trỏ ngăn xếp hiện tại dựa và con trỏ đến phiên bản heap mới và nên gọi lại vào thời gian chạy để thực sự thực hiện thao tác sao chép trên các trường được nhập trong Khối.

Mã ví dụ sau đây trong Đặc tả khó giải mã (và thực sự thiếu mô tả những gì xảy ra khi khối ngoài được sao chép vào heap). Dù sao, nó xuất hiện các đặc điểm kỹ thuật cố gắng để cho thấy rằng các biến nhập khẩu của khối bên trong sẽ được (đệ quy) sao chép vào khu vực lưu trữ nguyên của khối bên ngoài.

Khi khối ngoài sẽ được sao chép trên heap, có vẻ như các biến được nhập của các khối bên trong cuối cùng cũng sẽ tồn tại trên heap.

Vâng, bằng trực giác, tất cả điều này đều có ý nghĩa.

Tôi đã thực hiện một chương trình thử nghiệm nhỏ sẽ chứng minh điều này: (bạn phải gỡ lỗi và kiểm tra việc tháo gỡ để tìm ra những gì đang xảy ra dưới bề mặt).

#import <Foundation/Foundation.h> 


void foo(int param) 
{ 
    int x0 = param; 
    int x1 = param + 1; 
    void (^existingBlock)(void) = ^{ 
     int y0 = x0; 
     int y1 = x1; 
     printf("&y0: %p\n", &y0); 
     printf("&y1: %p\n", &y1); 
     printf("&x0: %p\n", &x0); 
     printf("&x1: %p\n", &x1); 
    }; 

    void (^vv)(void) = ^{ 
     int y2 = x0; 
     int y3 = x1; 
     existingBlock(); 
     printf("&y2: %p\n", &y2); 
     printf("&y3: %p\n", &y3); 
     printf("&x0: %p\n", &x0); 
     printf("&x1: %p\n", &x1); 
    }; 

    printf("Stack: &x: %p\n", &x0); 
    printf("Stack: &x: %p\n", &x1); 

    printf("------- on main thread -------\n"); 
    vv(); 

    dispatch_async(dispatch_get_global_queue(0, 0), ^{ 
     printf("------- on thread 2 -------\n"); 
     assert(vv); 
     sleep(1); 
     int y4 = x0; 
     int y5 = x1; 
     vv(); 
     printf("&y4: %p\n", &y4); 
     printf("&y5: %p\n", &y5); 
     printf("&x0: %p\n", &x0); 
     printf("&x1: %p\n", &x1); 
    }); 
} 

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

     foo(1); 
     sleep(2); 
    } 
    return 0; 
} 

Đầu ra là như sau:

Stack: &x: 0x7fff5fbff868 
Stack: &x: 0x7fff5fbff864 
------- on main thread ------- 
&y0: 0x7fff5fbff70c 
&y1: 0x7fff5fbff708 
&x0: 0x1001081e0 
&x1: 0x1001081e4 
&y2: 0x7fff5fbff76c 
&y3: 0x7fff5fbff768 
&x0: 0x10010a588 
&x1: 0x10010a58c 
------- on thread 2 ------- 
&y0: 0x1000e5d9c 
&y1: 0x1000e5d98 
&x0: 0x1001081e0 
&x1: 0x1001081e4 
&y2: 0x1000e5dfc 
&y3: 0x1000e5df8 
&x0: 0x10010a588 
&x1: 0x10010a58c 
&y4: 0x1000e5e6c 
&y5: 0x1000e5e68 
&x0: 0x10010a5e8 
&x1: 0x10010a5ec 

Khi khối được thực thi trên các chủ đề chính, nó sống trên stack (như thể hiện bởi các địa chỉ của các biến địa phương và nhập khẩu). Khi được thực hiện qua dispatch_async thời gian chạy đã sao chép khối - bao gồm các khối bên trong, như có thể thấy bằng địa chỉ của các biến cục bộ và được nhập của các khối.

Chúng tôi có thể đặt điểm ngắt tại hàm copy_helper_block và trên thực tế, chương trình dừng lại ở đó một lần để sao chép khối vv vào heap.

enter image description here

0

blockretain ed (không được sao chép trên mỗi sé) để bị bắt trong một khối khác async_block giống như đối tượng thông thường. Sao chép khối là kết quả của đối tượng khối đang được gửi một thông báo [block retain] được gọi là phương thức đã sao chép retain bị ghi đè.

+1

bạn lấy thông tin này từ đâu? Và những gì sẽ giữ lại một khối làm gì? –

+0

Các khối kế thừa từ 'NSObject' (cả hai loại' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' có 'objc_retain()' 'ed khi bị bắt trong một khối khác giống như 'NSArray' và 'NSString's –

+0

@Gabriele Petronella Kể từ iOS 6.0 và Mac OS X 10.8, Maxthon đúng khi nói rằng các khối là" Đối tượng ". ARC sẽ quản lý chúng dưới dạng con trỏ đối tượng có thể lưu lại. Điều đó KHÔNG có nghĩa là, tuy nhiên các khối đó được tự động sao chép. Trong ví dụ này, ngay cả trên mac OS X 10.8 hoặc iOS 6 và ARC được bật, bạn cần phải sao chép một cách rõ ràng khối 'block' để chuyển nó thành heap. Nếu không, 'khối' vẫn còn trên ngăn xếp và khối được gọi không đồng bộ' async_block' sẽ bị lỗi. – CouchDeveloper

5

Từ Apple docs trên dispatch_async sự:

khối

The block to submit to the target dispatch queue. This function performs Block_copy and Block_release on behalf of callers. This parameter cannot be NULL.

Vì vậy, async_block được sao chép.

mỗi this discussion, block (bên trong async_block trong ví dụ của bạn), sẽ là một readonly copy trong async_block

Các vấn đề liên quan