วิธีกำจัดคำเตือน 'ตัวเลือกที่ไม่ได้ประกาศ'


162

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

if ([self respondsToSelector:@selector(setError:)])
{
    [self performSelector:@selector(setError:) withObject:[NSError errorWithDomain:@"SomeDomain" code:1 userInfo:nil]];
}

อย่างไรก็ตามคอมไพเลอร์ไม่เห็นวิธีการใด ๆ รอบ ๆ ด้วย setError: ลายเซ็นดังนั้นมันทำให้ฉันเตือนสำหรับแต่ละบรรทัดที่มี@selector(setError:)ตัวอย่าง:

Undeclared selector 'setError:'

ฉันไม่ต้องการประกาศโปรโตคอลเพื่อกำจัดคำเตือนนี้เพราะฉันไม่ต้องการให้ทุกคลาสที่สามารถใช้สิ่งนี้เพื่อใช้สิ่งพิเศษ เพียงแค่การประชุมฉันต้องการให้พวกเขามีsetError:วิธีการหรือทรัพย์สิน

เป็นไปได้หรือไม่ อย่างไร?

ไชโย
สอี



ตัวเลือกที่คัดค้านจะทำให้เกิดคำเตือน ไม่ปลอดภัยในการเข้าถึงตัวเลือกอีกต่อไปเนื่องจากตัวเลือกอาจถูกลบออกในบางครั้ง
DawnSong

คำตอบ:


254

ตัวเลือกอื่นจะเป็นการปิดการใช้งานคำเตือนด้วย:

#pragma GCC diagnostic ignored "-Wundeclared-selector"

คุณสามารถวางบรรทัดนี้ในไฟล์. m ซึ่งมีคำเตือนเกิดขึ้น

ปรับปรุง:

มันทำงานได้กับ LLVM เช่นนี้:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"

... your code here ...

#pragma clang diagnostic pop

#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" // Do your thing #pragma clang diagnostic pop
dizy

ใช่มันเป็นไปตามที่ @dizy ระบุ (ขออภัยสำหรับคำตอบที่ล่าช้า แต่ฉันไม่ได้รับการแจ้งเตือน)
Klaas

ฉันต้องการความช่วยเหลือ#pragma clang diagnostic ignored "-Wselector"
สูงสุด

1
@mdorseif ส่วนใหญ่เวลาที่เตือนว่าคุณ 'ต้อง' ไม่รวมอยู่ในบันทึกการรวบรวม คุณสามารถปิดเสียงคำเตือนด้วยแนวคิดนี้ ดีใจที่คุณเพิ่มของคุณเกี่ยวกับตัวเลือก
Klaas

@epologee คุณสามารถทำเช่นเดียวกันผ่านการตั้งค่าการสร้าง "ตัวเลือกไม่ได้ประกาศ"

194

มีลักษณะที่NSSelectorFromString

 SEL selector = NSSelectorFromString(@"setError:");
 if ([self respondsToSelector:selector])

มันจะช่วยให้คุณสร้างตัวเลือกที่รันไทม์แทนที่จะรวบรวมเวลาผ่าน@selectorคำหลักและคอมไพเลอร์จะไม่มีโอกาสบ่น


สวัสดี @ sergio งานของคุณและ @ jacobrelkin ทำงานอย่างไร ส่งมาพร้อมกันสวยมาก คุณจะช่วยฉันเลือกคำตอบที่ดีกว่านี้ได้ไหมถ้ามี
เขียนบทละครที่

2
ฉันชอบคำตอบนี้มากขึ้นเพราะมันดูเหมือน "Cocoa" -y (?) สิ่งที่sel_registerName()ดูคลุมเครือและไม่ควรโทรหาคุณโดยตรงเว้นแต่คุณจะรู้ว่าคุณกำลังทำอะไรอยู่อย่างเช่น obj_msg_send ();)
Nicolas Miari

15
ถ้าไม่แน่ใจว่ามันเป็น Xcode 5 แต่ฉันได้รับการแจ้งเตือนที่แตกต่างกันกับการดำเนินการนี้: "PerformSelector อาจก่อให้เกิดการรั่วไหลเพราะเลือกเป็นที่รู้จัก"
Hampden123

1
@ Hampden123: นั่นเป็นปัญหาที่แตกต่าง ดูที่นี่: stackoverflow.com/questions/7017281/…
sergio

52

ฉันคิดว่าเป็นเพราะเหตุผลบางอย่างที่แปลกประหลาดตัวเลือกไม่ได้ลงทะเบียนกับรันไทม์

ลองลงทะเบียนตัวเลือกผ่านsel_registerName():

SEL setErrorSelector = sel_registerName("setError:");

if([self respondsToSelector:setErrorSelector]) {
   [self performSelector:setErrorSelector withObject:[NSError errorWithDomain:@"SomeDomain" code:1 userInfo:nil]];
}

สวัสดี @jacobrelkin ทั้งคำตอบของคุณและ @ sergio ทำงาน ส่งมาพร้อมกันสวยมาก คุณจะช่วยฉันเลือกคำตอบที่ดีกว่านี้ได้ไหมถ้ามี
เขียนบทละครที่

2
@epologee NSSelectorFromStringโทรศัพท์sel_registerName()ภายใต้ประทุนอยู่แล้ว เลือกสิ่งที่เหมาะกับคุณดีกว่า
Jacob Relkin

1
@epologee ฉันคิดว่าการโทรsel_registerName()โดยตรงมีความชัดเจนมากขึ้นเกี่ยวกับสาเหตุที่คุณทำ NSSelectorFromStringไม่ได้บอกคุณว่ามันจะพยายามลงทะเบียนตัวเลือก
Jacob Relkin

8
ถ้าไม่แน่ใจว่ามันเป็น Xcode 5 แต่ฉันได้รับการแจ้งเตือนที่แตกต่างกันกับการดำเนินการนี้: "PerformSelector อาจก่อให้เกิดการรั่วไหลเพราะเลือกเป็นที่รู้จัก"
Hampden123

@ Max_Power89 ไม่ดูความคิดเห็นอื่นของฉันด้านล่าง ฉันไม่ต้องการใช้เวลามากเกินไปกับสิ่งนี้ดังนั้นฉันจึงรวมไฟล์ส่วนหัวไว้ด้วย
Hampden123

7

ฉันได้รับข้อความนั้นให้หายไปโดย # include'ing ไฟล์ด้วยวิธีการ ไม่มีสิ่งอื่นใดถูกใช้จากไฟล์นั้น


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

2
คำตอบที่โหวตด้านบนไม่ถูกต้อง เจตนาของการเตือน "ตัวเลือกที่ไม่ได้ประกาศ" คือการตรวจจับข้อผิดพลาดในเวลารวบรวมหากคุณเปลี่ยนชื่อของตัวเลือกที่คุณใช้ ดังนั้นจึงเป็นสิ่งที่ถูกต้องที่สุดในการ #import ไฟล์ที่ประกาศวิธีการที่คุณใช้
Brane

7

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

ในส่วน 'คำเตือน Apple LLVM - Objective-C' เปลี่ยน:

Undeclared Selector - NO

6

หากคลาสของคุณใช้เมธอด setError: (แม้จะประกาศตัวตั้งค่าของคุณสมบัติข้อผิดพลาดในที่สุด) แบบไดนามิกคุณอาจต้องการประกาศในไฟล์อินเตอร์เฟส (.h) หรือถ้าคุณไม่ต้องการแสดงวิธีนั้น ลองด้วยเคล็ดลับเคล็ดลับ PrivateMethods:

@interface Yourclass (PrivateMethods)

- (void) yourMethod1;
- (void) yourMethod2;

@end

ก่อน @implementation ของคุณสิ่งนี้ควรซ่อนคำเตือน;)


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

และพวกเราบางคนกำลังทำสิ่งที่แปลกใหม่มากขึ้น - ตัวเลือกถูกนำไปใช้ในวัตถุ F # ในกรณีของฉัน
James Moore

1
นี้ไม่ได้กำจัดคำเตือนใน XCode 7.1.1 / iOS 9.1 ฉันเห็นPerformSelector may cause a leak because its selector is unknown
loretoparisi

3

มาโครที่สะดวกสบายในการใส่ใน.pchหรือของคุณCommon.hหรือทุกที่ที่คุณต้องการ:

#define SUPPRESS_UNDECLARED_SELECTOR_LEAK_WARNING(code)                        \
_Pragma("clang diagnostic push")                                        \
_Pragma("clang diagnostic ignored \"-Wundeclared-selector"\"")     \
code;                                                                   \
_Pragma("clang diagnostic pop")                                         \

มันเป็นการแก้ไขคำถามนี้สำหรับปัญหาที่คล้ายกัน ...


3

คุณสามารถปิดได้ใน Xcode เหมือนในสกรีนช็อต:

ป้อนคำอธิบายรูปภาพที่นี่


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

2

นอกจากนี้คุณยังสามารถโยนวัตถุที่เป็นปัญหาไปยัง id ก่อนเพื่อหลีกเลี่ยงการเตือน:

if ([object respondsToSelector:@selector(myMethod)]) {
    [(id)object myMethod];
}

1
นี่ไม่ได้เป็นการกำจัดคำเตือนเดียวกันในเนื้อหา if expression ไปจนถึง XC7.1 จนถึงทุกวันนี้
Martin-Gilles Lavoie

2

อีกวิธีหนึ่งในการหลีกเลี่ยงคำเตือนนี้เพื่อให้แน่ใจว่าวิธีการเลือกของคุณเป็นดังนี้:

-(void) myMethod :(id) sender{
}

อย่าลืม "(id) ผู้ส่ง" หากคุณต้องการยอมรับผู้ส่งใด ๆ หรือระบุประเภทของวัตถุผู้ส่งหากคุณต้องการ


0

ในขณะที่คำตอบที่ถูกต้องน่าจะอยู่ที่การแจ้ง Xcode ผ่านการนำเข้าหรือการลงทะเบียนตัวเลือกที่มีตัวเลือกดังกล่าวอยู่ในกรณีของฉันฉันไม่มีเซมิโคลอน ตรวจสอบให้แน่ใจก่อนที่คุณ "แก้ไข" ข้อผิดพลาดที่อาจผิดพลาดถูกต้องและรหัสของคุณไม่ได้ ฉันพบข้อผิดพลาดในตัวอย่าง MVCNetworking ของ Apple เช่น


ไม่คำตอบที่ถูกต้องไม่ได้แจ้งให้ Xcode ทราบผ่านการนำเข้าเนื่องจากมีการนำเข้าเหล่านั้นแล้ว คำตอบที่ถูกต้องคือคำตอบข้างต้นที่ถูกทำเครื่องหมายว่าเป็น ... คำตอบที่ถูกต้องแม้ว่าคำตอบของ @ sergio ก็จะแก้ปัญหาด้วยเช่นกัน การใช้ตัวเลือกที่ไม่ถูกต้องไม่ใช่หัวข้อของคำถามนี้ดังนั้นการเปลี่ยนตัวเลือกจึงไม่ใช่คำตอบ ฉันจะช่วยคุณลดคะแนนโหวต
เขียนบทละคร

1
ขอบคุณที่เตือนฉันว่าฉันน่าจะใช้ความคิดเห็น ทั้งหมดที่ฉันสามารถพูดได้คือการนำเข้าที่หายไปยังทำให้เกิดการเตือน Xcode นี้หากไม่ใช่กรณีเฉพาะนี้ ฉันจะแนะนำ NSSelectorFromString หรือตัวเลือก "การลงทะเบียน" อื่น ๆ เมื่อสร้างตัวเลือกขณะใช้งานจริงหรือตอบสนองต่อการเรียกใช้เมธอดในรูปแบบไดนามิก (เช่น methodSignatureForSelector) การลงทะเบียนหมายความว่าคุณกำลัง "แก้ไขข้อผิดพลาด" และดังนั้นจึงไม่ถูกต้องในบางสถานการณ์เนื่องจากวิธีการที่ถูกต้องมากขึ้นคือการแก้ไขคำเตือน (หากการวิเคราะห์เสียงดังกราวถูกต้องนั่นคือ)
Louis St-Amour

ในความเป็นจริงตอนนี้ฉันเห็นว่าคำถามเดิมกล่าวอย่างชัดเจนว่า "โดยไม่ต้องใช้โปรโตคอลที่ใช้งาน" - และไม่พูดถึงการนำเข้าเลย ดังนั้นฉันจึงขอกล่าวว่าการนำเข้าหมวดหมู่นั้นอาจเป็นตัวเลือกที่ดีที่สุดสำหรับผู้ใช้รายนี้ อะไรก็ได้ที่นี่สามารถกำหนดตัวเลือกสองครั้งโดยพูดในทางเทคนิค ใช่? - แก้ไข: อ่าฉันได้ไปไกลเกินไปแล้ว ขอบคุณสำหรับคำตอบของคุณฉันจะหยุดทันที :)
Louis St-Amour

-1

ฉันสามารถรับคำเตือนให้หายไปโดยการเพิ่มวิธีผ่อนคลาย (การเปิดเผย: ฉันไม่ได้คิดถึงสิ่งนี้ แต่พบว่ามันเกิดจากการหาข้อมูลตามกำหนดเวลาด้วยช่วงเวลาที่แตกต่างกัน)

    [NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow]
                                     target:self
                                   selector:@selector(donothingatall:)
                                   userInfo:nil
                                    repeats:YES];


    [[NSRunLoop currentRunLoop] run];

    HTTPLogVerbose(@"%@: BonjourThread: Aborted", THIS_FILE);

    }
}

+ (void) donothingatall:(NSTimer *)timer
{

}

ในขณะที่ฉันชื่นชมการรู้วิธีซ่อนคำเตือนการแก้ไขจะดีกว่าและไม่ใช้เทคนิคของ Sergio และ Relkin สำหรับฉันด้วยเหตุผลที่ไม่รู้จัก


1
หากคนอื่นอ่านวิธีนี้ซึ่งจะได้ผลเขา / เธอจะค่อนข้างสับสนรวมถึงตัวคุณในอนาคต หากคุณแน่ใจว่าคุณรู้ว่ากำลังทำอะไรอยู่โดยการเรียกตัวเลือกที่ไม่มีอยู่แล้วทำให้เกิดการเตือนข้ามขั้นตอนวิธีการทำให้เข้าใจผิดและทำให้แน่ใจว่ารหัสของคุณแสดงเจตนา
เขียนบทละคร

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