การใช้ -performSelector: เทียบกับการเรียกใช้เมธอด


คำตอบ:


191

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

ดังนั้นแม้ว่าสิ่งเหล่านี้จะเทียบเท่า:

[anObject aMethod]; 
[anObject performSelector:@selector(aMethod)]; 

รูปแบบที่สองให้คุณทำสิ่งนี้:

SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];

ก่อนที่คุณจะส่งข้อความ


3
มันคุ้มค่าที่จะชี้ให้เห็นว่าคุณจะกำหนดผลลัพธ์ของ findTheApp ProperSelectorForTheCurrentSituation () ให้กับ aSelector จากนั้นเรียกใช้ [anObject performSelector: aSelector] @selector สร้าง SEL
Daniel Yankowsky

4
การใช้performSelector:เป็นสิ่งที่คุณอาจทำได้ก็ต่อเมื่อคุณใช้เป้าหมายการกระทำในชั้นเรียนของคุณ พี่น้องperformSelectorInBackground:withObject:และperformSelectorOnMainThread:withObject:waitUntilDone:มักจะมีประโยชน์มากขึ้น สำหรับการวางไข่เธรดพื้นหลังและสำหรับการเรียกผลลัพธ์กลับไปยังเธรดหลักจากเธรดพื้นหลังดังกล่าว
PeyloW

2
performSelectorยังมีประโยชน์ในการระงับการคอมไพล์คำเตือน หากคุณทราบว่ามีวิธีการนี้อยู่ (เช่นหลังการใช้งานrespondsToSelector) จะหยุด Xcode ไม่ให้พูดว่า "อาจไม่ตอบสนองต่อyour_selector" อย่าใช้มันแทนที่จะหาสาเหตุที่แท้จริงของคำเตือน ;)
Marc

ฉันอ่านหัวข้ออื่น ๆ ใน StackOverflow ว่าการใช้ performSelector เป็นภาพสะท้อนของการออกแบบที่น่าสยดสยองและต้องยกนิ้วให้มากมาย ฉันหวังว่าฉันจะได้พบมันอีกครั้ง ฉันค้นหา Google จำกัด ผลลัพธ์ไว้ที่ stackoverflow และได้ผลลัพธ์ 18,000 รายการ eww
Logicsaurus Rex

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

16

สำหรับตัวอย่างพื้นฐานในคำถามนี้

[object doSomething];
[object performSelector:@selector(doSomething)]; 

ไม่มีความแตกต่างในสิ่งที่จะเกิดขึ้น doSomething จะดำเนินการพร้อมกันโดยวัตถุ เฉพาะ "doSomething" เท่านั้นที่เป็นวิธีการที่ง่ายมากซึ่งไม่ส่งคืนอะไรเลยและไม่ต้องการพารามิเตอร์ใด ๆ

มันมีอะไรที่ซับซ้อนกว่านี้เล็กน้อยเช่น:

(void)doSomethingWithMyAge:(NSUInteger)age;

สิ่งต่างๆจะซับซ้อนขึ้นเพราะ [object doSomethingWithMyAge: 42];

ไม่สามารถเรียกด้วยตัวแปรใด ๆ ของ "performSelector" ได้อีกต่อไปเนื่องจากตัวแปรทั้งหมดที่มีพารามิเตอร์ยอมรับเฉพาะพารามิเตอร์ออบเจ็กต์

ตัวเลือกที่นี่จะเป็น "doSomethingWithMyAge:" แต่จะพยายามใด ๆ

[object performSelector:@selector(doSomethingWithMyAge:) withObject:42];  

จะไม่คอมไพล์ การส่ง NSNumber: @ (42) แทนที่จะเป็น 42 จะไม่ช่วยเช่นกันเนื่องจากวิธีนี้คาดว่าจะมีประเภท C พื้นฐานไม่ใช่วัตถุ

นอกจากนี้ยังมีตัวแปร performSelector มากถึง 2 พารามิเตอร์ไม่มีอีกต่อไป ในขณะที่วิธีการหลายครั้งมีพารามิเตอร์อื่น ๆ อีกมากมาย

ฉันได้พบว่าแม้ว่าจะมีตัวแปรแบบซิงโครนัสของ performSelector:

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

ส่งคืนวัตถุเสมอฉันสามารถส่งคืน BOOL หรือ NSUInteger แบบธรรมดาได้ด้วยและมันก็ใช้งานได้

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

 SEL method = NSSelectorFromString([NSString stringWithFormat:@"doSomethingWithMy%@:", @"Age");
[object performSelector:method];

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

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
- (void)performSelector:(SEL)aSelector target:(id)target argument:(id)arg order:(NSUInteger)order modes:(NSArray *)modes;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;

(ใช่ฉันรวบรวมพวกเขาจากหมวดหมู่พื้นฐานหลายประเภทเช่น NSThread, NSRunLoop และ NSObject)

ตัวแปรแต่ละตัวมีพฤติกรรมพิเศษของตัวเอง แต่ทุกอย่างก็มีบางอย่างที่เหมือนกัน (อย่างน้อยเมื่อตั้งค่า waitUntilDone เป็น NO) การเรียกใช้ "performSelector" จะกลับมาทันทีและข้อความถึงวัตถุจะถูกวางไว้บนรันลูปปัจจุบันหลังจากเวลาผ่านไประยะหนึ่ง

เนื่องจากการดำเนินการล่าช้า - โดยปกติแล้วจะไม่มีค่าส่งคืนในรูปแบบวิธีการของตัวเลือกดังนั้นค่าส่งคืน - (โมฆะ) ในตัวแปรอะซิงโครนัสเหล่านี้ทั้งหมด

ฉันหวังว่าฉันจะพูดถึงเรื่องนี้ ...


12

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

ข้อแตกต่างที่สำคัญอย่างหนึ่งคือ-performSelector:และเพื่อน ๆ (รวมถึงตัวแปรแบบมัลติเธรดและแบบล่าช้า ) ค่อนข้าง จำกัด เนื่องจากได้รับการออกแบบมาเพื่อใช้กับวิธีการที่มีพารามิเตอร์ 0-2 ตัวอย่างเช่นการเรียก-outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:ด้วยพารามิเตอร์ 6 ตัวและการส่งคืนNSStringนั้นค่อนข้างเทอะทะและไม่รองรับวิธีการที่ให้มา


5
ในการทำเช่นนั้นคุณต้องใช้NSInvocationวัตถุ
Dave DeLong

6
ความแตกต่างอีกประการหนึ่ง: performSelector:และเพื่อน ๆ ทุกคนต่างก็ใช้อาร์กิวเมนต์ของวัตถุซึ่งหมายความว่าคุณไม่สามารถใช้เพื่อเรียก (เช่น) setAlphaValue:ได้เนื่องจากอาร์กิวเมนต์ของมันเป็นแบบลอย
Chuck

4

ตัวเลือกก็เหมือนกับตัวชี้ฟังก์ชันในภาษาอื่น ๆ คุณใช้เมื่อคุณไม่ทราบเวลารวบรวมวิธีที่คุณต้องการเรียกใช้ในรันไทม์ นอกจากนี้เช่นเดียวกับตัวชี้ฟังก์ชันพวกเขาจะห่อหุ้มส่วนคำกริยาของการเรียกใช้เท่านั้น หากเมธอดมีพารามิเตอร์คุณจะต้องส่งผ่านด้วย

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


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

1
ตัวเลือกไม่เหมือนกับตัวชี้ฟังก์ชัน แต่ฉันยังคิดว่ามันคล้ายกัน เป็นตัวแทนของคำกริยา ตัวชี้ฟังก์ชัน C ยังแทนคำกริยา ไม่มีประโยชน์หากไม่มีบริบทเพิ่มเติม ตัวเลือกต้องการวัตถุและพารามิเตอร์ พอยน์เตอร์ฟังก์ชันต้องการพารามิเตอร์ (ซึ่งอาจรวมถึงอ็อบเจ็กต์ที่จะใช้งาน) ประเด็นของฉันคือการเน้นว่าพวกมันแตกต่างจากวัตถุ NSInvocation อย่างไรซึ่งมีบริบทที่จำเป็นทั้งหมด บางทีการเปรียบเทียบของฉันอาจทำให้สับสนซึ่งในกรณีนี้ฉันต้องขออภัย
Daniel Yankowsky

1
ตัวเลือกไม่ใช่ตัวชี้ฟังก์ชัน ไม่ได้ใกล้เคียง. เป็นสตริง C ธรรมดาในความเป็นจริงที่มี "ชื่อ" ของวิธีการ (ซึ่งตรงข้ามกับ 'ฟังก์ชัน') พวกเขาไม่ใช่ลายเซ็นของเมธอดด้วยซ้ำเพราะไม่ได้ฝังประเภทของพารามิเตอร์ อ็อบเจ็กต์สามารถมีมากกว่าหนึ่งวิธีสำหรับตัวเลือกเดียวกัน (ประเภทพารามิเตอร์ที่แตกต่างกันหรือประเภทการส่งคืนที่แตกต่างกัน)
Motti Shneor

-7

มีความแตกต่างที่ลึกซึ้งระหว่างทั้งสอง

    [object doSomething]; // is executed right away

    [object performSelector:@selector(doSomething)]; // gets executed at the next runloop

นี่คือข้อความที่ตัดตอนมาจากเอกสารของ Apple

"performSelector: withObject: afterDelay: ดำเนินการตัวเลือกที่ระบุบนเธรดปัจจุบันในระหว่างรอบการทำงานรอบถัดไปและหลังจากช่วงหน่วงเวลาที่เป็นทางเลือกเนื่องจากจะรอจนกว่าจะถึงรอบการทำงานรอบถัดไปเพื่อดำเนินการตัวเลือกวิธีการเหล่านี้จะให้การหน่วงเวลามินิอัตโนมัติจาก รหัสที่ดำเนินการอยู่ในปัจจุบันตัวเลือกที่อยู่ในคิวหลายตัวจะดำเนินการทีละรายการตามลำดับที่ถูกจัดคิว "


1
คำตอบของคุณไม่ถูกต้องตามความเป็นจริง เอกสารที่คุณอ้างอิงเป็นเรื่องเกี่ยวกับperformSelector:withObject:afterDelay:แต่คำถามและตัวอย่างข้อมูลของคุณกำลังใช้อยู่performSelector:ซึ่งเป็นวิธีการที่แตกต่างกันอย่างสิ้นเชิง จากเอกสารสำหรับมัน: <quote> performSelector:วิธีการนี้เทียบเท่ากับการส่งaSelectorข้อความไปยังผู้รับโดยตรง </quote>
jscs

3
ขอบคุณ Josh สำหรับคำชี้แจง คุณถูก; ฉันคิดว่าperformSelector/performSelector:withObject/performSelector:withObject:afterDelayทุกคนมีพฤติกรรมเหมือนกันซึ่งเป็นความผิดพลาด
avi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.