Objective-C에서 델리게이트를 작성하는 방법


답변:


889

Objective-C 대리자는 delegate다른 개체 속성 에 할당 된 개체입니다. 하나를 만들려면 원하는 대리자 메서드를 구현하는 클래스를 정의하고 해당 클래스를 대리자 프로토콜을 구현하는 것으로 표시합니다.

예를 들어 UIWebView. 대리자의 webViewDidStartLoad:메서드 를 구현하려면 다음과 같은 클래스를 만들 수 있습니다.

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

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

그런 다음 MyClass 인스턴스를 만들어 웹보기의 대리자로 할당 할 수 있습니다.

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

한편, UIWebView대리자가 webViewDidStartLoad:메시지를 사용 하여 메시지에 응답하고 respondsToSelector:적절한 경우 메시지를 보내는 지 확인 하기 위해 이와 유사한 코드가 있을 수 있습니다.

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

대리인 속성 자체는 일반적으로 weak(ARC로) 선언 되거나assign객체의 델리게이트는 종종 해당 객체에 대한 강력한 참조를 보유하기 때문에 루프 유지를 피하기 위해 ( (ARC 이전 됩니다. 예를 들어, 뷰 컨트롤러는 종종 뷰 컨트롤러에 포함 된 뷰의 위임자입니다.

반 대표단 만들기

자신의 델리게이트를 정의하려면 프로토콜 에 관한 Apple Docs에서 설명한대로 어딘가에 메소드를 선언해야 합니다 . 일반적으로 공식 프로토콜을 선언합니다. UIWebView.h에서 해석 된 선언은 다음과 같습니다.

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

UIWebViewDelegate경우 델리게이트에 대한 특수 유형을 작성하므로 인터페이스 또는 추상 기본 클래스와 유사합니다 . 델리게이트 구현자는이 프로토콜을 채택해야합니다.

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

그런 다음 프로토콜에서 메소드를 구현하십시오. 프로토콜에서 @optional(대부분의 델리게이트 메소드와 같이) 선언 된 메소드의 -respondsToSelector:경우 특정 메소드를 호출하기 전에 확인해야 합니다.

명명

위임 메소드는 일반적으로 위임 클래스 이름으로 시작하여 이름이 지정되며 위임 오브젝트를 첫 번째 매개 변수로 사용합니다. 그들은 종종 의지,해야 할 일 또는 행동을 사용합니다. 그래서 webViewDidStartLoad:보다는 (첫 번째 매개 변수는 웹이다) loadStarted(예를 들어, 매개 변수를 고려하지가).

속도 최적화

메시지를 보낼 때마다 대리자가 선택기에 응답하는지 확인하는 대신 대리자가 설정 될 때 해당 정보를 캐시 할 수 있습니다. 이를 수행하는 매우 깨끗한 방법 중 하나는 다음과 같이 비트 필드를 사용하는 것입니다.

@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

그런 다음 본문에서 대리인이 반복 delegateRespondsTo해서 보내지 않고 구조체 에 액세스하여 메시지를 처리하는지 확인할 수 있습니다 -respondsToSelector:.

비공식 대의원

프로토콜이 존재하기 전에, 사용하는 것이 일반적이었다 카테고리 에를 NSObject대리인이 구현할 수있는 방법을 선언 할 수 있습니다. 예를 들어 CALayer여전히이 작업을 수행합니다.

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

이것은 컴파일러가 모든 객체가 구현할 수 있음을 알려줍니다 displayLayer:.

그런 다음 -respondsToSelector:위에서 설명한 것과 동일한 방법 을 사용 하여이 메소드를 호출합니다. 대리인은이 메서드를 구현하고 delegate속성을 할당합니다. 그게 전부입니다 (프로토콜을 준수한다고 선언하지는 않음). 이 방법은 Apple의 라이브러리에서 일반적이지만 새 코드는 위의 최신 프로토콜 접근 방식을 사용해야합니다.이 접근 방식은 오염 NSObject(자동 완성 기능의 유용성을 떨어 뜨림)하고 컴파일러가 오타 및 유사한 오류에 대해 경고하기 어렵 기 때문입니다.


의 반환 값 이 유형이므로 unsigned int유형 을 캐스팅해야한다고 생각합니다 . BOOLdelegate respondsToSelectorBOOL
Roland

C ++에서와 같이 대리를 다형성에 사용할 수 있습니까?

@ 댄 예, 물론입니다. 일반적으로 프로토콜은 다형성에 사용됩니다.
Jesse Rusak

@JesseRusak 일관성을 위해 "JSSomethingDelegate"가 "SomethingDelegate"여야한다고 생각합니다.
Hans Knöchel

382

승인 된 답변은 훌륭하지만 1 분 답변을 찾고 있다면 다음을 시도하십시오.

MyClass.h 파일은 다음과 같아야합니다 (주석이있는 델리게이트 라인 추가!)

#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

MyClass.m 파일은 다음과 같아야합니다

#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

다른 클래스 (이 경우 MyVC라고하는 UIViewController) 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

델리게이트 방법 구현

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

4
이 답변을 빠른 참조로 사용하는 것이 좋습니다. 그러나 왜 MyClass.h의 delegate 속성이 'IBOutlet'으로 표시되어 있습니까?
Arno van der Meer

4
@ArnovanderMeer 잘 잡아라! 이유가 기억 나지 않습니다. 내 프로젝트에는 필요하지만이 예제에서는 필요하지 않으므로 제거했습니다. thx
Tibidabo

감사. 허용되는 답변만큼 훌륭하고 철저하게, 나는 작은 샘플 코드에서 가장 잘 배웁니다. 두 가지 답변을하는 것이 좋습니다.
sudo

@Tibidabo 완전히 대단합니다. 모든 사람들이 이와 같은 프로그래밍 개념을 설명 할 수 있기를 바랍니다. 나는 수년간 '대표'에 대한 수백 가지의 설명을 보았으며 지금 까지이 이론을 실제로 파악한 적이 없습니다! 정말 감사합니다 ...
Charles Robertson

5
myClassMyVC.m 내부에서 인스턴스화 는 어디에 있습니까 ?
Lane Rettig

18

델리게이트 지원을 만들기 위해 공식 프로토콜 방법을 사용할 때 다음과 같은 것을 추가하여 적절한 유형 검사 (컴파일 타임이 아닌 런타임)를 보장 할 수 있음을 발견했습니다.

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

대리자 접근 자 (setDelegate) 코드에서. 이것은 실수를 최소화하는 데 도움이됩니다.


18

부디! iOS에서 델리게이트가 어떻게 작동하는지 이해하려면 단계별 자습서를 아래에서 확인하십시오.

iOS에서 위임

두 개의 ViewController를 만들었습니다 (데이터를 서로 보내기 위해)

  1. FirstViewController 구현 대리자 (데이터 제공).
  2. SecondViewController는 데이터를받을 델리게이트를 선언합니다.

17

어쩌면 이것은 누락 된 내용에 더 가깝습니다.

C ++과 같은 관점에서 오면 대의원은 약간 익숙해 지지만 기본적으로 '그냥 작동합니다.

작동 방식은 NSWindow에 대리자로 작성한 일부 개체를 설정하지만 개체에는 가능한 많은 대리자 메서드 중 하나 또는 몇 가지에 대한 구현 (메서드) 만 있습니다. 그래서 어떤 일이 발생하고 NSWindow객체를 호출하려고합니다-Objective-c를 사용합니다.respondsToSelector 메소드를 사용하여 객체가 해당 메소드를 호출할지 여부를 결정한 다음 호출합니다. 이것은 objective-c의 작동 방식입니다-요청시 방법을 찾아보십시오.

자신의 객체 로이 작업을 수행하는 것은 전적으로 사소한 일입니다. 특별히 진행되는 것은 없습니다. 예를 들어 NSArray27 개의 객체, 모든 다른 종류의 객체, 18 개의 메소드 만 가질 수 있습니다 -(void)setToBue;. 다른 9 개는 그렇지 않습니다. 따라서 setToBlue다음과 같은 18 가지를 모두 호출 하십시오.

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

델리게이트에 대한 또 다른 점은 유지되지 않는다는 것이므로 항상 메소드 nil에서 델리게이트를로 설정 해야 MyClass dealloc합니다.


15

Apple에서 권장하는 모범 사례로서 대리인 (정의상 프로토콜 임)이 프로토콜을 따르는 것이 NSObject좋습니다.

@protocol MyDelegate <NSObject>
    ...
@end

& 델리게이트 내에서 선택적 메소드 (즉, 반드시 구현할 필요는없는 메소드)를 작성하려면 다음 @optional과 같이 주석을 사용할 수 있습니다 .

@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

따라서 선택적으로 지정한 메소드를 사용할 때 (클래스에서) respondsToSelector뷰 (대리자에 맞는)가 실제로 선택적 메소드를 구현했는지 확인 해야합니다.


11

대표단을 이해하면이 모든 답변이 의미가 있다고 생각합니다. 개인적으로 나는 C / C ++의 땅에서 왔으며 Fortran과 같은 절차 언어 이전에 C ++ 패러다임에서 비슷한 아날로그를 찾는 데 2 ​​분이 걸렸습니다.

C ++ / Java 프로그래머에게 델리게이트를 설명한다면

대리인이란 무엇입니까? 다른 클래스 내의 클래스에 대한 정적 포인터입니다. 포인터를 할당하면 해당 클래스에서 함수 / 메소드를 호출 할 수 있습니다. 따라서 클래스의 일부 함수는 다른 클래스에 대한 "위임"됩니다 (C ++ 세계에서-클래스 객체 포인터로 가리키는 포인터).

프로토콜이란 무엇입니까? 개념적으로 대리자 클래스로 할당하는 클래스의 헤더 파일과 비슷한 용도로 사용됩니다. 프로토콜은 포인터가 클래스 내에서 대리자로 설정된 클래스에서 구현해야하는 메서드를 정의하는 명시적인 방법입니다.

C ++에서 비슷한 것을 어떻게 할 수 있습니까? C ++ 에서이 작업을 수행하려고하면 클래스 정의에서 클래스 (객체)에 대한 포인터를 정의한 다음 기본 클래스에 대한 위임으로 추가 기능을 제공하는 다른 클래스에 연결합니다. 그러나이 배선은 코드 내에서 유지되어야하며 서투르고 오류가 발생하기 쉽습니다. 목표 C는 프로그래머가이 규범을 유지하는 데 최선이 아니라고 가정하고 깨끗한 구현을 시행하기 위해 컴파일러 제한을 제공합니다.


당신이 말하는 것은 직관에 대해 이야기하는 동안 의미론입니다. 당신이 말하는 것은 가상 기능입니다. 그러나 새로운 용어에 익숙해지는 것은 어려울 수 있습니다. 이 답변은 C ++ / C의
패러렐

당신이 말하는 것은 정말 분명하지 않습니다. 왜 신선한 답변을 작성하지 말고 더 많은 사람들이 유용하다고 생각하면 투표 할 것입니까?
DrBug

9

스위프트 버전

델리게이트는 다른 클래스에서 일부 작업을 수행하는 클래스입니다. 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!"
}

실제로 다음과 같은 상황에서 대리인이 자주 사용됩니다.

  1. 클래스가 일부 정보를 다른 클래스와 통신해야하는 경우
  2. 클래스가 다른 클래스가 클래스를 사용자 정의하도록 허용하려는 경우

델리게이트 클래스가 필요한 프로토콜을 준수한다는 점을 제외하고 클래스는 미리 알 필요가 없습니다.

다음 두 기사를 읽는 것이 좋습니다. 그들은 문서 보다 델리게이트를 더 잘 이해하도록 도와주었습니다 .


8

좋아, 이것은 실제로 질문에 대한 대답은 아니지만, 자신의 대리인을 만드는 방법을 찾고 있다면 훨씬 간단한 것이 더 나은 대답 일 수 있습니다.

거의 필요하지 않기 때문에 대의원을 거의 구현하지 않습니다. 대리자 개체에 대해 단 하나의 대리자를 가질 수 있습니다. 따라서 단방향 통신 / 통과 데이터에 대한 대리인이 알림보다 훨씬 낫습니다.

NSNotification은 둘 이상의 수신자에게 객체를 전달할 수 있으며 사용이 매우 쉽습니다. 다음과 같이 작동합니다.

MyClass.m 파일은 다음과 같아야합니다

#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

다른 수업에서 알림을 사용하려면 : 수업을 관찰자로 추가합니다.

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

선택기를 구현하십시오.

- (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
}

다음과 같은 경우 클래스를 관찰자로 제거하는 것을 잊지 마십시오.

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

8

개발 한 클래스가 있고 이벤트가 발생할 때 알릴 수 있도록 위임 속성을 선언하려고한다고 가정 해보십시오.

@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

따라서 MyClass헤더 파일 (또는 별도의 헤더 파일)에 프로토콜을 선언하고 대리인이 구현 해야하는 필수 / 선택적 이벤트 핸들러를 선언 한 다음 MyClass유형 ( id< MyClassDelegate>) 의 속성을 선언하십시오. 프로토콜 MyClassDelegate에서 delegate 속성이 weak로 선언 된 것을 알 수 있습니다. 이것은 유지주기를 방지하는 데 매우 중요합니다 (대부분의 경우MyClass 인스턴스를 하므로 델리게이트를 유지로 선언하면 둘 다 서로를 유지합니다) 그들 중 누구도 풀릴 것입니다).

프로토콜 메소드가 MyClass인스턴스를 대리자에게 매개 변수로 전달하는 경우도 있습니다. 대리자가 MyClass인스턴스에서 일부 메서드를 호출하려는 경우 모범 사례이며 MyClassDelegate여러 MyClass인스턴스가있는 경우와 같이 대리자 가 여러 인스턴스에 대해 선언 할 때 도움이됩니다. UITableView's당신의 인스턴스 ViewControllerUITableViewDelegate그들 모두에 자신을 선언 합니다.

그리고 MyClass당신의 내부 에서 다음과 같이 선언 된 이벤트로 대리인에게 알립니다.

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

먼저 델리게이트가 구현하지 않고 프로토콜이 필요한 경우에도 앱이 중단 될 경우를 대비하여 델리게이트가 호출하려는 프로토콜 메소드에 응답하는지 확인합니다.


6

델리게이트를 생성하는 간단한 방법은 다음과 같습니다.

.h 파일에 프로토콜을 작성하십시오. 프로토콜 앞에 @class를 사용하고 UIViewController 이름을 사용하여 정의해야합니다.< As the protocol I am going to use is UIViewController class>.

단계 : 1 : UIViewController 클래스의 서브 클래스가 될 "YourViewController"라는 새 클래스 프로토콜을 작성하고이 클래스를 두 번째 ViewController에 지정하십시오.

2 단계 : "YourViewController"파일로 이동하여 다음과 같이 수정하십시오.

#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

프로토콜 동작에 정의 된 메소드는 프로토콜 정의의 일부로 @optional 및 @required로 제어 할 수 있습니다.

3 단계 : 델리게이트 구현

    #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

// 메소드가 호출되기 전에 메소드가 정의되었는지 테스트

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

5

자신의 대리인을 만들려면 먼저 구현하지 않고 프로토콜을 만들고 필요한 방법을 선언해야합니다. 그런 다음 대리자 또는 대리자 메서드를 구현하려는 헤더 클래스에이 프로토콜을 구현하십시오.

프로토콜은 다음과 같이 선언해야합니다.

@protocol ServiceResponceDelegate <NSObject>

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

@end

일부 작업을 수행해야하는 서비스 클래스입니다. 델리게이트를 정의하는 방법과 델리게이트를 설정하는 방법을 보여줍니다. 작업이 완료된 후 구현 클래스에서 대리자의 메서드가 호출됩니다.

@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

델리게이트를 자체적으로 설정하여 서비스 클래스가 호출되는 기본 뷰 클래스입니다. 또한 프로토콜은 헤더 클래스에서 구현됩니다.

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

- (void) go;

@end

@implementation viewController

//
//some methods
//

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

이제이 클래스에서 대리자 메서드를 구현하면 작업 / 작업이 완료되면 제어가 다시 시작됩니다.


4

면책 조항 : 이것은 Swift을 만드는 방법 의 버전입니다 delegate.

그렇다면 대리인은 무엇입니까? … 소프트웨어 개발에는 주어진 맥락에서 일반적으로 발생하는 문제를 해결하는 데 도움이되는 재사용 가능한 일반적인 솔루션 아키텍처가 있습니다. 이러한 "템플릿"은 디자인 패턴으로 가장 잘 알려져 있습니다. 대리인은 특정 이벤트가 발생할 때 한 개체가 다른 개체에 메시지를 보낼 수 있도록하는 디자인 패턴입니다. 객체 A가 객체 B를 호출하여 동작을 수행한다고 상상해보십시오. 조치가 완료되면 오브젝트 A는 B가 태스크를 완료하고 필요한 조치를 취했음을 알고 있어야합니다. 이는 델리게이트의 도움으로 달성 할 수 있습니다!

더 나은 설명을 위해 간단한 응용 프로그램에서 Swift를 사용하여 클래스간에 데이터를 전달하는 사용자 지정 대리자를 만드는 방법을 보여 드리겠습니다. 이 시작 프로젝트를 다운로드하거나 복제하여 시작하십시오!

두 클래스와 응용 프로그램을 참조 할 수 ViewController AViewController B. B에는 두 가지보기가있어 탭시의 배경색을 변경하는데 ViewController너무 복잡하지 않습니다. 이제 클래스 B의 뷰를 누를 때 클래스 A의 배경색을 변경하는 쉬운 방법을 생각해 봅시다.

문제는이 관점이 클래스 B의 일부이며 클래스 A에 대해 전혀 모른다는 것입니다. 따라서이 두 클래스간에 통신 할 수있는 방법을 찾아야합니다. 구현을 6 단계로 나누었으므로 필요할 때 이것을 치트 시트로 사용할 수 있습니다.

1 단계 : ClassBVC 파일에서 pragma mark 1 단계를 찾아서 추가하십시오.

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

첫 번째 단계는 protocol,이 경우 클래스 B에서 프로토콜을 작성하는 것입니다.이 프로토콜 내에서 구현의 요구 사항에 따라 원하는 수의 함수를 작성할 수 있습니다. 이 경우 옵션 UIColor으로 인수 를 허용하는 간단한 함수가 하나 있습니다. delegate클래스 이름 끝에 단어 를 추가하여 프로토콜 이름을 지정하는 것이 좋습니다 ( 이 경우) ClassBVCDelegate.

2 단계 : pragma mark 2 단계를 찾아서 ClassVBC추가합니다

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

여기서 우리는 클래스의 델리게이트 속성을 생성합니다.이 속성은 protocol유형을 채택 해야하며 선택 사항이어야합니다. 또한 유지주기 및 잠재적 인 메모리 누수를 피하기 위해 속성 앞에 weak 키워드를 추가해야합니다. 이것이 의미하는 바를 모르는 경우이 키워드를 추가하십시오.

3 단계 : handleTap 내부 프라 그마 마크 3 단계를 찾을 method에서 ClassBVC이 추가

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

알아야 할 한 가지, 응용 프로그램을 실행하고 모든보기를 탭하면 새로운 동작이 표시되지 않으며 올바른 것이지만 지적하고자하는 것은 대리인이 호출 될 때 충돌하지 않는 응용 프로그램입니다. 옵션 값으로 생성하기 때문에 위임 된 사람이 존재하지 않아도 충돌하지 않습니다. 이제 ClassAVC파일 로 가서 위임받은 사람을 만들어 봅시다 .

4 단계 : handleTap 메소드에서 pragma mark 4 단계 ClassAVC를 찾아 클래스 유형 옆에 다음과 같이 추가하십시오.

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

이제 ClassAVC가 ClassBVCDelegate프로토콜을 채택 했으므로 컴파일러에서“ClassAVC 유형이 'ClassBVCDelegate'프로토콜을 준수하지 않는다는 오류가 발생한다는 것을 알 수 있습니다. 이는 단지 프로토콜의 방법을 아직 사용하지 않았다는 것을 의미합니다. 클래스 A가 프로토콜을 채택 할 때 프로토콜은 클래스 B와 계약을 체결하는 것과 같으며이 계약은“나를 채택하는 모든 클래스는 반드시 내 기능을 사용해야합니다!”라고 말합니다.

빠른 참고 사항 : Objective-C배경 에서 온 경우 아마도 해당 방법을 선택적으로 만들어 오류를 종료 할 수 있다고 생각하지만 놀랍게도 Swift언어는 선택 사항을 지원하지 않습니다. protocols원한다면 언어 를 만들 수 있습니다. 귀하의 확장 protocol또는 protocol구현에 @objc 키워드를 사용하십시오 .

개인적으로 다른 선택적 방법으로 프로토콜을 만들어야하는 경우 다른 것으로 나누는 것을 선호합니다. protocols그러면 객체에 하나의 단일 책임을 부여한다는 개념을 따르지 만 특정 구현에 따라 다를 수 있습니다.

다음은 선택적 메소드에 대한 좋은 기사입니다.

5 단계 : segue 준비 방법 내에서 pragma 마크 5 단계를 찾아서 추가합니다.

//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
}

여기서 우리는 단지 인스턴스를 생성 ClassBVC하고 델리게이트를 self에게 할당하지만 여기서 self는 무엇입니까? 자아는 ClassAVC위임 된 것입니다!

6 단계 : 마지막으로 6 단계에서 pragma 6 단계를 찾고 ClassAVC의 기능을 사용하고 protocolfunc changeBackgroundColor 입력을 시작 하면 자동 완성이라는 것을 알 수 있습니다. 내부에 구현을 추가 할 수 있습니다.이 예제에서는 배경색을 변경하고 추가합니다.

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

이제 앱을 실행하십시오!

Delegatestableview과거에 위임을 사용한 과거, UIKIT주위에 많은 작업 클래스 및 기타 많은 클래스 를 작성하면 frameworks이러한 주요 문제를 해결할 수 있습니다.

  • 물체를 단단히 연결하지 마십시오.
  • 객체를 서브 클래 싱 할 필요없이 동작과 모양을 수정합니다.
  • 임의의 개체에 대해 작업을 처리 할 수 ​​있습니다.

축하합니다, 당신은 단지 커스텀 델리게이트를 구현합니다. 아마도 당신이 아마도 그렇게 생각하고 있다는 것을 알고 있습니까? 글쎄, 위임은 iOS개발자 가되고 싶을 때 이해해야 할 매우 중요한 디자인 패턴 이며, 객체간에 일대일 관계가 있다는 것을 항상 명심하십시오.

여기 에서 원래 튜토리얼을 볼 수 있습니다


4

답변은 실제로 답변되지만 델리게이트 생성을위한 "치트 시트"를 제공하고 싶습니다.

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

ViewController.h

@protocol NameDelegate <NSObject>

-(void)delegateMEthod: (ArgType) arg;

@end

@property id <NameDelegate> delegate;

ViewController.m

[self.delegate delegateMEthod: argument];

MainViewController.m

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

방법:

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

2

내 관점에서 해당 대리자 메서드에 대해 별도의 클래스를 만들고 원하는 곳에서 사용할 수 있습니다.

내 사용자 정의 DropDownClass.h에서

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;

그 후 in.m 파일은 객체로 배열을 만듭니다.

 - (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];
        }
    }
}];
}

여기에 모두 사용자 지정 대리자 클래스가 설정되어 있습니다.이 후 원하는 위치 에서이 대리자 메서드를 사용할 수 있습니다.

그 후 내 다른 viewcontroller 가져 오기에서

이와 같은 대리자 메소드 호출을위한 조치 작성

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

다음과 같은 호출 위임 메소드 후에

- (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

위임 :-만들기

@protocol addToCartDelegate <NSObject>

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

@end

당신이 데이터를 전송하는 것을 볼 수 있도록 대리인을 보내 주시기 바랍니다

[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. .m 클래스에서 메소드를 구현하십시오.-(void) didRemoveCellWithTag : (NSInteger) tag {NSLog @ ( "Tag % d", tag);

}


0

예를 들어, 우리가 온라인으로 제품을 구매하는 경우, 다른 팀이 처리하는 배송 / 배송과 같은 프로세스를 거칩니다. 따라서 배송이 완료되면 배송 팀이 배송 팀에 통보해야 하며이 정보를 방송하는 일대일 커뮤니케이션이어야합니다. 다른 사람 / 공급 업체에게는 오버 헤드가 발생합니다.이 정보를 필요한 사람에게만 전달하려고 할 수 있습니다.

따라서 앱의 관점에서 생각하면 이벤트는 온라인 주문이 될 수 있고 다른 팀은 여러보기와 같을 수 있습니다.

다음은 ShippingView를 Shipping 팀으로, DeliveryView를 Delivery 팀으로 간주하는 코드입니다.

//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
        //
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.