“__Block” anahtar kelimesi ne anlama geliyor?


446

__blockObjective-C anahtar kelimesi tam olarak ne anlama geliyor? Bloklardaki değişkenleri değiştirmenize izin verdiğini biliyorum, ama bilmek istiyorum ...

  1. Derleyiciye tam olarak ne anlatıyor?
  2. Başka bir şey yapıyor mu?
  3. Eğer hepsi bu kadarsa, neden ilk etapta ihtiyaç duyulur?
  4. Belgelerde herhangi bir yerde mi? (Bulamıyorum).

3
kontrol burada ve "Bloklar ve değişkenler" bölümü.


1
@Code Monkey: Genel olarak sözdizimi değil, özellikle anahtar kelimeyi soruyordum. Yani bunun gerçekten bir kopya olduğunu düşünmeyin.
mjisrawi

3
@Kod Maymun: Hayır, bu bir kopya değil. Bahsettiğiniz soru hiç konuşmuyor __block.
DarkDust

3
Birisi Objective- C'lerin__block Swift'e nasıl çevrilmesi gerektiğini merak ederse : ”[Swift'teki] kapanışlar [Objective-C'deki] bloklarla benzer yakalama semantiğine sahip ancak tek bir anahtar yolla farklılık gösteriyor: Değişkenler kopyalanmak yerine değiştirilebilir. Başka bir deyişle, Objective-C'deki __block davranışı Swift'teki değişkenler için varsayılan davranıştır. ” Apple'ın kitabından: Swift'i Kakao ve Objective-C ile kullanma (Swift 2.2).
Jari Keinänen

Yanıtlar:


544

Derleyiciye, işaretli herhangi bir değişkenin bir blok içinde kullanıldığında özel bir şekilde ele alınması gerektiğini söyler. Normalde, bloklarda da kullanılan değişkenler ve içerikleri kopyalanır, dolayısıyla bu değişkenlerde yapılan herhangi bir değişiklik blok dışında gösterilmez. İşaretli olduklarında __block, blok içinde yapılan değişiklikler de onun dışında görülebilir.

Örnek ve daha fazla bilgi için Apple'ın Blok Programlama Konuları'ndaki __block Depolama Türü konusuna bakın .

Önemli örnek şudur:

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

Bu örnekte, her ikisi de localCounterve localCharacterblok çağrılmadan önce modifiye edilir. Ancak, blok içinde, sadece anahtar kelime localCharactersayesinde yapılacak değişiklik görülebilir __block. Tersine, blok değişebilir localCharacterve bu değişiklik blok dışında görülebilir.


8
Mükemmel, özlü bir açıklama ve çok yararlı bir örnek. Teşekkürler!
Evan Stone

1
ABlock localCounter'ı nasıl değiştirir? Sadece CounterGlobal'ı değiştiriyor gibi görünüyor. Teşekkürler
CommaToast

8
Değiştirmez localCounter, ancak değiştirir localCharacter. Ayrıca, localCounterbloktaki değere dikkat edin : blok çağırılmadan önce değişken artmasına rağmen , blok oluşturulduktan sonra (bu değer "yakalandığında") 42'dir .
DarkDust

1
Bu yararlı bir açıklama olsa da, açıklamanızda çelişkili ifadelerin neler olduğunu açıklayabilir misiniz? Yukarıda "aBlock değiştirir ... localCounter" ve sonra yorumlarda "[aBlock] localCounter değiştirmez" diyorsunuz. Hangisi? Eğer "değiştirilmemiş" ise cevabınız düzenlenmeli mi?
Praxiteles

2
Genel olarak, __block içermeyen değişkenler değere göre yakalanır ve blok oluşturulduğunda bloğun "ortamına" paketlenir. Ancak __block vars yakalanmayacaktır, bir bloğun içinde veya dışında kullanıldıklarında, oldukları gibi referans alırlar.
jchnxu

28

@bbum, bir blog yayınındaki blokları derinlemesine kapsar ve __block depolama türüne dokunur.

__block farklı bir depolama türüdür

Statik, otomatik ve geçici gibi __block bir depolama türüdür. Derleyiciye değişkenin depolamasının farklı yönetileceğini söyler.

...

Ancak, __block değişkenleri için blok korunmaz. Gerektiğinde saklamak ve bırakmak size kalmıştır.
...

Kullanım durumlarına gelince __block, argümanı korumadığından bazen döngüleri önlemek için kullanılır. Yaygın bir örnek kendini kullanmaktır.

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

Koruma döngüsü sorunu hakkında daha fazla bilgi için bu gönderiye bakın : benscheirman.com/2012/01/… . Misiniz __weakde bu belirli durumda yeterli? Belki biraz daha net ...
Hari Karam Singh

17
Son olarak, __block'un güçlü referans döngülerinden (sabit tutma döngüleri) kaçınmak için kullanılabileceği iddiası bir ARC bağlamında basit bir şekilde yanlıştır. ARC __block içinde değişkenin güçlü bir şekilde referans alınmasına neden olduğundan, aslında bunlara neden olma olasılığı daha yüksektir. stackoverflow.com/a/19228179/189006
Krishnan

10

Normalde __block kullanmadığınızda, blok değişkeni kopyalar (korur), bu nedenle değişkeni değiştirseniz bile blok eski nesneye erişebilir.

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

Bu 2 durumda __block'a ihtiyacınız var:

Blok içindeki değişkeni değiştirmek ve dışarıda görünmesini beklemek istiyorsanız:

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

2. bloğu bildirdikten sonra değişkeni değiştirmek istiyorsanız ve bloğun değişikliği görmesini bekliyorsanız:

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

8

__block iki şekilde kullanılabilen bir depolama niteleyicisidir:

  1. Bir değişkenin, orijinal değişkenin sözcüksel kapsamı ile bu kapsamda bildirilen tüm bloklar arasında paylaşılan bir depolama biriminde yaşadığını belirtir. Clang, bu değişkeni temsil etmek için bir yapı oluşturur ve bu yapıyı referans olarak kullanır (değere göre değil).

  2. MRC olarak, __block nesne değişkenler blok yakalar muhafaza önlemek için kullanılabilir. Bunun ARC için çalışmadığına dikkat edin. ARC, kullanmak gerekir __weak yerine.

Ayrıntılı bilgi için apple doc'ye başvurabilirsiniz .


6

__blockkapsam değişkenlerini değişken hale getirmek için kullanılan bir depolama türüdür, bu belirteciyle bir değişken bildirirseniz, referansı daha fazla ayrıntı için salt okunur bir kopya değil bloklara geçirilir. bkz . iOS'ta Blok Programlama


2

umarım bu sana yardımcı olur

varsayalım ki şöyle bir kodumuz var:

{
     int stackVariable = 1;

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

"değişken atanamaz" gibi bir hata verecektir çünkü blok içindeki yığın değişkeni varsayılan olarak değiştirilemez.

önüne __block (depolama değiştirici) eklenmesi bildirimi blok içinde değiştirilebilir hale getirir. __block int stackVariable=1;


1

Gönderen Blok Dil Spec :

Yeni Blok türüne ek olarak, yerel değişkenler için yeni bir depolama niteleyicisi olan __block da sunuyoruz. [testme: blok hazır bilgisi içinde bir __block bildirimi] __block depolama niteleyicisi, mevcut yerel depolama niteleyicileri otomatik, kayıt ve statik olarak karşılıklı olarak ayrıcalıklıdır. [testme] __block tarafından nitelenen değişkenler, ayrılmış depolama alanındaymış gibi davranır ve bahsedilen değişkenin son kullanımından sonra otomatik olarak geri kazanılır. Bir uygulama, depolamanın başlangıçta otomatik olduğu ve bir referans Bloğunun Block_copy'si üzerine ayrılan (yığın) depolamaya "taşındığı" bir optimizasyon seçebilir. Bu değişkenler, normal değişkenler gibi mutasyona uğratılabilir.

Bir __block değişkeninin bir Blok olması durumunda, __block değişkeninin ayrılan depoda bulunduğunu ve bu nedenle de ayrılan depoda olan bir Bloğa referansta bulunduğu varsayılmalıdır (bunun bir Block_copy işleminin sonucu olduğu). Buna rağmen, bir uygulama Bloklar için başlangıçta otomatik depolama sağlıyorsa bir Block_copy veya Block_release yapmak için herhangi bir hüküm yoktur. Bunun nedeni, paylaşılan değişkeni güncellemeye çalışan potansiyel olarak birkaç iş parçacığının doğal yarış durumu ve eski değerlerin atılması ve yenilerinin kopyalanması sırasında senkronizasyon gereksinimidir. Bu tür bir senkronizasyon, bu dil spesifikasyonunun kapsamı dışındadır.

__Block değişkeninin neyi derlemesi gerektiğiyle ilgili ayrıntılar için bkz. Blok Uygulama Spesifikasyonu , bölüm 2.3.


Bağlantılarınız her ikisi de ölü
Ben Leggiero

Bu gerçekten bir cevap değil ve etli olabilir ya da kaldırılabilir. Teşekkürler!
Dan Rosenstark

0

Önek olduğu değişkenin bir blok içinde kullanılabileceği anlamına gelir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.