Làm cách nào để tạo đại biểu trong Objective-C?


Câu trả lời:


889

Một đại biểu Objective-C là một đối tượng đã được gán cho thuộc delegatetính một đối tượng khác. Để tạo một lớp, bạn định nghĩa một lớp thực hiện các phương thức ủy nhiệm mà bạn quan tâm và đánh dấu lớp đó là thực hiện giao thức ủy nhiệm.

Ví dụ, giả sử bạn có một UIWebView. Nếu bạn muốn thực hiện webViewDidStartLoad:phương thức của đại biểu , bạn có thể tạo một lớp như thế này:

@interface MyClass<UIWebViewDelegate>
// ...
@end

@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView { 
    // ... 
}
@end

Sau đó, bạn có thể tạo một phiên bản của MyClass và gán nó làm đại biểu của chế độ xem web:

MyClass *instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;

Về UIWebViewphía, nó có thể có mã tương tự như thế này để xem liệu đại biểu có trả lời webViewDidStartLoad:tin nhắn bằng cách sử dụng respondsToSelector:và gửi nó nếu thích hợp.

if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
    [self.delegate webViewDidStartLoad:self];
}

Bản thân thuộc tính ủy nhiệm thường được khai báo weak(trong ARC) hoặc assign(trước ARC) để tránh các vòng lặp giữ lại, vì đại biểu của một đối tượng thường giữ tham chiếu mạnh đến đối tượng đó. (Ví dụ: bộ điều khiển chế độ xem thường là đại biểu của chế độ xem mà nó chứa.)

Làm đại biểu cho các lớp học của bạn

Để xác định các đại biểu của riêng bạn, bạn sẽ phải khai báo các phương thức của họ ở đâu đó, như được thảo luận trong Apple Docs về các giao thức . Bạn thường khai báo một giao thức chính thức. Tuyên bố, được diễn giải từ UIWebView.h, sẽ giống như thế này:

@protocol UIWebViewDelegate <NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end

Điều này tương tự với một giao diện hoặc lớp cơ sở trừu tượng, vì nó tạo ra một loại đặc biệt cho đại biểu của bạn, UIWebViewDelegatetrong trường hợp này. Các đại biểu triển khai sẽ phải áp dụng giao thức này:

@interface MyClass <UIWebViewDelegate>
// ...
@end

Và sau đó thực hiện các phương thức trong giao thức. Đối với các phương thức được khai báo trong giao thức là @optional(giống như hầu hết các phương thức ủy nhiệm), bạn cần kiểm tra -respondsToSelector:trước khi gọi một phương thức cụ thể trên nó.

Đặt tên

Các phương thức ủy nhiệm thường được đặt tên bắt đầu bằng tên lớp ủy nhiệm và lấy đối tượng ủy nhiệm làm tham số đầu tiên. Họ cũng thường sử dụng một hình thức di chúc, nên- hoặc đã làm. Vì vậy, webViewDidStartLoad:(tham số đầu tiên là chế độ xem web) chứ không phải loadStarted(không lấy tham số) chẳng hạn.

Tối ưu hóa tốc độ

Thay vì kiểm tra xem một đại biểu có phản hồi với bộ chọn mỗi lần chúng tôi muốn nhắn tin hay không, bạn có thể lưu trữ thông tin đó khi các đại biểu được đặt. Một cách rất rõ ràng để làm điều này là sử dụng bitfield, như sau:

@protocol SomethingDelegate <NSObject>
@optional
- (void)something:(id)something didFinishLoadingItem:(id)item;
- (void)something:(id)something didFailWithError:(NSError *)error;
@end

@interface Something : NSObject
@property (nonatomic, weak) id <SomethingDelegate> delegate;
@end

@implementation Something {
  struct {
    unsigned int didFinishLoadingItem:1;
    unsigned int didFailWithError:1;
  } delegateRespondsTo;
}
@synthesize delegate;

- (void)setDelegate:(id <SomethingDelegate>)aDelegate {
  if (delegate != aDelegate) {
    delegate = aDelegate;

    delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)];
    delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)];
  }
}
@end

Sau đó, trong phần thân, chúng ta có thể kiểm tra xem đại biểu của chúng ta xử lý các thông điệp bằng cách truy cập vào delegateRespondsTocấu trúc của chúng ta , thay vì gửi đi gửi -respondsToSelector:lại nhiều lần.

Đại biểu không chính thức

Trước khi giao thức tồn tại, nó đã được phổ biến để sử dụng một loại trên NSObjectđể khai báo các phương pháp một đại biểu có thể thực hiện. Ví dụ, CALayervẫn làm điều này:

@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end

Điều này nói với trình biên dịch rằng bất kỳ đối tượng có thể thực hiện displayLayer:.

Sau đó, bạn sẽ sử dụng -respondsToSelector:cách tiếp cận tương tự như được mô tả ở trên để gọi phương thức này. Các đại biểu thực hiện phương thức này và gán thuộc delegatetính, và đó là (không có tuyên bố bạn tuân thủ giao thức). Phương pháp này phổ biến trong các thư viện của Apple, nhưng mã mới nên sử dụng cách tiếp cận giao thức hiện đại hơn ở trên, vì cách tiếp cận này gây ô nhiễm NSObject(khiến tự động hoàn thành ít hữu ích hơn) và khiến trình biên dịch khó cảnh báo bạn về lỗi chính tả và lỗi tương tự.


Tôi nghĩ rằng bạn cần phải chuyển unsigned intloại thành BOOLgiá trị trả về delegate respondsToSelectorcủa loại BOOL.
Roland

Đại biểu có thể được sử dụng cho Đa hình như trong C ++ không?

@Dan Vâng, chắc chắn rồi. Các giao thức nói chung được sử dụng cho đa hình.
Jesse Rusak

@JesseRusak Tôi nghĩ rằng "JSS SomethingDelegate" phải là "SomethingDelegate" để thống nhất :)
Hans Knöchel

382

Câu trả lời được phê duyệt là tuyệt vời, nhưng nếu bạn đang tìm kiếm câu trả lời trong 1 phút, hãy thử điều này:

Tệp MyClass.h sẽ trông như thế này (thêm dòng đại biểu có ý kiến!)

#import <BlaClass/BlaClass.h>

@class MyClass;             //define class, so protocol can see MyClass
@protocol MyClassDelegate <NSObject>   //define delegate protocol
    - (void) myClassDelegateMethod: (MyClass *) sender;  //define delegate method to be implemented within another class
@end //end protocol

@interface MyClass : NSObject {
}
@property (nonatomic, weak) id <MyClassDelegate> delegate; //define MyClassDelegate as delegate

@end

Tệp MyClass.m sẽ trông như thế này

#import "MyClass.h"
@implementation MyClass 
@synthesize delegate; //synthesise  MyClassDelegate delegate

- (void) myMethodToDoStuff {
    [self.delegate myClassDelegateMethod:self]; //this will call the method implemented in your other class    
}

@end

Để sử dụng đại biểu của bạn trong một lớp khác (UIViewControll gọi là MyVC trong trường hợp này) MyVC.h:

#import "MyClass.h"
@interface MyVC:UIViewController <MyClassDelegate> { //make it a delegate for MyClassDelegate
}

MyVC.m:

myClass.delegate = self;          //set its delegate to self somewhere

Thực hiện phương pháp đại biểu

- (void) myClassDelegateMethod: (MyClass *) sender {
    NSLog(@"Delegates are great!");
}

4
Tuyệt vời để sử dụng câu trả lời này như một tài liệu tham khảo nhanh. Nhưng tại sao tài sản dành cho đại biểu trong MyClass.h của bạn được đánh dấu là 'IBOutlet'?
Arno van der Meer

4
@ArnovanderMeer Bắt tốt! Tôi không thể nhớ tại sao. Tôi cần nó trong dự án của tôi nhưng không phải trong ví dụ này, tôi đã loại bỏ nó. thx
Tibidabo

Cảm ơn. Đẹp và kỹ lưỡng như câu trả lời được chấp nhận, tôi học tốt nhất từ ​​một số mã mẫu nhỏ gọn. Thật tốt khi có hai câu trả lời.
sudo

@Tibidabo Hoàn toàn nổi bật. Tôi thực sự mong mọi người có thể giải thích các khái niệm lập trình như thế này. Tôi đã thấy hàng trăm lời giải thích, về 'đại biểu', trong nhiều năm qua và chưa bao giờ thực sự nắm bắt được lý thuyết này cho đến bây giờ! Cảm ơn bạn rất nhiều ...
Charles Robertson

5
Trường hợp được myClasskhởi tạo bên trong MyVC.m?
Lane Rettig

18

Khi sử dụng phương thức giao thức chính thức để tạo hỗ trợ cho đại biểu, tôi đã thấy rằng bạn có thể đảm bảo kiểm tra loại thích hợp (mặc dù, thời gian chạy, không phải biên dịch thời gian) bằng cách thêm một cái gì đó như:

if (![delegate conformsToProtocol:@protocol(MyDelegate)]) {
    [NSException raise:@"MyDelegate Exception"
                format:@"Parameter does not conform to MyDelegate protocol at line %d", (int)__LINE__];
}

trong mã truy cập đại biểu (setDelegate) của bạn. Điều này giúp giảm thiểu sai lầm.


18

Xin vui lòng! kiểm tra hướng dẫn từng bước đơn giản dưới đây để hiểu cách các Đại biểu hoạt động trong iOS.

Đại biểu trong iOS

Tôi đã tạo hai ViewControllers (để gửi dữ liệu từ người này sang người khác)

  1. FirstViewControll triển khai ủy nhiệm (cung cấp dữ liệu).
  2. SecondViewControll khai báo ủy nhiệm (sẽ nhận dữ liệu).

17

Có lẽ đây là nhiều hơn những gì bạn đang thiếu:

Nếu bạn đến từ một quan điểm như C ++, các đại biểu sẽ quen một chút - nhưng về cơ bản, 'họ chỉ hoạt động'.

Cách thức hoạt động là bạn đặt một số đối tượng mà bạn đã viết làm đại biểu cho NSWindow, nhưng đối tượng của bạn chỉ có các triển khai (phương thức) cho một hoặc một vài trong số nhiều phương thức ủy nhiệm có thể. Vì vậy, một cái gì đó xảy ra và NSWindowmuốn gọi đối tượng của bạn - nó chỉ sử dụng respondsToSelectorphương thức của Objective-c để xác định xem đối tượng của bạn có muốn phương thức đó được gọi không, và sau đó gọi nó. Đây là cách hoạt động của object-c - các phương thức được tìm kiếm theo yêu cầu.

Hoàn toàn không quan trọng để làm điều này với các đối tượng của riêng bạn, không có gì đặc biệt xảy ra, ví dụ bạn có thể có NSArray27 đối tượng, tất cả các loại đối tượng khác nhau, chỉ có 18 đối tượng trong số đó có -(void)setToBue;9 phương pháp khác. Vì vậy, để kêu gọi setToBluetất cả 18 người cần thực hiện, đại loại như thế này:

for (id anObject in myArray)
{
  if ([anObject respondsToSelector:@selector(@"setToBlue")])
     [anObject setToBlue]; 
}

Một điều khác về các đại biểu là chúng không được giữ lại, vì vậy bạn luôn phải đặt đại biểu niltheo MyClass deallocphương thức của mình .


15

Theo thông lệ tốt được Apple khuyến nghị, sẽ tốt cho đại biểu (theo giao thức, theo định nghĩa), phù hợp với NSObjectgiao thức.

@protocol MyDelegate <NSObject>
    ...
@end

& để tạo các phương thức tùy chọn trong đại biểu của bạn (nghĩa là các phương thức không nhất thiết phải được triển khai), bạn có thể sử dụng @optionalchú thích như thế này:

@protocol MyDelegate <NSObject>
    ...
    ...
      // Declaration for Methods that 'must' be implemented'
    ...
    ...
    @optional
    ...
      // Declaration for Methods that 'need not necessarily' be implemented by the class conforming to your delegate
    ...
@end

Vì vậy, khi sử dụng các phương thức mà bạn đã chỉ định là tùy chọn, bạn cần (trong lớp) kiểm tra respondsToSelectorxem chế độ xem (có phù hợp với đại biểu của bạn) có thực sự thực hiện (các) phương thức tùy chọn của bạn hay không.


11

Tôi nghĩ rằng tất cả những câu trả lời này rất có ý nghĩa một khi bạn hiểu các đại biểu. Cá nhân tôi đến từ vùng đất của C / C ++ và trước đó các ngôn ngữ thủ tục như Fortran, v.v ... đây là 2 phút tôi tìm kiếm các chất tương tự tương tự trong mô hình C ++.

Nếu tôi giải thích các đại biểu cho một lập trình viên C ++ / Java, tôi sẽ nói

Đại biểu là gì? Đây là những con trỏ tĩnh đến các lớp trong một lớp khác. Khi bạn gán một con trỏ, bạn có thể gọi các hàm / phương thức trong lớp đó. Do đó, một số hàm của lớp của bạn được "ủy nhiệm" (Trong thế giới C ++ - con trỏ tới bởi một con trỏ đối tượng lớp) cho một lớp khác.

Giao thức là gì? Về mặt khái niệm, nó phục vụ mục đích tương tự như tệp tiêu đề của lớp bạn đang chỉ định làm lớp đại biểu. Giao thức là một cách rõ ràng để xác định phương thức nào cần được thực hiện trong lớp mà con trỏ được đặt làm đại biểu trong một lớp.

Làm thế nào tôi có thể làm một cái gì đó tương tự trong C ++? Nếu bạn đã cố gắng thực hiện điều này trong C ++, bạn sẽ bằng cách định nghĩa các con trỏ tới các lớp (đối tượng) trong định nghĩa lớp và sau đó nối chúng với các lớp khác sẽ cung cấp các hàm bổ sung như là đại biểu cho lớp cơ sở của bạn. Nhưng hệ thống dây này cần phải được xử lý trong mã và sẽ vụng về và dễ bị lỗi. Mục tiêu C chỉ cho rằng các lập trình viên không giỏi nhất trong việc duy trì số thập phân này và cung cấp các hạn chế của trình biên dịch để thực thi một triển khai sạch.


Những gì bạn đang nói là ngữ nghĩa trong khi tôi đang nói về trực giác. Những gì bạn đang nói là chức năng ảo - nhưng chỉ để làm quen với thuật ngữ mới có thể là thách thức. Câu trả lời phục vụ cho những người mới bắt đầu muốn nghĩ về một paralell trong C ++ / C
DrBug 23/12/14

Những gì bạn đang nói không thực sự rõ ràng với tôi. Tại sao bạn không viết một phản hồi mới và để xem nếu nhiều người thấy nó hữu ích, họ sẽ bỏ phiếu?
DrBug

9

Phiên bản Swift

Một đại biểu chỉ là một lớp thực hiện một số công việc cho một lớp khác. Đọc đoạn mã sau để biết ví dụ Playground hơi ngớ ngẩn (nhưng hy vọng sẽ khai sáng) cho thấy cách thức này được thực hiện trong Swift.

// A protocol is just a list of methods (and/or properties) that must
// be used by any class that adopts the protocol.
protocol OlderSiblingDelegate: class {
    // This protocol only defines one required method
    func getYourNiceOlderSiblingAGlassOfWater() -> String
}

class BossyBigBrother {

    // The delegate is the BossyBigBrother's slave. This position can 
    // be assigned later to whoever is available (and conforms to the 
    // protocol).
    weak var delegate: OlderSiblingDelegate?

    func tellSomebodyToGetMeSomeWater() -> String? {
        // The delegate is optional because there might not be anyone
        // nearby to boss around.
        return delegate?.getYourNiceOlderSiblingAGlassOfWater()
    }
}

// PoorLittleSister conforms to the OlderSiblingDelegate protocol
class PoorLittleSister: OlderSiblingDelegate {

    // This method is repquired by the protocol, but the protocol said
    // nothing about how it needs to be implemented.
    func getYourNiceOlderSiblingAGlassOfWater() -> String {
        return "Go get it yourself!"
    }

}

// initialize the classes
let bigBro = BossyBigBrother()
let lilSis = PoorLittleSister()

// Set the delegate 
// bigBro could boss around anyone who conforms to the 
// OlderSiblingDelegate protocol, but since lilSis is here, 
// she is the unlucky choice.
bigBro.delegate = lilSis

// Because the delegate is set, there is a class to do bigBro's work for him.
// bigBro tells lilSis to get him some water.
if let replyFromLilSis = bigBro.tellSomebodyToGetMeSomeWater() {
    print(replyFromLilSis) // "Go get it yourself!"
}

Trong thực tế, đại biểu thường được sử dụng trong các tình huống sau

  1. Khi một lớp cần truyền đạt một số thông tin cho một lớp khác
  2. Khi một lớp muốn cho phép một lớp khác tùy chỉnh nó

Các lớp không cần biết bất cứ điều gì về nhau trước ngoại trừ lớp đại biểu phù hợp với giao thức được yêu cầu.

Tôi rất khuyên bạn nên đọc hai bài viết sau. Họ đã giúp tôi hiểu các đại biểu thậm chí còn tốt hơn các tài liệu đã làm.


8

Ok, đây không thực sự là một câu trả lời cho câu hỏi, nhưng nếu bạn đang tìm cách làm đại biểu của riêng mình thì có lẽ điều gì đó đơn giản hơn nhiều có thể là một câu trả lời tốt hơn cho bạn.

Tôi hầu như không thực hiện các đại biểu của mình vì tôi hiếm khi cần. Tôi chỉ có thể có MỘT đại biểu cho một đối tượng đại biểu. Vì vậy, nếu bạn muốn đại biểu của mình truyền thông / truyền dữ liệu một chiều hơn bạn sẽ thông báo tốt hơn nhiều.

NSNotification có thể truyền các đối tượng cho nhiều người nhận và nó rất dễ sử dụng. Nó hoạt động như thế này:

Tệp MyClass.m sẽ trông như thế này

#import "MyClass.h"
@implementation MyClass 

- (void) myMethodToDoStuff {
//this will post a notification with myClassData (NSArray in this case)  in its userInfo dict and self as an object
[[NSNotificationCenter defaultCenter] postNotificationName:@"myClassUpdatedData"
                                                    object:self
                                                  userInfo:[NSDictionary dictionaryWithObject:selectedLocation[@"myClassData"] forKey:@"myClassData"]];
}
@end

Để sử dụng thông báo của bạn trong các lớp khác: Thêm lớp làm người quan sát:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherClassUpdatedItsData:) name:@"myClassUpdatedData" object:nil];

Thực hiện bộ chọn:

- (void) otherClassUpdatedItsData:(NSNotification *)note {
    NSLog(@"*** Other class updated its data ***");
    MyClass *otherClass = [note object];  //the object itself, you can call back any selector if you want
    NSArray *otherClassData = [note userInfo][@"myClassData"]; //get myClass data object and do whatever you want with it
}

Đừng quên xóa lớp của bạn như một người quan sát nếu

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

8

giả sử bạn có một lớp mà bạn đã phát triển và muốn khai báo một thuộc tính đại biểu để có thể thông báo cho nó khi một số sự kiện xảy ra:

@class myClass;

@protocol myClassDelegate <NSObject>

-(void)myClass:(MyClass*)myObject requiredEventHandlerWithParameter:(ParamType*)param;

@optional
-(void)myClass:(MyClass*)myObject optionalEventHandlerWithParameter:(ParamType*)param;

@end


@interface MyClass : NSObject

@property(nonatomic,weak)id< MyClassDelegate> delegate;

@end

do đó, bạn khai báo một giao thức trong MyClasstệp tiêu đề (hoặc một tệp tiêu đề riêng) và khai báo các trình xử lý sự kiện bắt buộc / tùy chọn mà đại biểu của bạn phải / nên thực hiện, sau đó khai báo một thuộc tính MyClassthuộc loại ( id< MyClassDelegate>) có nghĩa là bất kỳ lớp c mục tiêu nào phù hợp với giao thức MyClassDelegate, bạn sẽ nhận thấy rằng thuộc tính của đại biểu được khai báo là yếu, điều này rất quan trọng để ngăn chặn chu kỳ giữ lại (thường xuyên nhất là đại biểu giữ lạiMyClass thể hiện, vì vậy nếu bạn tuyên bố đại biểu là giữ lại, cả hai sẽ giữ lại lẫn nhau trong số họ sẽ được phát hành).

bạn cũng sẽ nhận thấy rằng các phương thức giao thức chuyển MyClassthể hiện cho đại biểu làm tham số, đây là cách tốt nhất trong trường hợp đại biểu muốn gọi một số phương thức trên MyClasscá thể và cũng giúp khi đại biểu tuyên bố chính nó như MyClassDelegatenhiều MyClasstrường hợp, như khi bạn có nhiều UITableView'scác trường hợp trong của bạn ViewControllervà tuyên bố chính nó như là một UITableViewDelegatecho tất cả chúng.

và bên trong MyClassbạn thông báo cho đại biểu với các sự kiện được tuyên bố như sau:

if([_delegate respondsToSelector:@selector(myClass: requiredEventHandlerWithParameter:)])
{
     [_delegate myClass:self requiredEventHandlerWithParameter:(ParamType*)param];
}

trước tiên bạn kiểm tra xem đại biểu của bạn có phản hồi với phương thức giao thức mà bạn sắp gọi trong trường hợp đại biểu không thực hiện hay không và ứng dụng sẽ sập sau đó (ngay cả khi phương thức giao thức được yêu cầu).


6

Đây là một phương pháp đơn giản để tạo đại biểu

Tạo giao thức trong tệp .h. Đảm bảo rằng nó được xác định trước giao thức bằng cách sử dụng @ class theo sau là tên của UIViewControll< As the protocol I am going to use is UIViewController class>.

Bước: 1: Tạo một Giao thức lớp mới có tên "YourViewControll", đây sẽ là lớp con của lớp UIViewContoder và gán lớp này cho ViewContoder thứ hai.

Bước: 2: Chuyển đến tệp "YourViewControll" và sửa đổi nó như dưới đây:

#import <UIKit/UIkit.h>
@class YourViewController;

@protocol YourViewController Delegate <NSObject>

 @optional
-(void)defineDelegateMethodName: (YourViewController *) controller;

@required
-(BOOL)delegateMethodReturningBool: (YourViewController *) controller;

  @end
  @interface YourViewController : UIViewController

  //Since the property for the protocol could be of any class, then it will be marked as a type of id.

  @property (nonatomic, weak) id< YourViewController Delegate> delegate;

@end

Các phương thức được xác định trong hành vi giao thức có thể được kiểm soát với @optional và @required như một phần của định nghĩa giao thức.

Bước: 3: Thực hiện Đại biểu

    #import "delegate.h"

   @interface YourDelegateUser ()
     <YourViewControllerDelegate>
   @end

   @implementation YourDelegateUser

   - (void) variousFoo {
      YourViewController *controller = [[YourViewController alloc] init];
      controller.delegate = self;
   }

   -(void)defineDelegateMethodName: (YourViewController *) controller {
      // handle the delegate being called here
   }

   -(BOOL)delegateMethodReturningBool: (YourViewController *) controller {
      // handle the delegate being called here
      return YES;
   }

   @end

// kiểm tra xem phương thức đã được xác định trước khi bạn gọi nó chưa

 - (void) someMethodToCallDelegate {
     if ([[self delegate] respondsToSelector:@selector(defineDelegateMethodName:)]) {
           [self.delegate delegateMethodName:self]; 
     }
  }

5

Để tạo đại biểu của riêng bạn, trước tiên bạn cần tạo một giao thức và khai báo các phương thức cần thiết mà không cần thực hiện. Và sau đó thực hiện giao thức này vào lớp tiêu đề của bạn, nơi bạn muốn thực hiện các phương thức ủy nhiệm hoặc ủy nhiệm.

Một giao thức phải được khai báo như sau:

@protocol ServiceResponceDelegate <NSObject>

- (void) serviceDidFailWithRequestType:(NSString*)error;
- (void) serviceDidFinishedSucessfully:(NSString*)success;

@end

Đây là lớp dịch vụ trong đó một số nhiệm vụ nên được thực hiện. Nó chỉ ra cách xác định đại biểu và cách đặt đại biểu. Trong lớp thực hiện sau khi nhiệm vụ hoàn thành, các phương thức được gọi.

@interface ServiceClass : NSObject
{
id <ServiceResponceDelegate> _delegate;
}

- (void) setDelegate:(id)delegate;
- (void) someTask;

@end

@implementation ServiceClass

- (void) setDelegate:(id)delegate
{
_delegate = delegate;
}

- (void) someTask
{
/*

   perform task

*/
if (!success)
{
[_delegate serviceDidFailWithRequestType:@”task failed”];
}
else
{
[_delegate serviceDidFinishedSucessfully:@”task success”];
}
}
@end

Đây là lớp khung nhìn chính từ đó lớp dịch vụ được gọi bằng cách đặt ủy nhiệm cho chính nó. Và giao thức cũng được thực hiện trong lớp tiêu đề.

@interface viewController: UIViewController <ServiceResponceDelegate>
{
ServiceClass* _service;
}

- (void) go;

@end

@implementation viewController

//
//some methods
//

- (void) go
{
_service = [[ServiceClass alloc] init];
[_service setDelegate:self];
[_service someTask];
}

Đó là nó, và bằng cách thực hiện các phương thức ủy nhiệm trong lớp này, điều khiển sẽ quay trở lại sau khi hoàn thành thao tác / tác vụ.


4

Disclaimer: đây là Swiftphiên bản của cách tạo a delegate.

Vậy, đại biểu là gì? Trong quá trình phát triển phần mềm, có các kiến ​​trúc giải pháp có thể tái sử dụng chung giúp giải quyết các vấn đề thường xảy ra trong một bối cảnh nhất định, các mẫu này, vì thế, được biết đến như là các mẫu thiết kế. Đại biểu là một mẫu thiết kế cho phép một đối tượng gửi tin nhắn đến đối tượng khác khi một sự kiện cụ thể xảy ra. Tưởng tượng một đối tượng A gọi một đối tượng B để thực hiện một hành động. Khi hành động hoàn tất, đối tượng A nên biết rằng B đã hoàn thành nhiệm vụ và thực hiện hành động cần thiết, điều này có thể đạt được với sự giúp đỡ của các đại biểu!

Để giải thích rõ hơn, tôi sẽ chỉ cho bạn cách tạo một đại biểu tùy chỉnh chuyển dữ liệu giữa các lớp, với Swift trong một ứng dụng đơn giản, bắt đầu bằng cách tải xuống hoặc nhân bản dự án khởi động này và chạy nó!

Bạn có thể thấy một ứng dụng có hai lớp ViewController AViewController B. B có hai chế độ xem trên tap thay đổi màu nền của ViewController, không có gì quá phức tạp phải không? bây giờ chúng ta hãy nghĩ một cách dễ dàng để thay đổi màu nền của lớp A khi các khung nhìn trên lớp B được nhấn.

Vấn đề là các quan điểm này là một phần của lớp B và không biết gì về lớp A, vì vậy chúng ta cần tìm cách giao tiếp giữa hai lớp này và đó là nơi phái đoàn tỏa sáng. Tôi chia việc thực hiện thành 6 bước để bạn có thể sử dụng nó như một bảng cheat khi bạn cần.

Bước 1: Tìm kiếm dấu pragma bước 1 trong tệp ClassBVC và thêm phần này

//MARK: step 1 Add Protocol here.
protocol ClassBVCDelegate: class {
func changeBackgroundColor(_ color: UIColor?)
}

Bước đầu tiên là tạo một protocol, trong trường hợp này, chúng tôi sẽ tạo giao thức trong lớp B, bên trong giao thức bạn có thể tạo bao nhiêu chức năng mà bạn muốn dựa trên yêu cầu thực hiện của bạn. Trong trường hợp này, chúng ta chỉ có một hàm đơn giản chấp nhận một tùy chọn UIColorlàm đối số. Đây là một cách thực hành tốt để đặt tên cho các giao thức của bạn thêm từ delegateở cuối tên lớp, trong trường hợp này , ClassBVCDelegate.

Bước 2: Tìm kiếm dấu pragma bước 2 ClassVBCvà thêm phần này

//MARK: step 2 Create a delegate property here.
weak var delegate: ClassBVCDelegate?

Ở đây chúng ta chỉ cần tạo một thuộc tính đại biểu cho lớp, thuộc tính này phải áp dụng protocolloại và nó phải là tùy chọn. Ngoài ra, bạn nên thêm từ khóa yếu trước thuộc tính để tránh chu kỳ lưu giữ và rò rỉ bộ nhớ tiềm năng, nếu bạn không biết điều đó có nghĩa là không lo lắng bây giờ, chỉ cần nhớ thêm từ khóa này.

Bước 3: Hãy tìm những bước đánh dấu pragma 3 bên trong handleTap methodtrong ClassBVCvà thêm này

//MARK: step 3 Add the delegate method call here.
delegate?.changeBackgroundColor(tapGesture.view?.backgroundColor)

Một điều mà bạn nên biết, chạy ứng dụng và nhấn vào bất kỳ chế độ xem nào, bạn sẽ không thấy bất kỳ hành vi mới nào và điều đó đúng nhưng điều tôi muốn chỉ ra là ứng dụng không bị lỗi khi đại biểu được gọi và đó là vì chúng tôi tạo ra nó như một giá trị tùy chọn và đó là lý do tại sao nó không bị sập ngay cả khi được ủy quyền chưa tồn tại. Bây giờ chúng ta hãy đi đến ClassAVCtập tin và làm cho nó, ủy thác.

Bước 4: Tìm kiếm dấu pragma bước 4 bên trong phương thức handleTap ClassAVCvà thêm cái này bên cạnh loại lớp của bạn như thế này.

//MARK: step 4 conform the protocol here.
class ClassAVC: UIViewController, ClassBVCDelegate {
}

Bây giờ ClassAVC đã thông qua ClassBVCDelegategiao thức, bạn có thể thấy rằng trình biên dịch của bạn đang báo lỗi cho bạn rằng Loại Loại 'ClassAVC không tuân thủ giao thức' ClassBVCDelegate 'và điều này chỉ có nghĩa là bạn chưa sử dụng các phương thức của giao thức, hãy tưởng tượng rằng Khi lớp A chấp nhận giao thức cũng giống như ký hợp đồng với lớp B và hợp đồng này nói rằng Bất kỳ lớp nào chấp nhận tôi PHẢI sử dụng các chức năng của tôi!

Lưu ý nhanh: Nếu bạn đến từ một Objective-Cnền tảng, có lẽ bạn đang nghĩ rằng bạn cũng có thể tắt lỗi đó làm cho phương thức đó trở thành tùy chọn, nhưng đối với tôi, và có lẽ là của bạn, Swiftngôn ngữ không hỗ trợ tùy chọn protocols, nếu bạn muốn làm điều đó, bạn có thể tạo một tiện ích mở rộng cho bạn protocolhoặc sử dụng từ khóa @objc trong protocolquá trình triển khai của bạn .

Cá nhân, nếu tôi phải tạo một giao thức với các phương thức tùy chọn khác nhau, tôi muốn chia nó thành khác nhau protocols, theo cách đó tôi sẽ tuân theo khái niệm trao một trách nhiệm duy nhất cho các đối tượng của mình, nhưng nó có thể thay đổi dựa trên việc triển khai cụ thể.

đây là một bài viết tốt về các phương pháp tùy chọn.

Bước 5: Tìm dấu pragma bước 5 bên trong chuẩn bị cho phương thức segue và thêm phần này

//MARK: step 5 create a reference of Class B and bind them through the `prepareforsegue` method.
if let nav = segue.destination as? UINavigationController, let classBVC = nav.topViewController as? ClassBVC {
classBVC.delegate = self
}

Ở đây chúng ta chỉ tạo một thể hiện ClassBVCvà gán đại biểu của nó cho bản thân, nhưng bản thân ở đây là gì? tốt, bản thân là ClassAVCcái đã được ủy thác!

Bước 6: Cuối cùng, hãy tìm pragma bước 6 ClassAVCvà sử dụng các chức năng của protocol, bắt đầu nhập func changeBackgroundColor và bạn sẽ thấy rằng nó tự động hoàn thành nó cho bạn. Bạn có thể thêm bất kỳ triển khai nào bên trong nó, trong ví dụ này, chúng tôi sẽ chỉ thay đổi màu nền, thêm phần này.

//MARK: step 6 finally use the method of the contract
func changeBackgroundColor(_ color: UIColor?) {
view.backgroundColor = color
}

Bây giờ chạy ứng dụng!

Delegatescó ở khắp mọi nơi và bạn có thể sử dụng chúng mà không cần thông báo, nếu bạn tạo một đoàn tableviewtrong quá khứ bạn đã sử dụng, nhiều lớp UIKITcông việc xung quanh chúng và nhiều lớp khác frameworksnữa, chúng sẽ giải quyết những vấn đề chính này.

  • Tránh khớp nối chặt chẽ của các đối tượng.
  • Sửa đổi hành vi và sự xuất hiện mà không cần phải phân lớp các đối tượng.
  • Cho phép các tác vụ được xử lý cho bất kỳ đối tượng tùy ý.

Xin chúc mừng, bạn chỉ cần thực hiện một đại biểu tùy chỉnh, tôi biết rằng có lẽ bạn đang suy nghĩ, rất nhiều rắc rối chỉ cho việc này? tốt, ủy quyền là một mẫu thiết kế rất quan trọng để hiểu nếu bạn muốn trở thành một iOSnhà phát triển và luôn ghi nhớ rằng họ có mối quan hệ 1-1 giữa các đối tượng.

Bạn có thể xem hướng dẫn ban đầu ở đây


4

Câu trả lời thực sự đã được trả lời, nhưng tôi muốn đưa cho bạn một "bảng cheat" để tạo một đại biểu:

DELEGATE SCRIPT

CLASS A - Where delegate is calling function

@protocol <#Protocol Name#> <NSObject>

-(void)delegateMethod;

@end

@interface <#Some ViewController#> : <#UIViewController#> 

@property (nonatomic, assign) id <<#Protocol Name#>> delegate;

@end


@implementation <#Some ViewController#> 

-(void)someMethod {
    [self.delegate methodName];
}

@end




CLASS B - Where delegate is called 

@interface <#Other ViewController#> (<#Delegate Name#>) {}
@end

@implementation <#Other ViewController#> 

-(void)otherMethod {
    CLASSA *classA = [[CLASSA alloc] init];

    [classA setDelegate:self];
}

-delegateMethod() {

}

@end

2

ViewControll.h

@protocol NameDelegate <NSObject>

-(void)delegateMEthod: (ArgType) arg;

@end

@property id <NameDelegate> delegate;

ViewControll.m

[self.delegate delegateMEthod: argument];

MainViewControll.m

ViewController viewController = [ViewController new];
viewController.delegate = self;

Phương pháp:

-(void)delegateMEthod: (ArgType) arg{
}

2

Theo quan điểm của tôi, tạo lớp riêng cho phương thức ủy nhiệm đó và bạn có thể sử dụng nơi bạn muốn.

trong DropDownClass.h tùy chỉnh của tôi

typedef enum
{
 DDSTATE,
 DDCITY
}DropDownType;

@protocol DropDownListDelegate <NSObject>
@required
- (void)dropDownDidSelectItemWithString:(NSString*)itemString     DropDownType:(DropDownType)dropDownType;
@end
@interface DropDownViewController : UIViewController
{
 BOOL isFiltered;
}
@property (nonatomic, assign) DropDownType dropDownType;
@property (weak) id <DropDownListDelegate> delegate;
@property (strong, nonatomic) NSMutableArray *array1DropDown;
@property (strong, nonatomic) NSMutableArray *array2DropDown;

sau đó tệp in.m tạo mảng với các đối tượng,

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
CGFloat rowHeight = 44.0f;
return rowHeight;
}

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return isFiltered?[self.array1DropDown count]:[self.array2DropDown count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleTableIdentifier = @"TableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}

if (self.delegate) {
    if (self.dropDownType == DDCITY) {
        cell.textLabel.text = [self.array1DropDown objectAtIndex:indexPath.row];
    }
    else if (self.dropDownType == DDSTATE) {
        cell.textLabel.text = [self.array2DropDown objectAtIndex:indexPath.row];
    }
}
return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
 [self dismissViewControllerAnimated:YES completion:^{
    if(self.delegate){
        if(self.dropDownType == DDCITY){
            [self.delegate dropDownDidSelectItemWithString:[self.array1DropDown objectAtIndex:indexPath.row] DropDownType:self.dropDownType];
        }
        else if (self.dropDownType == DDSTATE) {
            [self.delegate dropDownDidSelectItemWithString:[self.array2DropDown objectAtIndex:indexPath.row] DropDownType:self.dropDownType];
        }
    }
}];
}

Ở đây tất cả được đặt cho lớp đại biểu tùy chỉnh. Sau đó bạn có thể sử dụng phương thức ủy nhiệm này ở nơi bạn muốn. Ví dụ ...

sau khi nhập bộ điều khiển khung nhìn khác của tôi sau đó

tạo hành động để gọi phương thức ủy nhiệm như thế này

- (IBAction)dropDownBtn1Action:(id)sender {
DropDownViewController *vehicleModelDropView = [[DropDownViewController alloc]init];
vehicleModelDropView.dropDownType = DDCITY;
vehicleModelDropView.delegate = self;
[self presentViewController:vehicleModelDropView animated:YES completion:nil];
}

sau đó gọi phương thức đại biểu như thế này

- (void)dropDownDidSelectItemWithString:(NSString *)itemString DropDownType:(DropDownType)dropDownType {
switch (dropDownType) {
    case DDCITY:{
        if(itemString.length > 0){
            //Here i am printing the selected row
            [self.dropDownBtn1 setTitle:itemString forState:UIControlStateNormal];
        }
    }
        break;
    case DDSTATE: {
        //Here i am printing the selected row
        [self.dropDownBtn2 setTitle:itemString forState:UIControlStateNormal];
    }

    default:
        break;
}
}

0

Đại biểu: - Tạo

@protocol addToCartDelegate <NSObject>

-(void)addToCartAction:(ItemsModel *)itemsModel isAdded:(BOOL)added;

@end

Gửi và vui lòng chỉ định đại biểu để xem bạn đang gửi dữ liệu

[self.delegate addToCartAction:itemsModel isAdded:YES];

0
//1.
//Custom delegate 
@protocol TB_RemovedUserCellTag <NSObject>

-(void)didRemoveCellWithTag:(NSInteger)tag;

@end

//2.
//Create a weak reference in a class where you declared the delegate
@property(weak,nonatomic)id <TB_RemovedUserCellTag> removedCellTagDelegate;

//3. 
// use it in the class
  [self.removedCellTagDelegate didRemoveCellWithTag:self.tag];

//4. import the header file in the class where you want to conform to the protocol
@interface MyClassUsesDelegate ()<TB_RemovedUserCellTag>

@end

// 5. Thực hiện phương thức trong lớp .m - (void) didRemoveCellWithTag: (NSInteger) tag {NSLog @ ("Tag% d", tag);

}


0

Hãy bắt đầu với một ví dụ, nếu chúng tôi mua một sản phẩm trực tuyến, nó sẽ trải qua các quy trình như vận chuyển / giao hàng được xử lý bởi các nhóm khác nhau. Vì vậy, nếu việc vận chuyển được hoàn thành, nhóm vận chuyển nên thông báo cho nhóm giao hàng và nó sẽ được truyền thông một đến một khi truyền thông tin này sẽ là chi phí chung cho những người khác / nhà cung cấp có thể chỉ muốn truyền thông tin này cho những người được yêu cầu.

Vì vậy, nếu chúng tôi nghĩ về ứng dụng của mình, một sự kiện có thể là một đơn đặt hàng trực tuyến & các nhóm khác nhau có thể giống như nhiều lượt xem.

Dưới đây là mã coi ShippingView là nhóm Vận chuyển & DeliveryView là nhóm giao hàng:

//Declare the protocol with functions having info which needs to be communicated
protocol ShippingDelegate : class {
    func productShipped(productID : String)
}
//shippingView which shows shipping status of products
class ShippingView : UIView
{

    weak var delegate:ShippingDelegate?
    var productID : String

    @IBAction func checkShippingStatus(sender: UIButton)
    {
        // if product is shipped
        delegate?.productShipped(productID: productID)
    }
}
//Delivery view which shows delivery status & tracking info
class DeliveryView: UIView,ShippingDelegate
{
    func productShipped(productID : String)
    {
        // update status on view & perform delivery
    }
}

//Main page on app which has both views & shows updated info on product whole status
class ProductViewController : UIViewController
{
    var shippingView : ShippingView
    var deliveryView : DeliveryView

    override func viewDidLoad() {
        super.viewDidLoad()
        // as we want to update shipping info on delivery view, so assign delegate to delivery object
        // whenever shipping status gets updated it will call productShipped method in DeliveryView & update UI.
        shippingView.delegate = deliveryView
        //
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.