เหตุใดส่วนสุดท้ายของชื่อเมธอด Objective-C จึงต้องใช้อาร์กิวเมนต์ (เมื่อมีมากกว่าหนึ่งส่วน)


90

ใน Objective-C คุณไม่สามารถประกาศชื่อเมธอดโดยที่ส่วนประกอบสุดท้ายไม่รับอาร์กิวเมนต์ ตัวอย่างเช่นต่อไปนี้ผิดกฎหมาย

-(void)take:(id)theMoney andRun;
-(void)take:(id)yourMedicine andDontComplain;

เหตุใด Objective-C จึงออกแบบมาในลักษณะนี้ มันเป็นเพียงสิ่งประดิษฐ์ของ Smalltalk ที่ไม่มีใครเห็นว่าจำเป็นต้องกำจัด?

ข้อ จำกัด นี้มีความหมายใน Smalltalk เนื่องจาก Smalltalk ไม่มีตัวคั่นรอบการเรียกใช้ข้อความดังนั้นองค์ประกอบสุดท้ายจะถูกตีความว่าเป็นข้อความที่เป็นเอกภาพของอาร์กิวเมนต์สุดท้าย ยกตัวอย่างเช่นจะแยกวิเคราะห์เป็นBillyAndBobby take:'$100' andRun BillyAndBobby take:('$100' andRun)สิ่งนี้ไม่สำคัญใน Objective-C ที่ต้องใช้วงเล็บเหลี่ยม

การสนับสนุนส่วนประกอบตัวเลือกที่ไม่มีพารามิเตอร์จะไม่ช่วยให้เราได้รับประโยชน์มากนักในทุกวิธีการวัดภาษาตามปกติเนื่องจากชื่อวิธีการที่โปรแกรมเมอร์เลือก (เช่นrunWith:แทนที่จะเป็นtake:andRun) ไม่มีผลต่อความหมายเชิงฟังก์ชันของโปรแกรมหรือการแสดงออกของภาษา อันที่จริงโปรแกรมที่มีส่วนประกอบที่ไม่มีพารามิเตอร์นั้นเทียบเท่ากับอัลฟา ดังนั้นฉันจึงไม่สนใจคำตอบที่ระบุว่าคุณลักษณะดังกล่าวไม่จำเป็น (เว้นแต่เป็นเหตุผลที่ระบุไว้ของนักออกแบบ Objective-C ไม่มีใครรู้จัก Brad Cox หรือ Tom Love พวกเขามาที่นี่หรือไม่) หรือที่พูดว่า วิธีการเขียนชื่อเมธอดเพื่อไม่ให้คุณลักษณะนี้จำเป็น ประโยชน์หลักคือความสามารถในการอ่านและความสามารถในการเขียน (ซึ่งก็เหมือนกับความสามารถในการอ่านเพียง ... คุณรู้) เนื่องจากจะหมายความว่าคุณสามารถเขียนชื่อวิธีการที่คล้ายกับประโยคภาษาธรรมชาติได้ สิ่งที่ชอบ-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)theApplication(ซึ่ง Matt Gallagher ชี้ให้เห็นเกี่ยวกับ "Cocoa With Love"-(BOOL)application:(NSApplication*)theApplication shouldTerminateAfterLastWindowClosedดังนั้นจึงวางพารามิเตอร์ถัดจากคำนามที่เหมาะสม

รันไทม์ Objective-C ของ Apple (ตัวอย่าง) สามารถจัดการตัวเลือกประเภทนี้ได้อย่างสมบูรณ์แบบแล้วทำไมไม่คอมไพเลอร์ล่ะ? ทำไมไม่สนับสนุนพวกเขาในชื่อวิธีการด้วย?

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface Potrzebie : NSObject
-(void)take:(id)thing;
@end

@implementation Potrzebie
+(void)initialize {
    SEL take_andRun = NSSelectorFromString(@"take:andRun");
    IMP take_ = class_getMethodImplementation(self, @selector(take:));
    if (take_) {
        if (NO == class_addMethod(self, take_andRun, take_, "@@:@")) {
            NSLog(@"Couldn't add selector '%@' to class %s.", 
                  NSStringFromSelector(take_andRun), 
                  class_getName(self));
        }
    } else {
        NSLog(@"Couldn't find method 'take:'.");
    }
}

-(void)take:(id)thing {
    NSLog(@"-take: (actually %@) %@",NSStringFromSelector(_cmd), thing);
}
@end

int main() {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    Potrzebie *axolotl=[[Potrzebie alloc] init];
    [axolotl take:@"paradichloroaminobenzaldehyde"];
    [axolotl performSelector:NSSelectorFromString(@"take:andRun") 
                  withObject:@"$100"];
    [axolotl release];

    [pool release];
    return 0;
}

1
อธิบายเหตุผลไม่ได้ว่าทำไมถึงทำไม่ได้ แต่ทำไมถึงเป็นไปได้ Apple จะตั้งชื่อเมธอดว่า "takeAndRun:" และ "takeAndDontComplain:";)

หรือtakeAndRunWith:(id)theMoneyและtakeAndDon'tComplainAbout:(id)yourMedicine. ไวยกรณ์อึดอัดใจแน่ ๆ
Matthew Frederick

13
ขอบคุณที่ถามสิ่งนี้ - เป็นคำถามที่น่าสนใจมาก ฉันจะถามไปรอบ ๆ
bbum

5
มันเป็นความอึดอัดทางไวยากรณ์ที่ถูกบังคับเป็นครั้งคราวทำให้ฉันต้องการคุณสมบัตินี้
นอกสถานที่

1
คำถามที่ดี นอกจากนี้คอมไพเลอร์ไม่มีปัญหากับเมธอดที่มีชื่อเช่นนี้: - (void) :(id)theMoney;หรือ- (void) :(id)obj1 :(id)obj2;. ดังนั้นตัวเลือกที่ไม่มีอะไรเลยนอกจากโคลอนก็ใช้ได้ ;-)
Ole Begemann

คำตอบ:


111

นี่คือ Brad Cox คำตอบเดิมของฉันทำให้เข้าใจผิดคำถาม ฉันคิดว่าจริงๆแล้ว Fast เป็นส่วนขยายที่เข้ารหัสเพื่อเรียกใช้การส่งข้อความที่เร็วขึ้นไม่ใช่น้ำตาลที่เป็นประโยค คำตอบที่แท้จริงคือ Smalltalk ไม่รองรับอาจเป็นเพราะตัวแยกวิเคราะห์ไม่สามารถจัดการกับความคลุมเครือ (สันนิษฐาน) ได้ แม้ว่าวงเล็บเหลี่ยมของ OC จะลบความคลุมเครือ แต่ฉันก็ไม่คิดที่จะออกจากโครงสร้างคำหลักของ Smalltalk


ขอบคุณแบรด ฉันตีความคำตอบของคุณเหมือนกัน การออกจาก SmallTalk นั้นไม่ได้รับการพิจารณา
bbum

42

21 ปีของการเขียนโปรแกรม Objective-C และคำถามนี้ไม่เคยข้ามความคิดของฉัน เนื่องจากการออกแบบภาษาคอมไพเลอร์ถูกต้องและฟังก์ชันรันไทม์ไม่ถูกต้อง ()

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

โดยไม่ต้องคิดอะไรมากฉันพนันได้เลยว่ามี bugaboos เชิงไวยากรณ์บางตัวที่ไม่บังคับใช้รูปแบบปัจจุบัน อย่างน้อยก็จะทำให้คอมไพเลอร์ที่ยากที่จะเขียนในที่ไวยากรณ์ใด ๆ ที่มีองค์ประกอบเป็นตัวเลือกบรรณนิทัศน์กับการแสดงออกเป็นเสมอยากที่จะแยก อาจมีขอบเคสที่แบนออกมาป้องกัน แน่นอนว่า Obj-C ++ จะทำให้ท้าทายมากขึ้น แต่ก็ไม่ได้รวมเข้ากับภาษานี้จนกระทั่งหลายปีหลังจากที่ไวยากรณ์พื้นฐานถูกตั้งค่าเป็นหินแล้ว

สำหรับเหตุผลที่ Objective-C ได้รับการออกแบบด้วยวิธีนี้ฉันสงสัยว่าคำตอบก็คือนักออกแบบดั้งเดิมของภาษาไม่ได้พิจารณาอนุญาตให้ไวยากรณ์แทรกซ้อนเกินกว่าอาร์กิวเมนต์สุดท้ายนั้น

นั่นคือการคาดเดาที่ดีที่สุด ฉันจะถามพวกเขาคนหนึ่งและอัปเดตคำตอบของฉันเมื่อฉันหาข้อมูลเพิ่มเติม


ฉันถาม Brad Cox เกี่ยวกับเรื่องนี้และเขาก็ตอบในรายละเอียดมาก (ขอบคุณแบรด !!):

ตอนนั้นฉันจดจ่ออยู่กับการทำสำเนา Smalltalk ใน C ให้ได้มากที่สุดและทำอย่างมีประสิทธิภาพที่สุด รอบการสำรองใด ๆ ทำให้การส่งข้อความธรรมดาเป็นไปอย่างรวดเร็ว ไม่มีตัวเลือกการส่งข้อความแบบพิเศษ ("reallyFast?" [ bbum: ฉันถามโดยใช้ "doSomething: withSomething: reallyFast" เป็นตัวอย่าง ]) เนื่องจากข้อความธรรมดาเร็วที่สุดอยู่แล้ว สิ่งนี้เกี่ยวข้องกับการปรับแต่งเอาต์พุตแอสเซมเบลอร์ของ C proto-messager ด้วยมือซึ่งเป็นฝันร้ายของการพกพาที่บางคนไม่ได้ถูกนำออกไป ฉันจำได้ว่าเครื่องส่งข้อความที่แฮ็กด้วยมือนั้นเร็วมาก เกี่ยวกับค่าใช้จ่ายของการเรียกใช้ฟังก์ชันสองครั้ง หนึ่งเพื่อเข้าสู่ตรรกะของผู้ส่งข้อความและส่วนที่เหลือสำหรับการค้นหาวิธีการเมื่อมี
ต่อมามีการเพิ่มการปรับปรุงการพิมพ์แบบคงที่ในการพิมพ์แบบไดนามิกของ Smalltalk โดย Steve Naroff และคนอื่น ๆ ฉันมีส่วนร่วมอย่าง จำกัด ในเรื่องนั้น

ไปอ่านคำตอบของแบรด!


ไวยากรณ์ bugaboos สนใจฉันมาก
นอกสถานที่

1
สำหรับฉันแล้วดูเหมือนว่าถ้าคุณมีชื่อเมธอดที่ไม่มีเครื่องหมายจุดคู่แสดงว่าคุณมีปัญหาในการแยกวิเคราะห์เพราะคุณไม่แน่ใจว่ามันตั้งใจจะเป็นส่วนหนึ่งของชื่อเมธอดหรือไม่
NSResponder

2
ฉันคิดว่าครั้งแรกที่ฉันออกแบบโปรโตคอลสำหรับผู้รับมอบสิทธิ์ซึ่งใช้เวลาน้อยกว่า 21 ปีหลังจากเริ่มต้นด้วย Objective-C รูปแบบปกติคือการจัดหาวัตถุที่ส่งข้อความตัวแทนเป็นพารามิเตอร์แรก เช่น-myObjectWithDelegate: (id) foo wantsYouToDoSomethingWith: (id) bar. สิ่งนี้นำไปสู่ความไม่สอดคล้องกันในชื่อเมธอดหากคุณมีเมธอดในโปรโตคอลที่ไม่ต้องการพารามิเตอร์อื่น ดู NSTableViewDataSource สำหรับตัวอย่าง วิธีการหนึ่งไม่เป็นไปตามรูปแบบที่ดีงามของวิธีอื่น ๆ ทั้งหมด
JeremyP

21

สำหรับข้อมูลของคุณรันไทม์ไม่ได้สนใจตัวเลือกจริงๆสตริง C ใด ๆ ก็ใช้ได้คุณสามารถสร้างตัวเลือกแบบนั้นได้เช่นกัน: "== + === + ---__-- ¨¨¨¨ ¨ ^ :::::: "โดยไม่มีอาร์กิวเมนต์รันไทม์จะยอมรับคอมไพเลอร์ก็ไม่สามารถแยกวิเคราะห์ได้ ไม่มีการตรวจสอบความมีสติอย่างแน่นอนเมื่อพูดถึงตัวเลือก


7
+1 ฉันคิดว่าคุณบ้าไปแล้วที่แนะนำสิ่งนี้ แต่คุณพูดถูก สำหรับคนที่ไม่เชื่อ: pastie.org/1388361
Dave DeLong

6

ฉันถือว่าพวกเขาไม่ได้รับการสนับสนุนใน Objective-C เนื่องจากไม่สามารถใช้งานได้ใน Smalltalk เช่นกัน แต่นั่นมีเหตุผลที่แตกต่างจากที่คุณคิดนั่นคือไม่จำเป็น สิ่งที่จำเป็นคือการสนับสนุนวิธีการที่มีอาร์กิวเมนต์ 0, 1, 2, 3, ... สำหรับอาร์กิวเมนต์ทุกจำนวนมีไวยากรณ์ที่ใช้งานได้อยู่แล้วเพื่อเรียกใช้ การเพิ่มไวยากรณ์อื่น ๆ จะทำให้เกิดความสับสนโดยไม่จำเป็น

หากคุณต้องการตัวเลือกที่ไม่มีพารามิเตอร์หลายคำทำไมต้องหยุดด้วยคำพิเศษเพียงคำเดียว หนึ่งอาจถามว่า

 [axolotl perform selector: Y with object: Y]

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


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

นอกจากนี้การอนุญาตให้ใช้ส่วนประกอบที่ไม่มีพารามิเตอร์ตามอำเภอใจทำให้เกิดความเป็นไปได้ที่จะเกิดการชนกันของคำหลักและทำให้การแก้ไขชื่อซับซ้อนขึ้นโดยเฉพาะอย่างยิ่งเมื่อผสม C ++ และ ObjC ( andและorจะทำให้สะดุดโดยเฉพาะ) สิ่งนี้จะเกิดขึ้นน้อยกว่ามากหากอนุญาตให้เฉพาะองค์ประกอบสุดท้ายของชื่อวิธีการที่ไม่มีพารามิเตอร์เนื่องจากมักจะประกอบด้วยคำหลายคำ
นอกสถานที่
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.