ฉันยังใหม่กับ Objective-C และฉันสงสัยว่าอะไรคือความแตกต่างระหว่างสองคำสั่งต่อไปนี้?
[object performSelector:@selector(doSomething)];
[object doSomething];
ฉันยังใหม่กับ Objective-C และฉันสงสัยว่าอะไรคือความแตกต่างระหว่างสองคำสั่งต่อไปนี้?
[object performSelector:@selector(doSomething)];
[object doSomething];
คำตอบ:
โดยทั่วไปแล้ว performSelector ช่วยให้คุณกำหนดตัวเลือกแบบไดนามิกที่จะเรียกใช้ตัวเลือกบนวัตถุที่กำหนด กล่าวอีกนัยหนึ่งไม่จำเป็นต้องกำหนดตัวเลือกก่อนรันไทม์
ดังนั้นแม้ว่าสิ่งเหล่านี้จะเทียบเท่า:
[anObject aMethod];
[anObject performSelector:@selector(aMethod)];
รูปแบบที่สองให้คุณทำสิ่งนี้:
SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];
ก่อนที่คุณจะส่งข้อความ
performSelector:
เป็นสิ่งที่คุณอาจทำได้ก็ต่อเมื่อคุณใช้เป้าหมายการกระทำในชั้นเรียนของคุณ พี่น้องperformSelectorInBackground:withObject:
และperformSelectorOnMainThread:withObject:waitUntilDone:
มักจะมีประโยชน์มากขึ้น สำหรับการวางไข่เธรดพื้นหลังและสำหรับการเรียกผลลัพธ์กลับไปยังเธรดหลักจากเธรดพื้นหลังดังกล่าว
performSelector
ยังมีประโยชน์ในการระงับการคอมไพล์คำเตือน หากคุณทราบว่ามีวิธีการนี้อยู่ (เช่นหลังการใช้งานrespondsToSelector
) จะหยุด Xcode ไม่ให้พูดว่า "อาจไม่ตอบสนองต่อyour_selector
" อย่าใช้มันแทนที่จะหาสาเหตุที่แท้จริงของคำเตือน ;)
สำหรับตัวอย่างพื้นฐานในคำถามนี้
[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" จะกลับมาทันทีและข้อความถึงวัตถุจะถูกวางไว้บนรันลูปปัจจุบันหลังจากเวลาผ่านไประยะหนึ่ง
เนื่องจากการดำเนินการล่าช้า - โดยปกติแล้วจะไม่มีค่าส่งคืนในรูปแบบวิธีการของตัวเลือกดังนั้นค่าส่งคืน - (โมฆะ) ในตัวแปรอะซิงโครนัสเหล่านี้ทั้งหมด
ฉันหวังว่าฉันจะพูดถึงเรื่องนี้ ...
@ennuikiller เป็นจุด ๆ โดยทั่วไปตัวเลือกที่สร้างขึ้นแบบไดนามิกจะมีประโยชน์เมื่อคุณไม่ทราบ (และโดยปกติแล้วจะไม่สามารถ) ทราบชื่อของวิธีการที่คุณจะเรียกเมื่อคุณรวบรวมโค้ด
ข้อแตกต่างที่สำคัญอย่างหนึ่งคือ-performSelector:
และเพื่อน ๆ (รวมถึงตัวแปรแบบมัลติเธรดและแบบล่าช้า ) ค่อนข้าง จำกัด เนื่องจากได้รับการออกแบบมาเพื่อใช้กับวิธีการที่มีพารามิเตอร์ 0-2 ตัวอย่างเช่นการเรียก-outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:
ด้วยพารามิเตอร์ 6 ตัวและการส่งคืนNSString
นั้นค่อนข้างเทอะทะและไม่รองรับวิธีการที่ให้มา
NSInvocation
วัตถุ
performSelector:
และเพื่อน ๆ ทุกคนต่างก็ใช้อาร์กิวเมนต์ของวัตถุซึ่งหมายความว่าคุณไม่สามารถใช้เพื่อเรียก (เช่น) setAlphaValue:
ได้เนื่องจากอาร์กิวเมนต์ของมันเป็นแบบลอย
ตัวเลือกก็เหมือนกับตัวชี้ฟังก์ชันในภาษาอื่น ๆ คุณใช้เมื่อคุณไม่ทราบเวลารวบรวมวิธีที่คุณต้องการเรียกใช้ในรันไทม์ นอกจากนี้เช่นเดียวกับตัวชี้ฟังก์ชันพวกเขาจะห่อหุ้มส่วนคำกริยาของการเรียกใช้เท่านั้น หากเมธอดมีพารามิเตอร์คุณจะต้องส่งผ่านด้วย
มีNSInvocation
จุดประสงค์ที่คล้ายกันยกเว้นว่าจะรวมข้อมูลเพิ่มเติมเข้าด้วยกัน ไม่เพียง แต่รวมส่วนกริยาเท่านั้น แต่ยังรวมถึงออบเจ็กต์เป้าหมายและพารามิเตอร์ด้วย สิ่งนี้มีประโยชน์เมื่อคุณต้องการเรียกใช้เมธอดบนออบเจ็กต์เฉพาะที่มีพารามิเตอร์เฉพาะไม่ใช่ในตอนนี้ แต่ในอนาคต คุณสามารถสร้างที่เหมาะสมNSInvocation
และเริ่มการทำงานในภายหลัง
มีความแตกต่างที่ลึกซึ้งระหว่างทั้งสอง
[object doSomething]; // is executed right away
[object performSelector:@selector(doSomething)]; // gets executed at the next runloop
นี่คือข้อความที่ตัดตอนมาจากเอกสารของ Apple
"performSelector: withObject: afterDelay: ดำเนินการตัวเลือกที่ระบุบนเธรดปัจจุบันในระหว่างรอบการทำงานรอบถัดไปและหลังจากช่วงหน่วงเวลาที่เป็นทางเลือกเนื่องจากจะรอจนกว่าจะถึงรอบการทำงานรอบถัดไปเพื่อดำเนินการตัวเลือกวิธีการเหล่านี้จะให้การหน่วงเวลามินิอัตโนมัติจาก รหัสที่ดำเนินการอยู่ในปัจจุบันตัวเลือกที่อยู่ในคิวหลายตัวจะดำเนินการทีละรายการตามลำดับที่ถูกจัดคิว "
performSelector:withObject:afterDelay:
แต่คำถามและตัวอย่างข้อมูลของคุณกำลังใช้อยู่performSelector:
ซึ่งเป็นวิธีการที่แตกต่างกันอย่างสิ้นเชิง จากเอกสารสำหรับมัน: <quote> performSelector:
วิธีการนี้เทียบเท่ากับการส่งaSelector
ข้อความไปยังผู้รับโดยตรง </quote>
performSelector/performSelector:withObject/performSelector:withObject:afterDelay
ทุกคนมีพฤติกรรมเหมือนกันซึ่งเป็นความผิดพลาด