Apa arti kata kunci “__block”?

446

Apa sebenarnya arti __blockkata kunci dalam Objective-C? Saya tahu ini memungkinkan Anda untuk memodifikasi variabel dalam blok, tetapi saya ingin tahu ...

  1. Apa sebenarnya yang dikatakannya kepada kompiler?
  2. Apakah itu melakukan hal lain?
  3. Jika hanya itu yang dilakukannya, mengapa itu diperlukan?
  4. Apakah ada di dokumen di mana saja? (Saya tidak dapat menemukannya).
mjisrawi
sumber
3
periksa di sini , dan bagian "Blok dan variabel".
1
@ Kode Monyet: Saya bertanya secara spesifik tentang kata kunci, bukan sintaksis secara umum. Jadi jangan berpikir itu benar-benar duplikat.
mjisrawi
3
@ Kode Monyet: Tidak, ini bukan duplikat. Pertanyaan yang Anda sebutkan tidak berbicara __blocksama sekali.
DarkDust
3
Dan jika seseorang bertanya-tanya bagaimana Objective-C __blockharus diterjemahkan ke Swift: "Penutupan [dalam Swift] memiliki semantik tangkap yang sama dengan blok [di Objective-C] tetapi berbeda dalam satu cara kunci: Variabel lebih bisa diubah daripada disalin. Dengan kata lain, perilaku __block di Objective-C adalah perilaku default untuk variabel di Swift. " Dari buku Apple: Menggunakan Swift dengan Kakao dan Objective-C (Swift 2.2).
Jari Keinänen

Jawaban:

543

Ini memberitahu kompiler bahwa setiap variabel yang ditandai olehnya harus diperlakukan dengan cara khusus ketika digunakan di dalam blok. Biasanya, variabel dan isinya yang juga digunakan dalam blok disalin, sehingga setiap modifikasi yang dilakukan pada variabel-variabel ini tidak ditampilkan di luar blok. Ketika mereka ditandai dengan __block, modifikasi yang dilakukan di dalam blok juga terlihat di luarnya.

Untuk contoh dan info lebih lanjut, lihat Tipe Penyimpanan __block di Topik Pemrograman Blok Apple .

Contoh penting adalah yang ini:

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'
}

Dalam contoh ini, keduanya localCounterdan localCharacterdimodifikasi sebelum blok dipanggil. Namun, di dalam blok, hanya modifikasi yang localCharacterakan terlihat, berkat __blockkata kunci. Sebaliknya, blok dapat memodifikasi localCharacterdan modifikasi ini terlihat di luar blok.

DarkDust
sumber
8
Sangat bagus, penjelasan singkat, dan contoh yang sangat membantu. Terima kasih!
Evan Stone
1
Bagaimana aBlock memodifikasi localCounter? Tampaknya hanya memodifikasi CounterGlobal. Terima kasih
CommaToast
8
Itu tidak memodifikasi localCounter, tetapi itu memodifikasi localCharacter. Juga, perhatikan nilainya localCounterdi dalam blok: itu adalah 42, meskipun variabel bertambah sebelum blok dipanggil tetapi setelah blok dibuat (saat itulah nilainya "ditangkap").
DarkDust
1
Itu penjelasan yang membantu - meskipun - dapatkah Anda menjelaskan apa yang tampaknya merupakan pernyataan yang bertentangan dalam penjelasan Anda? Anda mengatakan di atas bahwa "aBlock memodifikasi ... localCounter" dan kemudian di komentar Anda mengatakan "[aBlock] TIDAK memodifikasi localCounter." Yang mana itu? Jika "tidak dimodifikasi" maka jawaban Anda harus diedit?
Praxiteles
2
Secara umum, vars tanpa __block akan ditangkap oleh nilai dan dimasukkan ke "lingkungan" blok, ketika blok dibuat. Tapi __block vars tidak akan ditangkap, kapan pun mereka digunakan di dalam atau di luar blok, mereka direferensikan apa adanya.
jchnxu
27

@bbum mencakup blok secara mendalam dalam posting blog dan menyentuh jenis penyimpanan __block.

__block adalah tipe penyimpanan yang berbeda

Sama seperti statis, otomatis, dan tidak stabil, __block adalah jenis penyimpanan. Ini memberitahu kompiler bahwa penyimpanan variabel harus dikelola secara berbeda.

...

Namun, untuk variabel __block, blok tidak mempertahankan. Terserah Anda untuk mempertahankan dan melepaskan, sesuai kebutuhan.
...

Adapun kasus penggunaan Anda akan menemukan __blockkadang-kadang digunakan untuk menghindari mempertahankan siklus karena tidak mempertahankan argumen. Contoh umum menggunakan self.

//Now using myself inside a block will not 
//retain the value therefore breaking a
//possible retain cycle.
__block id myself = self;
Joe
sumber
Lihat posting ini untuk info lebih lanjut tentang masalah siklus pertahankan: benscheirman.com/2012/01/… . Apakah __weakcukup dalam kasus khusus ini juga? Mungkin sedikit lebih jelas ...
Hari Karam Singh
17
Akhirnya klaim bahwa __block dapat digunakan untuk menghindari siklus referensi yang kuat (alias mempertahankan siklus) jelas salah dalam konteks ARC. Karena fakta bahwa dalam ARC __block menyebabkan variabel menjadi sangat direferensikan, itu sebenarnya lebih cenderung menyebabkan mereka. stackoverflow.com/a/19228179/189006
Krishnan
10

Biasanya ketika Anda tidak menggunakan __block, blok akan menyalin (mempertahankan) variabel, jadi meskipun Anda memodifikasi variabel, blok memiliki akses ke objek yang lama.

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

Dalam 2 kasus ini Anda perlu __block:

1.Jika Anda ingin memodifikasi variabel di dalam blok dan mengharapkannya terlihat di luar:

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

2.Jika Anda ingin memodifikasi variabel setelah Anda mendeklarasikan blok dan Anda mengharapkan blok untuk melihat perubahan:

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

__block adalah kualifikasi penyimpanan yang dapat digunakan dalam dua cara:

  1. Menandai bahwa suatu variabel tinggal di penyimpanan yang dibagi antara ruang lingkup leksikal dari variabel asli dan blok apa pun yang dinyatakan dalam ruang lingkup itu. Dan dentang akan menghasilkan struct untuk mewakili variabel ini, dan menggunakan struct ini dengan referensi (bukan oleh nilai).

  2. Dalam MRC, __block dapat digunakan untuk menghindari mempertahankan variabel objek yang ditangkap blok. Berhati-hatilah karena ini tidak bekerja untuk ARC. Di ARC, Anda harus menggunakan __weak sebagai gantinya.

Anda dapat merujuk ke dokumen Apple untuk informasi terperinci.

Mindy
sumber
6

__blockadalah tipe penyimpanan yang digunakan untuk membuat variabel ruang lingkup bisa berubah, lebih jelasnya jika Anda mendeklarasikan variabel dengan specifier ini, rujukannya akan diteruskan ke blok bukan salinan hanya baca untuk detail selengkapnya lihat Blok Pemrograman di iOS

mithilesh
sumber
2

Semoga ini bisa membantu Anda

misalkan kita memiliki kode seperti:

{
     int stackVariable = 1;

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

itu akan memberikan kesalahan seperti "variabel tidak dapat ditentukan" karena variabel tumpukan di dalam blok secara default tidak dapat diubah.

menambahkan __block (pengubah penyimpanan) di depannya deklarasi membuatnya bisa berubah di dalam blok yaitu __block int stackVariable=1;

Anurag Bhakuni
sumber
1

Dari Spesifikasi Bahasa Blok :

Selain tipe Blok baru kami juga memperkenalkan kualifikasi penyimpanan baru, __block, untuk variabel lokal. [testme: pernyataan __block dalam blok literal] Kualifikasi penyimpanan __block saling eksklusif untuk kualifikasi, penyimpanan, dan statis penyimpanan lokal yang ada. [testme] Variabel yang memenuhi syarat oleh __block bertindak seolah-olah mereka berada dalam penyimpanan yang dialokasikan dan penyimpanan ini adalah secara otomatis pulih setelah penggunaan terakhir dari variabel tersebut. Implementasi dapat memilih optimasi di mana penyimpanan pada awalnya otomatis dan hanya "dipindahkan" ke penyimpanan (tumpukan) yang dialokasikan pada Block_copy dari Block referensi. Variabel seperti itu dapat dimutasi seperti variabel normal.

Dalam kasus di mana variabel __block adalah Blok satu harus mengasumsikan bahwa variabel __block berada di penyimpanan yang dialokasikan dan dengan demikian diasumsikan untuk merujuk Blok yang juga dalam penyimpanan yang dialokasikan (bahwa itu adalah hasil dari operasi Block_copy). Meskipun demikian, tidak ada ketentuan untuk melakukan Block_copy atau Block_release jika implementasi menyediakan penyimpanan otomatis awal untuk Blok. Ini disebabkan oleh kondisi ras inheren dari beberapa utas yang berpotensi mencoba memperbarui variabel yang dibagikan dan perlunya sinkronisasi seputar pelepasan nilai yang lebih lama dan menyalin yang baru. Sinkronisasi semacam itu berada di luar ruang lingkup spesifikasi bahasa ini.

Untuk perincian tentang apa yang harus dikompilasi oleh variabel __block, lihat Spesifikasi Implementasi Blok , bagian 2.3.

Martin Gordon
sumber
Tautan Anda keduanya mati
Ben Leggiero
Ini sebenarnya bukan jawaban dan dapat disempurnakan atau dihapus. Terima kasih!
Dan Rosenstark
0

Ini berarti bahwa variabel itu adalah awalan untuk tersedia untuk digunakan dalam suatu blok.

Rich Allen
sumber