เครื่องหมายขีดล่างหน้าตัวแปรในคลาสโกโก้ที่มีวัตถุประสงค์เป็นอย่างไร


157

ฉันเคยเห็นในตัวอย่าง iPhone สองสามตัวอย่างที่คุณลักษณะได้ใช้เครื่องหมายขีดล่าง _ หน้าตัวแปร ไม่มีใครรู้ว่าสิ่งนี้หมายความว่าอย่างไร หรือมันทำงานอย่างไร

ไฟล์ส่วนต่อประสานที่ฉันใช้อยู่ดูเหมือนว่า:

@interface MissionCell : UITableViewCell {
    Mission *_mission;
    UILabel *_missionName;
}

@property (nonatomic, retain) UILabel *missionName;

- (Mission *)mission;

ฉันไม่แน่ใจว่าสิ่งที่กล่าวมาข้างต้นทำอะไร แต่เมื่อฉันพยายามตั้งชื่อภารกิจดังนี้

aMission.missionName = missionName;

ฉันได้รับข้อผิดพลาด:

ขอชื่อ 'MissionName' ของสมาชิกในบางสิ่งที่ไม่ใช่โครงสร้างหรือสหภาพ

คำตอบ:


97

หากคุณใช้คำนำหน้าขีดเส้นใต้สำหรับ ivars ของคุณ (ซึ่งไม่มีอะไรมากกว่าแบบแผนทั่วไป แต่มีประโยชน์) คุณต้องทำสิ่งพิเศษ 1 อย่างดังนั้น accessor ที่สร้างขึ้นโดยอัตโนมัติ ในไฟล์การนำไปใช้งานของคุณคุณsynthesizeควรมีลักษณะดังนี้:

@synthesize missionName = _missionName;

โดยทั่วไปมากขึ้นนี่คือ:

@synthesize propertyName = _ivarName;

78
ด้วยคุณสมบัติการสังเคราะห์อัตโนมัตินี้ไม่จำเป็นอีกต่อไป Xcode สังเคราะห์ @property xxxx โดยมี ivar ชื่อ _xxxx อยู่ด้านหลัง เรียบร้อย
LearnCocos2D

@ LearnCocos2D สวัสดี! มือใหม่สำหรับ iOS ที่นี่และมีบางสิ่งที่ฉันต้องการชี้แจง สำหรับทุกเวลานี้สิ่งที่ฉันไม่ได้ประกาศpropertyในแฟ้ม .h และใน .m เหม่ฉันเข้าถึงได้โดยใช้เหมือนเช่นนั้นself self.somePropertyนี่เป็นวิธีที่ถูกต้องหรือไม่ หรือฉันควรใช้ ivars ในรหัส?
Isuru

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

Noob คำถาม: ทำไมไม่ใช้ ivars โดยตรง? เหตุใดฉันจึงควรประกาศแยกต่างหากเพื่อเก็บ ivar
อัลเลน

1
@ อัลเลนถ้าฉันเข้าใจคำถามของคุณถูกต้อง: var ที่แยกต่างหากที่คุณกำลังประกาศคือตัวชี้ไปยังตัวแปรจริง นี่เป็นสิ่งสำคัญด้วยเหตุผลสองสามประการ (ที่ฉันรู้) ประการแรกเมื่อคุณส่งตัวชี้ไปยังฟังก์ชันที่คุณไม่ได้ทำซ้ำมันเป็นคุณค่า คุณเพียงแค่บอกฟังก์ชั่นที่จะหาค่าที่จะใช้ สิ่งนี้จะช่วยให้หน่วยความจำที่คุณใช้ต่ำ (และยังช่วยในการจัดสรรและยกเลิกการจัดสรรหน่วยความจำซึ่งเป็นสิ่งสำคัญในกรณีที่ไม่มี 'การรวบรวมขยะ' ซึ่งคุณจะพบใน Java)
David Sigley

18

มันเป็นแค่แบบแผนสำหรับการอ่านมันไม่ได้ทำอะไรเป็นพิเศษกับคอมไพเลอร์ คุณจะเห็นคนใช้กับตัวแปรอินสแตนซ์ส่วนตัวและชื่อวิธี Apple แนะนำให้อย่าใช้ขีดเส้นใต้ (ถ้าคุณไม่ระวังคุณสามารถลบล้างบางอย่างใน superclass ของคุณ) แต่คุณไม่ควรรู้สึกแย่กับการเพิกเฉยคำแนะนำนั้น :)


19
จากสิ่งที่ฉันเข้าใจ Apple แนะนำไม่ให้ใช้คำนำหน้าขีดล่างบนชื่อเมธอด (พวกเขาสงวนไว้สำหรับตัวเองเป็นแบบแผนสำหรับวิธีส่วนตัว) แต่พวกเขาไม่มีคำแนะนำดังกล่าวเกี่ยวกับชื่อตัวแปรอินสแตนซ์
Kelan

9
@Kelan อันที่จริงแล้ว Apple สนับสนุนให้ทำเช่นนั้น : "โดยปกติแล้วคุณไม่ควรเข้าถึงตัวแปรอินสแตนซ์โดยตรงแทนคุณควรใช้วิธีการเข้าถึง (คุณเข้าถึงตัวแปรอินสแตนซ์โดยตรงในวิธี init และ dealloc) เพื่อช่วยส่งสัญญาณนี้อินสแตนซ์คำนำหน้า ชื่อตัวแปรที่มีเครื่องหมายขีดล่าง (_) ตัวอย่างเช่น: \ @implementation MyClass {BOOL _showsTitle;} "
dmirkitanov

ฉันไม่คิดว่าจริง ๆ แล้วแอปเปิ้ลสนับสนุนให้เราทำเช่นนั้นเนื่องจากโค้ดตัวอย่างทั้งหมดของพวกเขาเองใน iOS Developer Library ไม่มี ( ) อยู่ในนั้น Apple ยังกล่าวว่าพวกเขาได้สงวนไว้ซึ่งต้องหมายความว่าพวกเขาใช้ภายในสำหรับกรอบของตัวเองเช่น UIKit เป็นต้นซึ่งเป็นเหตุผลว่าทำไมเราไม่ควรใช้อย่างระมัดระวัง แต่ฉันเห็นว่าในลิงก์ที่คุณระบุ @kelan พวกเขาพูดจริง ๆ ใน "ประวัติการแก้ไข" ว่ามันเป็น "เหมาะสม" ที่จะใช้ ( ) ฉันตีความว่าเป็น "เรา" สามารถใช้งานได้ถ้าเราต้องการ
WYS

เอกสารแอปเปิ้ลที่บอกว่าจะไม่ใช้คำนำหน้าขีดชื่อเป็นวิธีการที่นี่
ThomasW

9

วัตถุประสงค์ที่เป็นประโยชน์เพียงอย่างเดียวที่ฉันได้เห็นคือการแยกแยะความแตกต่างระหว่างตัวแปรท้องถิ่นและตัวแปรสมาชิกตามที่ระบุไว้ข้างต้น แต่ไม่ใช่แบบแผนที่จำเป็น เมื่อจับคู่กับ @property จะเพิ่มความฟุ้งซ่านของการสังเคราะห์คำสั่ง - @synthesize missionName = _missionName;และน่าเกลียดทุกที่

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

-(void)setMyString:(NSString*)_myString
{
    myString = _myString;
}

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


มันเป็นวิธีอื่น ๆ ตัวแปรส่วนตัวถูกขีดเส้นใต้ ทรัพย์สินไม่ และเมื่อใดที่พวกเขาสังเคราะห์คุณเข้าคู่กัน
Justin

นั่นคือสิ่งที่ฉันอธิบายยกเว้นว่าฉันเรียกมันว่า "ตัวแปรสมาชิก" แทนที่จะเป็น "ตัวแปรส่วนตัว"
Peter DeWeese

อุ๊ย! สิ่งนี้กำลังถามปัญหา…การสังเคราะห์อัตโนมัติจะทำให้ ivar _myString ซึ่งหมายความว่า setter ของคุณไม่ทำงาน (เพราะจะไม่สามารถบอก ivar ของคุณได้จากพารามิเตอร์เมธอด)
geowar

ถูกต้องซึ่งเป็นสาเหตุที่ฉันเพิ่มการแก้ไขในตอนท้ายเมื่อ Apple เพิ่มการสังเคราะห์อัตโนมัติ
Peter DeWeese

5

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

สำหรับข้อผิดพลาดดูเหมือน aMission มีประเภทผิด มันประกาศอะไร?


เป็นเรื่องปกติใน IDE ด้วย IntelliSense มันจะทำให้ตัวแปรสมาชิก / โมดูล / คลาสของคุณแสดงที่ด้านบนของรายการ คำต่อท้ายทั่วไปอีกตัวคือ "m_"
STW

1
ถ้ามันไม่ได้หมายความว่าคุณจะสลับไปมาระหว่าง _missionName และ missionName เหมือนในตัวอย่างด้านบนได้อย่างไร การประกาศของฉันดูเหมือนว่า: Mission * aMission = [[การจัดสรรภารกิจ] init]; aMission.missionName = @ "a Mission";
Atma

1
หนึ่งคือตัวแปรอินสแตนซ์และอื่น ๆ เป็นคุณสมบัติ คุณไม่สามารถเข้าถึงตัวแปรอินสแตนซ์ด้วยไวยากรณ์เช่น aMission.missionName เนื่องจากไวยากรณ์นั้นไม่สามารถทำงานกับพอยน์เตอร์ได้
Chuck

นอกจากนี้โปรดทราบว่าคุณกำลังพยายามใช้งานบนวัตถุ Mission แต่ส่วนต่อประสานที่คุณโพสต์ด้วยคุณสมบัติ MissionName คือ MissionCell
Smorgan

2

นี่เป็นเพียงการตั้งชื่อของคุณสมบัติการสังเคราะห์

เมื่อคุณสังเคราะห์ตัวแปรในไฟล์. m Xcode จะให้ข้อมูล _variable ที่ชาญฉลาดให้คุณโดยอัตโนมัติ


1

มีการขีดเส้นใต้ไม่เพียง แต่ทำให้มันเป็นไปได้ในการแก้ไข ivars ของคุณโดยไม่ต้องหันไปใช้self.memberไวยากรณ์ แต่มันทำให้รหัสของคุณอ่านได้มากขึ้นเพราะคุณรู้ว่าเมื่อตัวแปรเป็น Ivar (เพราะคำนำหน้าขีดของมัน) หรืออาร์กิวเมนต์สมาชิก (ไม่มีขีด )

ตัวอย่าง:

- (void) displayImage: (UIImage *) image {

    if (image != nil) {
        // Display the passed image...
        [_imageView setImage: image];
    } else {
        // fall back on the default image...
        [_imageView setImage: _image];
    }
}

ในตัวอย่างนี้จะเป็นการดีที่ได้เห็นการเปรียบเทียบการใช้ self.image (หรือ [ภาพตัวเอง]) เช่นกัน เมื่อใดควรใช้ self.image ดีกว่าและควรใช้ _image เมื่อใด
Boeckm

2
@Boeckm: โดยทั่วไปคุณควรใช้self.imageซึ่งเข้าถึงคุณสมบัติ ครั้งเดียวที่คุณควรเข้าถึงตัวแปรอินสแตนซ์_imageโดยตรงอยู่ภายในinitวิธีการและdeallocวิธีการเมื่อเรียกวิธีอื่นใดอาจมีความเสี่ยง (เนื่องจากวัตถุนั้นถูกกำหนดค่าเริ่มต้นครึ่ง
Peter Hosey

1

ดูเหมือนว่าจะเป็นรายการ "ต้นแบบ" สำหรับคำถามเกี่ยวกับ self.variableName เทียบกับ _variablename สิ่งที่ทำให้ฉันเป็นห่วงคือใน. h ฉันมี:

...
@interface myClass : parentClass {
className *variableName;    // Note lack of _
}

@property (strong, nonatomic) className  *variableName;
...

สิ่งนี้นำไปสู่ ​​self.variableName และ _variableName เป็นสองตัวแปรที่แตกต่างใน. m สิ่งที่ฉันต้องการคือ:

...
@interface myClass : parentClass {
className *_variableName;    // Note presence of _
}

@property (strong, nonatomic) className  *variableName;
...

จากนั้นในคลาส '.m, self.variableName และ _variableName จะเทียบเท่ากัน

สิ่งที่ฉันยังไม่ชัดเจนคือสาเหตุที่ทำให้หลายตัวอย่างยังคงใช้งานได้แม้สิ่งนี้จะไม่สำเร็จ

รังสี


0

แทนการขีดล่างคุณสามารถใช้ชื่อ self.variable หรือคุณสามารถสังเคราะห์ตัวแปรเพื่อใช้ตัวแปรหรือเต้าเสียบโดยไม่ต้องขีดเส้นใต้


2
หากคุณต้องการตัวแปรในคลาสเดียวกันเพียงแค่ประกาศในไฟล์. m แล้วมันจะช่วยให้คุณสามารถโทรออกได้โดยไม่ต้องใช้ตัวเองหรือขีดเส้นใต้
Ansal Antony

0

สิ่งที่ขาดหายไปจากคำตอบอื่น ๆ คือการใช้_variableป้องกันคุณจากการพิมพ์variableและเข้าถึง ivar โดยไม่ได้ตั้งใจมากกว่าคุณสมบัติ (ตั้งใจไว้ล่วงหน้า)

คอมไพเลอร์จะบังคับให้คุณใช้หรือself.variable _variableการใช้เครื่องหมายขีดล่างทำให้ไม่สามารถพิมพ์variableได้ซึ่งจะช่วยลดข้อผิดพลาดของโปรแกรมเมอร์

- (void)fooMethod {

    // ERROR - "Use of undeclared identifier 'foo', did you mean '_foo'?"
    foo = @1;

    // So instead you must specifically choose to use the property or the ivar:

    // Property
    self.foo = @1;

    // Ivar
    _foo = @1;

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