วิธีดำเนินการโทรกลับใน Objective-C


119

จะเรียกใช้ฟังก์ชันเรียกกลับใน Objective-C ได้อย่างไร?

ฉันต้องการดูตัวอย่างที่สมบูรณ์และฉันควรเข้าใจ

คำตอบ:


94

โดยปกติการโทรกลับในวัตถุประสงค์ C จะกระทำกับผู้รับมอบสิทธิ์ นี่คือตัวอย่างของการใช้งานตัวแทนแบบกำหนดเอง


ไฟล์ส่วนหัว:

@interface MyClass : NSObject {
    id delegate;
}
- (void)setDelegate:(id)delegate;
- (void)doSomething;
@end

@interface NSObject(MyDelegateMethods)
- (void)myClassWillDoSomething:(MyClass *)myClass;
- (void)myClassDidDoSomething:(MyClass *)myClass;
@end

ไฟล์ Implementation (.m)

@implementation MyClass
- (void)setDelegate:(id)aDelegate {
    delegate = aDelegate; /// Not retained
}

- (void)doSomething {
    [delegate myClassWillDoSomething:self];
    /* DO SOMETHING */
    [delegate myClassDidDoSomething:self];
}
@end

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

ถัดไปคุณมีวัตถุบางอย่างคือผู้รับมอบสิทธิ์ของ "MyClass" และ MyClass จะเรียกใช้วิธีการมอบสิทธิ์ในผู้รับมอบสิทธิ์ตามความเหมาะสม หากการโทรกลับของผู้รับมอบสิทธิ์เป็นทางเลือกโดยทั่วไปคุณจะป้องกันการเรียกกลับที่ไซต์การจัดส่งด้วยข้อความเช่น "if ([delegate responseToSelector: @selector (myClassWillDoSomething :)) {" ในตัวอย่างของฉันผู้ร่วมประชุมจำเป็นต้องใช้ทั้งสองวิธี

แทนที่จะใช้โปรโตคอลแบบไม่เป็นทางการคุณยังสามารถใช้โปรโตคอลที่เป็นทางการที่กำหนดด้วย @protocol หากคุณทำเช่นนั้นคุณจะเปลี่ยนประเภทของตัวตั้งค่าผู้รับมอบสิทธิ์และตัวแปรอินสแตนซ์เป็น " id <MyClassDelegate>" แทนที่จะเป็นเพียง " id"

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


+1 คำตอบที่ดีอย่างละเอียด ไอซิ่งบนเค้กจะเชื่อมโยงไปยังเอกสารของ Apple ในเชิงลึกเพิ่มเติมเกี่ยวกับผู้ได้รับมอบหมาย :-)
Quinn Taylor

จอนขอบคุณมากสำหรับความช่วยเหลือของคุณ ผมขอขอบคุณความช่วยเหลือของคุณ. ฉันขอโทษเกี่ยวกับเรื่องนี้ แต่ฉันยังไม่ชัดเจนในคำตอบ Message .m เป็นคลาสที่ตั้งค่าตัวเองเป็นผู้รับมอบสิทธิ์ระหว่างการเรียกใช้ฟังก์ชัน doSomething doSomething เป็นฟังก์ชันเรียกกลับที่ผู้ใช้เรียกหรือไม่ เนื่องจากฉันรู้สึกว่าผู้ใช้เรียก doSomething และฟังก์ชันการโทรกลับของคุณคือ myClassWillDoSomethingg & myClassDidDoSomething เช่นกันคุณช่วยแสดงวิธีสร้างคลาสที่สูงกว่าที่เรียกใช้ฟังก์ชันโทรกลับได้ไหม ฉันเป็นโปรแกรมเมอร์ภาษาซีดังนั้นฉันจึงยังไม่คุ้นเคยกับสภาพแวดล้อม Obj-C มากนัก
ReachConnection

"ข้อความ. m" มีความหมายเพียงแค่ในไฟล์. m ของคุณ คุณจะมีชั้นเรียนแยกกันเรียกว่า "Foo" Foo จะมีตัวแปร "MyClass * myClass" และเมื่อถึงจุดหนึ่ง Foo จะพูดว่า "[myClass setDelegate: self]" ณ จุดใดก็ตามหลังจากนั้นหากใครก็ตามรวมถึง foo เรียกใช้ doSomethingMethod บนอินสแตนซ์ของ MyClass foo จะมีการเรียกใช้เมธอด myClassWillDoSomething และ myClassDidDoSomething ฉันจะโพสต์ตัวอย่างอื่นที่สองที่ไม่ใช้ผู้รับมอบสิทธิ์
Jon Hess

ฉันไม่คิดว่า. m ย่อมาจาก "message"
ชัค


140

เพื่อความสมบูรณ์เนื่องจาก StackOverflow RSS เพิ่งฟื้นคืนคำถามให้ฉันแบบสุ่มตัวเลือกอื่น (ใหม่กว่า) คือการใช้บล็อก:

@interface MyClass: NSObject
{
    void (^_completionHandler)(int someParameter);
}

- (void) doSomethingWithCompletionHandler:(void(^)(int))handler;
@end


@implementation MyClass

- (void) doSomethingWithCompletionHandler:(void(^)(int))handler
{
    // NOTE: copying is very important if you'll call the callback asynchronously,
    // even with garbage collection!
    _completionHandler = [handler copy];

    // Do stuff, possibly asynchronously...
    int result = 5 + 3;

    // Call completion handler.
    _completionHandler(result);

    // Clean up.
    [_completionHandler release];
    _completionHandler = nil;
}

@end

...

MyClass *foo = [[MyClass alloc] init];
int x = 2;
[foo doSomethingWithCompletionHandler:^(int result){
    // Prints 10
    NSLog(@"%i", x + result);
}];

2
@ Ahruman: อะไรคืออักขระ "^" - ใน "void (^ _completionHandler) (int someParameter);" หมายความว่าอย่างไร คุณช่วยอธิบายได้ไหมว่าเส้นนั้นทำอะไร?
Konrad Höffner

2
คุณสามารถให้คำอธิบายว่าเหตุใดคุณจึงต้องคัดลอกตัวจัดการการโทรกลับ
Elliot Chance

52

นี่คือตัวอย่างที่ช่วยให้แนวคิดของผู้ได้รับมอบหมายออกไปและทำการโทรกลับ

@interface Foo : NSObject {
}
- (void)doSomethingAndNotifyObject:(id)object withSelector:(SEL)selector;
@end

@interface Bar : NSObject {
}
@end

@implementation Foo
- (void)doSomethingAndNotifyObject:(id)object withSelector:(SEL)selector {
    /* do lots of stuff */
    [object performSelector:selector withObject:self];
}
@end

@implementation Bar
- (void)aMethod {
    Foo *foo = [[[Foo alloc] init] autorelease];
    [foo doSomethingAndNotifyObject:self withSelector:@selector(fooIsDone:)];
}

- (void)fooIsDone:(id)sender {
    NSLog(@"Foo Is Done!");
}
@end

โดยทั่วไปวิธีการ - [Foo doSomethingAndNotifyObject: withSelector:] จะเป็นแบบอะซิงโครนัสซึ่งจะทำให้การโทรกลับมีประโยชน์มากกว่าที่อยู่ที่นี่


1
ขอบคุณมากครับจอห์น ฉันเข้าใจการใช้งานการโทรกลับครั้งแรกของคุณหลังจากความคิดเห็นของคุณ เช่นกันการใช้งานการเรียกกลับครั้งที่ 2 ของคุณตรงไปตรงมามากกว่า ทั้งสองอย่างดีมาก
ReachConnection

1
ขอบคุณที่โพสต์จอนนี้มันมีประโยชน์มาก ฉันต้องเปลี่ยน [object performSelectorwithObject: self]; ไปยัง [object performSelector: selector withObject: self]; เพื่อให้ทำงานได้อย่างถูกต้อง
บรรเจิด

16

เพื่อให้คำถามนี้เป็นปัจจุบันการเปิดตัวARCของ iOS 5.0 หมายความว่าสามารถทำได้โดยใช้Blocksอย่างรัดกุมยิ่งขึ้น:

@interface Robot: NSObject
+ (void)sayHi:(void(^)(NSString *))callback;
@end

@implementation Robot
+ (void)sayHi:(void(^)(NSString *))callback {
    // Return a message to the callback
    callback(@"Hello to you too!");
}
@end

[Robot sayHi:^(NSString *reply){
  NSLog(@"%@", reply);
}];

มีF **** ng Block Syntaxอยู่เสมอหากคุณลืมไวยากรณ์ Block ของ Objective-C


ใน @interface ควรจะเป็น+ (void)sayHi:(void(^)(NSString *reply))callback;ไม่ได้+ (void)sayHi:(void(^)(NSString *))callback;
Tim007

ไม่เป็นไปตามF **** ng Block Syntaxข้างต้น: - (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;(Note parameterTypesnot parameters)
Ryan Brodie

4

CallBack: มีการโทรกลับ 4 ประเภทใน Objective C

  1. ประเภทตัวเลือก : คุณสามารถดู NSTimer, UIPangesture เป็นตัวอย่างของ Selector callback ใช้สำหรับการเรียกใช้โค้ดที่ จำกัด มาก

  2. ประเภทผู้รับมอบสิทธิ์ : ทั่วไปและใช้มากที่สุดในกรอบงานของ Apple UITableViewDelegate, NSNURLConnectionDelegate โดยปกติจะใช้เพื่อแสดงการดาวน์โหลดรูปภาพจำนวนมากจากเซิร์ฟเวอร์แบบอะซิงโครนัสเป็นต้น

  3. NSNotifications : NotificationCenter เป็นหนึ่งในคุณสมบัติของ Objective C ซึ่งใช้ในการแจ้งพนักงานรับเงินจำนวนมากในเวลาที่เกิดเหตุการณ์
  4. บล็อก : บล็อกมักใช้ในการเขียนโปรแกรม Objective C เป็นคุณสมบัติที่ยอดเยี่ยมและใช้สำหรับการเรียกใช้งานโค้ด คุณยังสามารถดูบทช่วยสอนเพื่อทำความเข้าใจ: บทช่วยสอนเกี่ยวกับบล็อก

โปรดแจ้งให้เราทราบหากมีคำตอบอื่น ๆ ฉันจะขอบคุณมัน

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