คำหลัก“ __block” หมายถึงอะไร


446

สิ่งที่ว่าไม่__blockคำหลักใน Objective-C หมายถึง? ฉันรู้ว่ามันช่วยให้คุณสามารถปรับเปลี่ยนตัวแปรภายในบล็อกได้ แต่ฉันอยากรู้ว่า ...

  1. มันบอกอะไรกับคอมไพเลอร์?
  2. มันทำอะไรอย่างอื่นได้ไหม?
  3. หากเป็นเช่นนั้นแล้วทำไมจึงเป็นสิ่งจำเป็นในตอนแรก
  4. มันอยู่ในเอกสารทุกที่หรือไม่? (หาไม่เจอ)

3
ตรวจสอบที่นี่และส่วน "บล็อกและตัวแปร"

1
มีความเป็นไปได้ที่ซ้ำกันของไวยากรณ์บล็อก Objective-C - มีคนอธิบายได้ไหม

1
@ รหัสลิง: ฉันถามเฉพาะเกี่ยวกับคำหลักไม่ใช่ไวยากรณ์โดยทั่วไป ดังนั้นอย่าคิดว่ามันซ้ำซ้อนจริงๆ
mjisrawi

3
@ รหัสลิง: ไม่นี่ไม่ใช่เรื่องซ้ำ คำถามที่คุณพูดถึงไม่ได้พูดถึง__blockเลย
DarkDust

3
และถ้ามีคนสงสัยว่า Objective-C __blockควรแปลเป็น Swift อย่างไร: "การปิด [ในสวิฟท์] มีความหมายคล้ายการจับเป็นบล็อก [ใน Objective-C] แต่แตกต่างกันในวิธีสำคัญ: ตัวแปรแปรปรวนแทนที่จะคัดลอก กล่าวอีกนัยหนึ่งพฤติกรรมของ __block ใน Objective-C เป็นพฤติกรรมเริ่มต้นสำหรับตัวแปรใน Swift” จากหนังสือของ Apple: การใช้ Swift กับ Cocoa และ Objective-C (Swift 2.2)
Jari Keinänen

คำตอบ:


543

มันบอกคอมไพเลอร์ว่าตัวแปรใด ๆ ที่ทำเครื่องหมายโดยมันจะต้องได้รับการปฏิบัติด้วยวิธีพิเศษเมื่อมีการใช้ภายในบล็อก โดยปกติแล้วตัวแปรและเนื้อหาที่ใช้ในบล็อกจะถูกคัดลอกดังนั้นการแก้ไขใด ๆ ที่ทำกับตัวแปรเหล่านี้จะไม่แสดงนอกบล็อก เมื่อพวกเขาถูกทำเครื่องหมายด้วย__blockการแก้ไขที่ทำไว้ภายในบล็อกจะมองเห็นได้จากภายนอก

สำหรับตัวอย่างและข้อมูลเพิ่มเติมดูที่ __block Storage Typeในหัวข้อการเขียนโปรแกรมบล็อกของ Appleหัวข้อ

ตัวอย่างที่สำคัญคือสิ่งนี้:

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

ในตัวอย่างนี้ทั้งสองlocalCounterและlocalCharacterถูกแก้ไขก่อนที่บล็อกจะถูกเรียก อย่างไรก็ตามในบล็อกมีเพียงการปรับเปลี่ยนที่localCharacterจะมองเห็นได้ด้วย__blockคำหลัก ในทางกลับกันบล็อกสามารถแก้ไขได้localCharacterและการแก้ไขนี้สามารถมองเห็นได้ภายนอกบล็อก


8
คำอธิบายที่ยอดเยี่ยมกระชับและเป็นตัวอย่างที่มีประโยชน์มาก ขอบคุณ!
Evan Stone

1
aBlock แก้ไข localCounter อย่างไร ดูเหมือนว่าจะแก้ไข CounterGlobal เท่านั้น ขอบคุณ
CommaToast

8
มันไม่ได้ปรับเปลี่ยนแต่มันไม่ปรับเปลี่ยนlocalCounter localCharacterนอกจากนี้ให้ความสนใจกับค่าที่localCounterมีอยู่ในบล็อกนั่นคือ 42 ถึงแม้ว่าตัวแปรจะเพิ่มขึ้นก่อนที่จะมีการเรียกบล็อก แต่หลังจากบล็อกถูกสร้างขึ้น (นั่นคือเมื่อค่าได้รับ "จับ")
DarkDust

1
นั่นเป็นคำอธิบายที่เป็นประโยชน์ แต่คุณสามารถอธิบายสิ่งที่ดูเหมือนจะเป็นข้อความขัดแย้งในคำอธิบายของคุณได้ไหม คุณพูดข้างต้นว่า "aBlock แก้ไข ... localCounter" และจากความคิดเห็นที่คุณพูดว่า "[aBlock] ไม่ได้แก้ไข localCounter" มันคืออะไร หากเป็น "ไม่แก้ไข" คำตอบของคุณควรได้รับการแก้ไขไหม?
Praxiteles

2
โดยทั่วไปแล้ว vars ที่ไม่มี __block จะถูกจับด้วยค่าและบรรจุลงใน "สิ่งแวดล้อม" ของบล็อกเมื่อสร้างบล็อก แต่จะไม่ถูกยึด __block vars ทุกครั้งที่มีการใช้ภายในหรือภายนอกบล็อกพวกเขาจะถูกอ้างอิงเหมือนเดิม
jchnxu

27

@bbum ครอบคลุมบล็อกในเชิงลึกในโพสต์บล็อกและสัมผัสกับประเภทการจัดเก็บข้อมูล __block

__block เป็นประเภทการจัดเก็บข้อมูลที่แตกต่างกัน

__block เป็นประเภทการจัดเก็บข้อมูลเช่นเดียวกับคงที่อัตโนมัติและระเหยได้ มันบอกคอมไพเลอร์ว่าการจัดเก็บของตัวแปรจะถูกจัดการแตกต่างกัน

...

อย่างไรก็ตามสำหรับตัวแปร __block บล็อกจะไม่ถูกเก็บไว้ มันขึ้นอยู่กับคุณที่จะเก็บและปล่อยตามที่ต้องการ
...

สำหรับกรณีการใช้งานบางครั้งคุณจะพบว่า__blockมีการใช้เพื่อหลีกเลี่ยงการเก็บรอบเพราะมันไม่ได้เก็บอาร์กิวเมนต์ ตัวอย่างทั่วไปคือการใช้ตัวเอง

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

เห็นโพสต์นี้สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการรักษาปัญหาวงจร: benscheirman.com/2012/01/... จะ__weakพอเพียงในกรณีเฉพาะนี้เช่นกัน? อาจจะชัดเจนกว่านี้เล็กน้อย ...
Hari Karam Singh

17
ในที่สุดการอ้างสิทธิ์ว่า __block สามารถใช้เพื่อหลีกเลี่ยงรอบการอ้างอิงที่รัดกุม (aka รอบการรักษา) นั้นผิดปกติในบริบท ARC เนื่องจากข้อเท็จจริงที่ว่าใน ARC __block ทำให้เกิดการอ้างอิงตัวแปรอย่างมากจึงมีแนวโน้มที่จะทำให้เกิดตัวแปรดังกล่าว stackoverflow.com/a/19228179/189006
Krishnan

10

โดยปกติเมื่อคุณไม่ได้ใช้ __block บล็อกจะคัดลอก (เก็บไว้) ตัวแปรดังนั้นแม้ว่าคุณจะแก้ไขตัวแปรบล็อกจะสามารถเข้าถึงวัตถุเก่าได้

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

ใน 2 กรณีนี้คุณต้องมี __block:

1. หากคุณต้องการแก้ไขตัวแปรภายในบล็อกและคาดว่าจะสามารถมองเห็นได้นอก:

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

2. ถ้าคุณต้องการแก้ไขตัวแปรหลังจากคุณประกาศบล็อกและคุณคาดว่าบล็อกจะเห็นการเปลี่ยนแปลง:

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

8

__blockเป็นตัวระบุหน่วยเก็บข้อมูลที่สามารถใช้ได้สองวิธี:

  1. ทำเครื่องหมายว่าตัวแปรอาศัยอยู่ในหน่วยเก็บข้อมูลที่ใช้ร่วมกันระหว่างขอบเขตศัพท์ของตัวแปรดั้งเดิมและบล็อกใด ๆ ที่ประกาศภายในขอบเขตนั้น และเสียงดังกราวจะสร้าง struct เพื่อเป็นตัวแทนของตัวแปรนี้และใช้ struct นี้โดยการอ้างอิง (ไม่ใช่ตามค่า)

  2. ใน MRC นั้น__blockสามารถใช้เพื่อหลีกเลี่ยงการเก็บรักษาตัวแปรของวัตถุที่บล็อกได้ ระวังว่านี่ไม่ได้ผลกับ ARC ใน ARC คุณควรใช้__ อ่อนแอแทน

คุณสามารถอ้างถึงApple docสำหรับข้อมูลรายละเอียด


6

__blockเป็นประเภทหน่วยเก็บข้อมูลที่ใช้สร้างตัวแปรขอบเขตไม่แน่นอนมากขึ้นถ้าคุณประกาศตัวแปรด้วยตัวระบุนี้การอ้างอิงของมันจะถูกส่งผ่านไปยังบล็อกไม่ใช่การคัดลอกแบบอ่านอย่างเดียวสำหรับรายละเอียดเพิ่มเติมดูบล็อกการเขียนโปรแกรมใน iOS


2

หวังว่านี่จะช่วยคุณได้

สมมติว่าเรามีรหัสเช่น:

{
     int stackVariable = 1;

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

มันจะทำให้เกิดข้อผิดพลาดเช่น "ตัวแปรไม่สามารถกำหนดได้" เนื่องจากตัวแปรสแต็กภายในบล็อกนั้นเป็นค่าเริ่มต้นที่ไม่เปลี่ยนรูป

เพิ่ม __block (ตัวดัดแปลงที่เก็บข้อมูล) ล่วงหน้าการประกาศทำให้ไม่สามารถเปลี่ยนแปลงได้ภายในบล็อกเช่น __block int stackVariable=1;


1

จากข้อมูลจำเพาะภาษาบล็อก :

นอกจากประเภทบล็อกใหม่แล้วเรายังแนะนำตัวจัดเก็บข้อมูลใหม่ __block สำหรับตัวแปรท้องถิ่น [testme: การประกาศ __block ภายในตัวอักษรบล็อก] คุณสมบัติการจัดเก็บข้อมูล __block เป็นสิ่งที่เกิดขึ้นร่วมกันโดยอัตโนมัติกับตัวระบุที่จัดเก็บแบบโลคัลที่มีอยู่โดยอัตโนมัติลงทะเบียนและคงที่ [testme] ตัวแปรที่ผ่านการรับรองโดย __block กู้คืนโดยอัตโนมัติหลังจากการใช้ตัวแปรดังกล่าวครั้งสุดท้าย การนำไปใช้อาจเลือกการปรับให้เหมาะสมที่เริ่มต้นการจัดเก็บโดยอัตโนมัติและ "ย้าย" ไปยังที่เก็บข้อมูลที่จัดสรร (ฮีป) บน Block_copy ของการอ้างอิงบล็อกเท่านั้น ตัวแปรดังกล่าวอาจกลายพันธุ์ได้ตามตัวแปรปกติ

ในกรณีที่ตัวแปร __block เป็น Block หนึ่งจะต้องสมมติว่าตัวแปร __block นั้นอยู่ในที่เก็บข้อมูลที่จัดสรรและจะถือว่าเป็นการอ้างอิง Block ที่อยู่ในที่เก็บข้อมูลที่จัดสรร (นั่นคือผลลัพธ์ของการดำเนินการ Block_copy) แม้จะมีสิ่งนี้ไม่มีบทบัญญัติให้ทำ Block_copy หรือ Block_release หากการใช้งานมีการจัดเก็บข้อมูลเริ่มต้นอัตโนมัติสำหรับบล็อก นี่เป็นเพราะสภาพการแย่งชิงโดยธรรมชาติของหลายเธรดที่อาจพยายามอัพเดตตัวแปรที่ใช้ร่วมกันและความจำเป็นในการซิงโครไนซ์รอบการทิ้งค่าเก่าและคัดลอกใหม่ การซิงโครไนซ์ดังกล่าวอยู่นอกเหนือขอบเขตของข้อกำหนดภาษานี้

สำหรับรายละเอียดเกี่ยวกับสิ่งที่ตัวแปร __block ควรรวบรวมไว้ให้ดูที่ข้อกำหนดการใช้งานบล็อกส่วน 2.3


ลิงก์ของคุณนั้นตายแล้ว
Ben Leggiero

นี่ไม่ใช่คำตอบที่แท้จริงและสามารถเอาออกหรือเอาออกได้ ขอบคุณ!
Dan Rosenstark

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.