วัตถุประสงค์ -C: การเรียกตัวเลือกที่มีหลายอาร์กิวเมนต์


142

ใน MyClass.m ฉันได้กำหนดไว้แล้ว

- (void) myTest: (NSString *) withAString{
    NSLog(@"hi, %@", withAString);
}

และการประกาศที่เหมาะสมใน MyClass.h หลังจากนั้นฉันต้องการโทร

[self performSelector:@selector(mytest:withAString:) withObject: mystring];

ใน MyClass.m แต่ฉันได้รับข้อผิดพลาดคล้ายกับ * แอปสิ้นสุดเนื่องจากข้อยกเว้นที่ไม่ได้ตรวจสอบ 'NSInvalidArgumentException' เหตุผล: '* - [MyClass myTest: withAtring:]: ตัวเลือกที่ไม่รู้จักถูกส่งไปยังอินสแตนซ์ 0xe421f0'

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


4
โพสต์ของคุณถามถึง 'ข้อโต้แย้งหลายข้อ' แต่คุณใช้เพียงข้อเดียว ตอนนี้ฉันอยากรู้ว่าใครบางคนจะทำอย่างไรกับข้อโต้แย้งหลาย ๆ นอกเหนือจากการห่อไว้ในอาร์เรย์ / dict / อะไรก็ตาม
RonLugge

คำตอบ:


137

ลายเซ็นวิธีการของคุณคือ:

- (void) myTest:(NSString *)

โดยมีเกิดขึ้นเป็นพารามิเตอร์ (ชื่อทำให้เข้าใจผิดดูเหมือนว่าเป็นส่วนหนึ่งของลายเซ็นของตัวเลือก)

หากคุณเรียกใช้ฟังก์ชันในลักษณะนี้:

[self performSelector:@selector(myTest:) withObject:myString];

มันจะทำงาน.

แต่ตามที่ผู้โพสต์คนอื่น ๆ แนะนำไว้คุณอาจต้องการเปลี่ยนชื่อวิธี:

- (void)myTestWithAString:(NSString*)aString;

และโทร:

[self performSelector:@selector(myTestWithAString:) withObject:myString];

2
ตอนนี้ฉันเห็นผู้คนได้รับประโยชน์จากคำตอบนี้ฉันได้ทบทวนคำตอบของฉันแล้ว ฉันขอแนะนำให้โทรเป็นเพียง: - (void) testWithString: (NSString *) aString;
Lyndsey Ferguson

313

ใน Objective-C ลายเซ็นของตัวเลือกประกอบด้วย:

  1. ชื่อของวิธีการ (ในกรณีนี้จะเป็น 'myTest') (จำเป็น)
  2. A ':' (โคลอน) ตามหลังชื่อเมธอดหากเมธอดมีอินพุต
  3. ชื่อและ ':' สำหรับอินพุตเพิ่มเติมทุกรายการ

Selectors ไม่มีความรู้เกี่ยวกับ:

  1. ประเภทอินพุต
  2. ประเภทผลตอบแทนของวิธีการ

นี่คือการใช้คลาสที่เมธอด performMethodsViaSelectors ดำเนินการเมธอดคลาสอื่นโดยใช้ตัวเลือก:

@implementation ClassForSelectors
- (void) fooNoInputs {
    NSLog(@"Does nothing");
}
- (void) fooOneIput:(NSString*) first {
    NSLog(@"Logs %@", first);
}
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second {
    NSLog(@"Logs %@ then %@", first, second);
}
- (void) performMethodsViaSelectors {
    [self performSelector:@selector(fooNoInputs)];
    [self performSelector:@selector(fooOneInput:) withObject:@"first"];
    [self performSelector:@selector(fooFirstInput:secondInput:) withObject:@"first" withObject:@"second"];
}
@end

วิธีที่คุณต้องการสร้างตัวเลือกสำหรับมีอินพุตเดียวดังนั้นคุณจะต้องสร้างตัวเลือกสำหรับมัน:

SEL myTestSelector = @selector(myTest:);

3
คำตอบที่ดี. หากต้องการให้ความกระจ่างเล็กน้อยคุณชื่อตัวเลือกต้องมีอย่างน้อยหนึ่งส่วนซึ่งอาจหรืออาจไม่ใช้พารามิเตอร์ - หากเป็นเช่นนั้นจะต้องมีเครื่องหมายโคลอน ชื่อตัวเลือกที่มีสองส่วนขึ้นไปจะต้องมีเครื่องหมายโคลอนอยู่หลังแต่ละส่วน - มันไม่ถูกต้องที่จะมีตัวเลือกของแบบฟอร์ม "-useFoo: andBar: toDoSomething"
Quinn Taylor

ขอบคุณสำหรับสิ่งนี้. ฉันเคยดิ้นรนกับสิ่งนี้มาระยะหนึ่งแล้วดีใจที่ได้รับความช่วยเหลือ!
James Hall

พารามิเตอร์อินพุตเป็นตัวเลขจำนวนเต็มหรือไม่ จะทำอย่างไรในกรณีนี้?
Hoang Pham

1
คุณจะต้องห่อจำนวนเต็มในวัตถุ NSNumber (ดูdeveloper.apple.com/library/ios/#documentation/Cocoa/Reference/ ...... ) และดึงค่าจำนวนเต็มในเนื้อความของวิธีการที่เรียก อาจเป็น verbose เล็กน้อย (และฉันไม่พบวิธีที่ดีกว่ารอบ ๆ ) แต่มันใช้งานได้ดี
Shane Arney

30
+100: มันเยี่ยมมาก! ฉันไม่รู้เกี่ยวกับความสามารถในการใช้พารามิเตอร์ "withObject:" หลายตัว ฉันจะ upvote นี้ร้อยครั้งถ้าผม ...
FreeAsInBeer

13

@ Shane Arney

performSelector:withObject:withObject:

คุณอาจต้องการพูดถึงว่าวิธีนี้ใช้สำหรับส่งอาร์กิวเมนต์สูงสุด 2 ข้อเท่านั้นและไม่สามารถล่าช้าได้ performSelector:withObject:afterDelay:)(เช่น

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


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

7

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

-(void)myTestWithString:(NSString *)sourceString comparedTo:(NSString *)testString ;

การสร้างตัวเลือกสำหรับเมธอดที่รับอาร์กิวเมนต์หลายตัวนั้นใช้ได้อย่างสมบูรณ์ (เช่น @selector (myTestWithString: comparTo :)) อย่างไรก็ตามเมธอด performSelector อนุญาตให้คุณส่งค่าหนึ่งค่าไปยัง myTest เท่านั้นซึ่งน่าเสียดายที่มีพารามิเตอร์มากกว่าหนึ่งพารามิเตอร์ มันจะผิดพลาดและบอกคุณว่าคุณไม่ได้ให้ค่าที่เพียงพอ

คุณสามารถกำหนดวิธีการเก็บรวบรวมใหม่ได้ตลอดเวลาเนื่องจากเป็นพารามิเตอร์เท่านั้น:

-(void)myTestWithObjects:(NSDictionary *)testObjects ;

อย่างไรก็ตามมีวิธีการแก้ปัญหาที่สง่างามมากขึ้น คำตอบคือใช้ NSInvocation พร้อมกับsetArgument:atIndex:และinvokeวิธีการ

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

โชคดี!


3

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

คุณคาดหวังว่าจะใช้พารามิเตอร์จำนวนเท่าใด


ขออภัยคุณเขียน ฉันพิมพ์มันออกมาและพยายามทำให้มันง่ายขึ้นแทนที่จะทำสำเนาและวางรหัสของฉัน แต่ฉันทำผิดพลาดในกระบวนการ ฉันคาดว่าวิธีนี้จะใช้พารามิเตอร์เดียว; สตริงที่ฉันต้องการจะพิมพ์
Stu

2

คิดว่าคลาสควรถูกกำหนดเป็น:

- (void) myTestWithSomeString:(NSString *) astring{
    NSLog(@"hi, %s", astring);
}

คุณมีเพียงพารามิเตอร์เดียวดังนั้นคุณควรมีเพียงพารามิเตอร์เดียว:

คุณอาจต้องการพิจารณาใช้% @ ใน NSLog ของคุณด้วย - มันเป็นนิสัยที่ดีในการเข้า - แล้วจะเขียนวัตถุใด ๆ - ไม่ใช่แค่สายอักขระ


-1

ผู้ใช้ iOS ยังคาดหวังการเติมข้อความอัตโนมัติในฟิลด์ข้อความมาตรฐานตัวอักษรตัวแรกของประโยคในภาษาที่เล็ก

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

เอกสารของ Apple บอกว่าไม่มี API สำหรับคุณลักษณะนี้และคุณลักษณะอื่น ๆ ที่คาดหวังในกระดานข้อมูลที่กำหนดเอง ดังนั้นคุณต้องค้นหาตรรกะของคุณเองเพื่อใช้งานสิ่งนี้

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