Từ khóa của __block có nghĩa là gì?


445

Chính xác thì __blocktừ khóa trong Objective-C nghĩa là gì? Tôi biết nó cho phép bạn sửa đổi các biến trong các khối, nhưng tôi muốn biết ...

  1. Chính xác thì nó nói gì với trình biên dịch?
  2. Nó có làm gì khác không?
  3. Nếu đó là tất cả những gì nó làm thì tại sao nó lại cần thiết ở nơi đầu tiên?
  4. Có phải trong tài liệu ở bất cứ đâu? (Tôi không thể tìm thấy nó).

3
kiểm tra ở đây và phần "Khối và biến".


1
@Code Monkey: Tôi đã hỏi cụ thể về từ khóa, không phải cú pháp nói chung. Vì vậy, đừng nghĩ rằng nó thực sự là một bản sao.
mjisrawi

3
@Code Monkey: Không, đây không phải là bản sao. Câu hỏi bạn đề cập không nói gì __blockcả.
DarkDust

3
Và nếu ai đó đang tự hỏi làm thế nào Objective-C __blocknên dịch sang Swift: Clos Closures [trong Swift] có ngữ nghĩa chụp tương tự như các khối [trong Objective-C] nhưng khác nhau theo một cách chính: Biến có thể thay đổi được thay vì sao chép. Nói cách khác, hành vi của __block trong Objective-C là hành vi mặc định cho các biến trong Swift. Từ cuốn sách của Apple: Sử dụng Swift với ca cao và Objective-C (Swift 2.2).
Jari Keinänen

Câu trả lời:


542

Nó báo cho trình biên dịch rằng bất kỳ biến nào được đánh dấu bởi nó phải được xử lý theo cách đặc biệt khi nó được sử dụng bên trong một khối. Thông thường, các biến và nội dung của chúng cũng được sử dụng trong các khối được sao chép, do đó mọi sửa đổi được thực hiện đối với các biến này không hiển thị bên ngoài khối. Khi chúng được đánh dấu __block, các sửa đổi được thực hiện bên trong khối cũng được hiển thị bên ngoài khối.

Để biết ví dụ và biết thêm thông tin, hãy xem Loại lưu trữ __block trong Chủ đề lập trình khối của Apple .

Ví dụ quan trọng là cái này:

extern NSInteger CounterGlobal;
static NSInteger CounterStatic;

{
    NSInteger localCounter = 42;
    __block char localCharacter;

    void (^aBlock)(void) = ^(void) {
        ++CounterGlobal;
        ++CounterStatic;
        CounterGlobal = localCounter; // localCounter fixed at block creation
        localCharacter = 'a'; // sets localCharacter in enclosing scope
    };

    ++localCounter; // unseen by the block
    localCharacter = 'b';

    aBlock(); // execute the block
    // localCharacter now 'a'
}

Trong ví dụ này, cả hai localCounterlocalCharacterđược sửa đổi trước khi khối được gọi. Tuy nhiên, bên trong khối, chỉ có thể sửa đổi để localCharacterhiển thị, nhờ vào __blocktừ khóa. Ngược lại, khối có thể sửa đổi localCharactervà sửa đổi này được hiển thị bên ngoài khối.


8
Tuyệt vời, giải thích ngắn gọn, và một ví dụ rất hữu ích. Cảm ơn!
Evan Stone

1
Làm thế nào để aBlock sửa đổi localCorer? Nó dường như chỉ sửa đổi CounterGlobal. Cảm ơn
CommaToast

8
Nó không sửa đổi localCounter, nhưng nó sửa đổi localCharacter. Ngoài ra, hãy chú ý đến giá trị localCountercó trong khối: đó là 42, mặc dù biến được tăng trước khi khối được gọi nhưng sau khi khối được tạo (đó là khi giá trị bị "bắt").
DarkDust

1
Đó là một lời giải thích hữu ích - mặc dù - bạn có thể giải thích những gì dường như là những phát biểu mâu thuẫn trong lời giải thích của bạn không? Bạn nói ở trên rằng "aBlock sửa đổi ... localCorer" và sau đó trong các nhận xét bạn nói "[aBlock] KHÔNG sửa đổi localCorer." Đó là cái gì Nếu nó "không được sửa đổi" thì câu trả lời của bạn có nên được chỉnh sửa không?
Praxlistes

2
Nói chung, các vars không có __block sẽ được ghi theo giá trị và được đóng gói vào "môi trường" của khối, khi khối được tạo. Nhưng vars __block sẽ không bị bắt, bất cứ khi nào chúng được sử dụng bên trong hoặc bên ngoài một khối, chúng sẽ được tham chiếu như vậy.
jchnxu

27

@bbum bao gồm các khối theo chiều sâu trong một bài đăng trên blog và chạm vào loại lưu trữ __block.

__block là một loại lưu trữ riêng biệt

Cũng giống như tĩnh, tự động và dễ bay hơi, __block là loại lưu trữ. Nó báo cho trình biên dịch rằng bộ lưu trữ của biến sẽ được quản lý khác nhau.

...

Tuy nhiên, đối với các biến __block, khối không giữ lại. Nó là tùy thuộc vào bạn để giữ lại và phát hành, khi cần thiết.
...

Đối với các trường hợp sử dụng, bạn sẽ thấy __blockđôi khi được sử dụng để tránh các chu kỳ giữ lại vì nó không giữ lại đối số. Một ví dụ phổ biến là sử dụng bản thân.

//Now using myself inside a block will not 
//retain the value therefore breaking a
//possible retain cycle.
__block id myself = self;

Xem bài đăng này để biết thêm về vấn đề giữ chu kỳ: benscheirman.com/2012/01/ . Sẽ __weakđủ trong trường hợp cụ thể này là tốt? Có lẽ rõ ràng hơn một chút ...
Hari Karam Singh

17
Cuối cùng, tuyên bố rằng __block có thể được sử dụng để tránh các chu kỳ tham chiếu mạnh (còn gọi là chu kỳ giữ lại) là hoàn toàn sai trong ngữ cảnh ARC. Do thực tế là trong ARC __block khiến biến được tham chiếu mạnh mẽ, nên thực sự có nhiều khả năng gây ra chúng. stackoverflow.com/a/19228179/189006
Krishnan

10

Thông thường khi bạn không sử dụng __block, khối sẽ sao chép (giữ lại) biến, vì vậy ngay cả khi bạn sửa đổi biến, khối vẫn có quyền truy cập vào đối tượng cũ.

NSString* str = @"hello";
void (^theBlock)() = ^void() {
    NSLog(@"%@", str);
};
str = @"how are you";
theBlock(); //prints @"hello"

Trong 2 trường hợp này, bạn cần __block:

1.Nếu bạn muốn sửa đổi biến bên trong khối và hy vọng nó sẽ hiển thị bên ngoài:

__block NSString* str = @"hello";
void (^theBlock)() = ^void() {
    str = @"how are you";
};
theBlock();
NSLog(@"%@", str); //prints "how are you"

2.Nếu bạn muốn sửa đổi biến sau khi bạn đã khai báo khối và bạn mong muốn khối sẽ thấy sự thay đổi:

__block NSString* str = @"hello";
void (^theBlock)() = ^void() {
    NSLog(@"%@", str);
};
str = @"how are you";
theBlock(); //prints "how are you"

8

__block là một vòng loại lưu trữ có thể được sử dụng theo hai cách:

  1. Đánh dấu rằng một biến sống trong một bộ lưu trữ được chia sẻ giữa phạm vi từ vựng của biến ban đầu và bất kỳ khối nào được khai báo trong phạm vi đó. Và clang sẽ tạo ra một cấu trúc để đại diện cho biến này và sử dụng cấu trúc này theo tham chiếu (không phải theo giá trị).

  2. Trong MRC, __block có thể được sử dụng để tránh giữ lại các biến đối tượng mà một khối bắt giữ. Cẩn thận rằng điều này không làm việc cho ARC. Trong ARC, bạn nên sử dụng __weak thay thế.

Bạn có thể tham khảo tài liệu apple để biết thông tin chi tiết.


6

__blocklà loại lưu trữ được sử dụng để biến các biến phạm vi thành biến đổi, thành thật hơn nếu bạn khai báo một biến với chỉ định này, tham chiếu của nó sẽ được chuyển đến các khối không phải là bản sao chỉ đọc để biết thêm chi tiết, xem Lập trình khối trong iOS


2

Hy vọng điều này sẽ giúp bạn

giả sử chúng ta có một mã như:

{
     int stackVariable = 1;

     blockName = ^()
     {
      stackVariable++;
     }
}

nó sẽ đưa ra một lỗi như "biến không thể gán được" bởi vì biến ngăn xếp bên trong khối theo mặc định là bất biến.

thêm __block (công cụ sửa đổi lưu trữ) trước phần khai báo làm cho nó có thể thay đổi bên trong khối tức là __block int stackVariable=1;


1

Từ Thông số ngôn ngữ khối :

Ngoài loại Khối mới, chúng tôi cũng giới thiệu vòng loại lưu trữ mới, __block, cho các biến cục bộ. [testme: a __block khai báo trong một khối chữ] Vòng loại lưu trữ __block loại trừ lẫn nhau cho tự động, đăng ký và tĩnh của vòng loại lưu trữ cục bộ. [Testme] Các biến đủ điều kiện của __block hoạt động như thể chúng được lưu trữ được phân bổ và bộ lưu trữ này là tự động phục hồi sau lần sử dụng cuối cùng của biến nói trên. Việc triển khai có thể chọn tối ưu hóa trong đó việc lưu trữ ban đầu là tự động và chỉ "di chuyển" sang lưu trữ được phân bổ (heap) trên một Block_copy của Khối tham chiếu. Các biến như vậy có thể bị đột biến như các biến thông thường.

Trong trường hợp biến __block là Khối, người ta phải giả sử rằng biến __block nằm trong bộ lưu trữ được phân bổ và như vậy được giả sử để tham chiếu Khối cũng nằm trong bộ lưu trữ được phân bổ (đó là kết quả của hoạt động Block_copy). Mặc dù điều này không có quy định để thực hiện Block_copy hoặc Block_release nếu việc triển khai cung cấp lưu trữ tự động ban đầu cho các Khối. Điều này là do điều kiện chủng tộc vốn có của một số luồng có khả năng cố gắng cập nhật biến được chia sẻ và nhu cầu đồng bộ hóa xung quanh việc xử lý các giá trị cũ và sao chép các giá trị mới. Đồng bộ hóa như vậy là vượt quá phạm vi của đặc điểm kỹ thuật ngôn ngữ này.

Để biết chi tiết về những gì một biến __block sẽ biên dịch thành, hãy xem Thông số thực hiện khối , phần 2.3.


Các liên kết của bạn đều đã chết
Ben Leggiero

Đây thực sự không phải là một câu trả lời và có thể được bổ sung hoặc loại bỏ. Cảm ơn!
Dan Rosenstark

0

Nó có nghĩa là biến nó là tiền tố có sẵn để được sử dụng trong một khối.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.