2009-06-18 65 views
109

Làm thế nào để thực hiện các chức năng gọi lại trong mục tiêu-C?Cách thực hiện cuộc gọi lại trong mục tiêu-C

Tôi chỉ muốn xem một số ví dụ đã hoàn thành và tôi nên hiểu nó.

+3

Câu hỏi này thật tuyệt vời và câu trả lời thực sự hữu ích .. –

+0

không tưởng tượng được điều này là khó hiểu ……… –

Trả lời

89

Thông thường, các cuộc gọi lại trong mục tiêu C được thực hiện với các đại biểu. Dưới đây là ví dụ về triển khai đại biểu tùy chỉnh;

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

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


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

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

Điều đó minh họa cách tiếp cận chung. Bạn tạo một danh mục trên NSObject khai báo tên của các phương thức gọi lại của bạn. NSObject không thực sự triển khai các phương thức này. Loại thể loại này được gọi là giao thức không chính thức, bạn chỉ nói rằng nhiều đối tượng có thể triển khai các phương thức này. Chúng là một cách để chuyển tiếp khai báo kiểu chữ ký của bộ chọn.

Tiếp theo bạn có một số đối tượng là đại biểu của "MyClass" và MyClass gọi các phương thức ủy nhiệm trên đại biểu khi thích hợp. Nếu callback đại biểu của bạn là tùy chọn, bạn sẽ thường bảo vệ chúng tại trang web công văn với một cái gì đó như "if ([delegate respondsToSelector: @selector (myClassWillDoSomething :)) {". Trong ví dụ của tôi, ủy nhiệm được yêu cầu để thực hiện cả hai phương thức.

Thay vì giao thức không chính thức, bạn cũng có thể sử dụng giao thức chính thức được xác định với @protocol. Nếu bạn làm điều đó, bạn sẽ thay đổi loại thiết lập đại biểu và biến mẫu thành "id <MyClassDelegate>" thay vì chỉ "id".

Ngoài ra, bạn sẽ nhận thấy đại biểu không được giữ lại. Điều này thường được thực hiện bởi vì đối tượng "sở hữu" các cá thể của "MyClass" thường là đại biểu. Nếu MyClass giữ lại đại biểu của nó, sau đó sẽ có một chu kỳ giữ lại. Đó là một ý tưởng tốt trong phương thức dealloc của một lớp có một cá thể MyClass và là ủy nhiệm của nó để xóa tham chiếu ủy nhiệm vì nó là một con trỏ ngược yếu. Nếu không, nếu một cái gì đó đang giữ dụ MyClass còn sống, bạn sẽ có một con trỏ lơ lửng.

+0

+1 Câu trả lời hoàn chỉnh kỹ lưỡng. Đóng băng trên bánh sẽ là một liên kết đến tài liệu Apple chuyên sâu hơn về các đại biểu. :-) –

+0

Jon, cảm ơn rất nhiều vì sự giúp đỡ của bạn. Tôi thực sự đánh giá cao sự giúp đỡ của bạn. Tôi xin lỗi về điều này, nhưng tôi không quá rõ ràng về câu trả lời. Thư .m là một lớp tự đặt làm đại biểu trong khi gọi hàm doSomething. Hàm doSomething có phải là hàm gọi lại mà người dùng gọi không? kể từ khi tôi dưới ấn tượng rằng người dùng gọi doSomething, và các chức năng gọi lại của bạn là myClassWillDoSomethingg & myClassDidDoSomething. Đồng thời, bạn có thể chỉ cho tôi cách tạo một lớp cao hơn gọi hàm gọi lại. Tôi là lập trình viên C nên tôi không quá quen thuộc với môi trường Obj-C. – ReachConnection

+0

"Thư .m" chỉ có nghĩa là, trong tệp .m của bạn. Bạn sẽ có một lớp riêng biệt, hãy gọi nó là "Foo". Foo sẽ có biến "MyClass * myClass" và tại một số điểm, Foo sẽ nói "[myClass setDelegate: self]". Tại bất kỳ thời điểm nào sau đó, nếu có ai bao gồm foo gọi hàm doSomethingMethod trên cá thể đó của MyClass, foo sẽ có myClassWillDoSomething của nó và các phương thức myClassDidDoSomething được gọi. Tôi cũng sẽ chỉ đăng một ví dụ khác thứ hai không sử dụng các đại biểu. –

46

Dưới đây là một ví dụ giúp duy trì khái niệm đại biểu và chỉ thực hiện lại cuộc gọi thô.

@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 

Thường là phương pháp - [Foo doSomethingAndNotifyObject: withSelector:] sẽ không đồng bộ làm cho hàm gọi lại hữu ích hơn ở đây.

+1

Cảm ơn rất nhiều John. Tôi hiểu triển khai gọi lại đầu tiên của bạn sau nhận xét của bạn. Đồng thời, triển khai gọi lại thứ 2 của bạn đơn giản hơn. Cả hai đều rất tốt. – ReachConnection

+1

Cảm ơn bạn đã đăng bài Jon này, nó rất hữu ích. Tôi đã phải thay đổi [object performSelectorwithObject: self]; tới [object performSelector: selector withObject: self]; để làm cho nó hoạt động chính xác. – Banjer

+0

Chắc chắn, đó là một câu trả lời StackOverflow cổ điển :) Hey, ai đó nên mở rộng một chút về "Thông thường các phương pháp sẽ là không đồng bộ ..." có thể là một ví dụ về điều đó ?? Tuyệt vời. – Fattie

130

Để hoàn chỉnh, kể từ StackOverflow RSS phục sinh chỉ ngẫu nhiên các câu hỏi cho tôi, người kia (mới hơn) tùy chọn là sử dụng các khối:

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

yêu giải pháp này - tương tự như thực hiện chức năng ẩn danh cho các cuộc gọi lại, đơn giản, ít mã hơn để viết. – David

+2

@Ahruman: Dấu "^" - ký tự trong "void (^ _completionHandler) (int someParameter);" nghĩa là? Bạn có thể giải thích những gì dòng đó không? –

+0

Các khối cũng làm cho việc gọi các callback kiểu C trở nên dễ dàng hơn (các con trỏ hàm nhận các tham số 'void *') thành các khối. Tôi đã viết một ví dụ làm thế nào để làm điều này ở đây: [Gói các callback kiểu C với các khối] (http://cutecoder.org/programming/wrapping-style-callbacks-blocks/). – adib

5

Để giữ cho câu hỏi này up-to-date, iOS 5.0 của giới thiệu ARC nghĩa này có thể đạt được bằng Blocks thậm chí ngắn gọn hơn:

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

luôn luôn F****ng Block Syntax nếu bạn đã bao giờ quên cú pháp Khối Objective-C.

+0

Trong @interface nên là '+ (void) sayHi: (void (^) (NSString * reply)) gọi lại;' không '+ (void) sayHi: (void (^) (NSString *)) gọi lại; ' –

+0

Không theo [F **** ng Block Syntax] đã nói ở trên (http://goshdarnblocksyntax.com/): '- (void) someMethodThatTakesABlock: (returnType (^ nullability) (parameterTypes)) blockName;' (Chú ý 'parameterTypes' không 'tham số') –

3

CallBack: Có 4 loại callback trong Objective C

  1. Selector loại: Bạn có thể thấy NSTimer, UIPangesture là những ví dụ của Selector gọi lại. Được sử dụng để thực thi mã rất hạn chế.

  2. Loại đại biểu: Phổ biến và được sử dụng nhiều nhất trong khung công tác của Apple. UITableViewDelegate, NSNURLConnectionDelegate. Chúng thường được sử dụng để hiển thị Tải xuống nhiều hình ảnh từ máy chủ một cách không đồng bộ, v.v.

  3. NSNotifications: NotificationCenter là một trong các tính năng của Mục tiêu C được sử dụng để thông báo cho nhiều người tiếp nhận tại thời điểm sự kiện xảy ra.
  4. Khối: Các khối thường được sử dụng trong lập trình C mục tiêu. Đó là tính năng tuyệt vời và được sử dụng để thực thi đoạn mã. Bạn cũng có thể tham khảo hướng dẫn để hiểu: Blocks tutorial

Vui lòng cho tôi biết nếu có câu trả lời khác. Tôi sẽ đánh giá cao nó.

Các vấn đề liên quan