Cara melakukan Callback di Objective-C

119

Bagaimana cara melakukan fungsi panggilan balik di Objective-C?

Saya hanya ingin melihat beberapa contoh lengkap dan saya harus memahaminya.

ReachConnection
sumber

Jawaban:

94

Biasanya, callback di objektif C dilakukan dengan delegasi. Berikut adalah contoh implementasi delegasi kustom;


File Header:

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

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

File Implementasi (.m)

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

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

Itu menggambarkan pendekatan umum. Anda membuat kategori di NSObject yang mendeklarasikan nama metode callback Anda. NSObject tidak benar-benar menerapkan metode ini. Jenis kategori ini disebut protokol informal, Anda hanya mengatakan bahwa banyak objek mungkin mengimplementasikan metode ini. Mereka adalah cara untuk meneruskan menyatakan jenis tanda tangan pemilih.

Selanjutnya Anda memiliki beberapa objek menjadi delegasi dari "MyClass" dan MyClass memanggil metode delegasi pada delegasi yang sesuai. Jika callback delegasi Anda bersifat opsional, Anda biasanya akan menjaganya di situs pengiriman dengan sesuatu seperti "if ([delegate respondsToSelector: @selector (myClassWillDoSomething :)) {". Dalam contoh saya, delegasi diharuskan untuk mengimplementasikan kedua metode tersebut.

Selain protokol informal, Anda juga dapat menggunakan protokol formal yang ditentukan dengan @protocol. Jika Anda melakukan itu, Anda akan mengubah jenis penyetel delegasi, dan variabel contoh menjadi " id <MyClassDelegate>" bukan hanya " id".

Selain itu, Anda akan melihat delegasi tidak dipertahankan. Hal ini biasanya dilakukan karena objek yang "memiliki" instance "MyClass" biasanya juga merupakan delegasi. Jika MyClass mempertahankan delegasinya, maka akan ada siklus retensi. Ada baiknya dalam metode dealloc kelas yang memiliki instance MyClass dan delegasinya adalah untuk menghapus referensi delegasi karena ini adalah penunjuk punggung yang lemah. Jika tidak, jika sesuatu membuat instance MyClass tetap hidup, Anda akan memiliki penunjuk yang menggantung.

Jon Hess
sumber
+1 Jawaban menyeluruh yang bagus. Icing pada kue akan menjadi tautan ke dokumentasi Apple yang lebih mendalam tentang delegasi. :-)
Quinn Taylor
Jon, terima kasih banyak atas bantuannya. Saya sangat menghargai bantuan Anda. Saya minta maaf tentang ini, tetapi saya tidak terlalu jelas menjawabnya. Message .m adalah kelas yang menetapkan dirinya sebagai delegasi selama panggilan fungsi doSomething. Apakah doSomething adalah fungsi callback yang dipanggil oleh pengguna? karena saya mendapat kesan bahwa pengguna memanggil doSomething, dan fungsi panggilan balik Anda adalah myClassWillDoSomethingg & myClassDidDoSomething. Selain itu, dapatkah Anda menunjukkan kepada saya cara membuat kelas yang lebih tinggi yang memanggil fungsi panggilan balik. Saya seorang programmer C jadi saya belum terlalu akrab dengan lingkungan Obj-C sampai sekarang.
ReachConnection
"Message .m" hanya berarti, dalam file .m Anda. Anda akan memiliki kelas terpisah, sebut saja "Foo". Foo akan memiliki variabel "MyClass * myClass", dan pada titik tertentu Foo akan berkata "[myClass setDelegate: self]". Kapan pun setelah itu, jika ada orang termasuk foo yang memanggil doSomethingMethod pada instance MyClass tersebut, foo akan meminta myClassWillDoSomething, dan metode myClassDidDoSomething akan dipanggil. Saya sebenarnya juga akan memposting contoh berbeda kedua yang tidak menggunakan delegasi.
Jon Hess
Saya tidak berpikir .m berarti "pesan".
Chuck
140

Untuk kelengkapan, karena StackOverflow RSS secara acak membangkitkan pertanyaan untuk saya, opsi lain (lebih baru) adalah menggunakan blok:

@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);
}];
Jens Ayton
sumber
2
@Ahruman: Apa yang dimaksud dengan "^" - karakter dalam "void (^ _completionHandler) (int someParameter);" berarti? Bisakah Anda menjelaskan apa yang baris itu lakukan?
Konrad Höffner
2
Dapatkah Anda memberikan penjelasan mengapa Anda perlu menyalin penangan panggilan balik?
Elliot Chance
52

Berikut adalah contoh yang membuat konsep delegasi tetap keluar, dan hanya melakukan panggilan mentah.

@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

Biasanya metode - [Foo doSomethingAndNotifyObject: withSelector:] akan menjadi asinkron yang akan membuat callback lebih berguna daripada di sini.

Jon Hess
sumber
1
Terima kasih banyak John. Saya memahami penerapan panggilan balik pertama Anda setelah komentar Anda. Selain itu, penerapan callback kedua Anda lebih mudah. Keduanya sangat bagus.
ReachConnection
1
Terima kasih telah memposting Jon ini, itu sangat membantu. Saya harus mengubah [object performSelectorwithObject: self]; ke [object performSelector: selector withObject: self]; agar berfungsi dengan benar.
Banjer
16

Untuk menjaga agar pertanyaan ini tetap mutakhir, pengenalan ARC iOS 5.0 berarti ini dapat dicapai menggunakan Blok lebih ringkas:

@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);
}];

Selalu ada F **** ng Block Syntax jika Anda lupa sintaks Block Objective-C.

Ryan Brodie
sumber
Dalam @interface + (void)sayHi:(void(^)(NSString *reply))callback;tidak boleh+ (void)sayHi:(void(^)(NSString *))callback;
Tim007
Tidak menurut Sintaks Blok F **** ng yang disebutkan di atas : - (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;(Catatan parameterTypestidak parameters)
Ryan Brodie
4

CallBack: Ada 4 jenis callback di Objective C

  1. Jenis pemilih: Anda dapat melihat NSTimer, UIPangesture adalah contoh callback Selector. Digunakan untuk eksekusi kode yang sangat terbatas.

  2. Jenis Delegasi : Umum dan paling banyak digunakan dalam kerangka Apple. UITableViewDelegate, NSNURLConnectionDelegate. Mereka biasanya digunakan untuk menunjukkan Mengunduh banyak gambar dari server secara asinkron dll.

  3. NSNotifications : NotificationCenter adalah salah satu fitur Objective C yang digunakan untuk memberi tahu banyak penerima pada saat peristiwa terjadi.
  4. Blok : Blok lebih umum digunakan dalam pemrograman Objective C. Ini adalah fitur hebat dan digunakan untuk mengeksekusi potongan kode. Anda juga bisa merujuk tutorial untuk memahami: Tutorial blok

Tolong beri tahu saya jika ada jawaban lain untuk itu. Saya akan menghargainya.

Anil Gupta
sumber