클래스업(ClassUp)에서 텍스트 뷰의 커스텀한 자동완성 형식을 구현하던 중 오랜만에 DelegateProtocol에 대한 고찰(?)아닌 고찰을 해보았다.

여기서는 Delegate에 대해서만 조금 설명해 보려고 한다.

일반적으로 Delegate는 아래와 같이 사용했다.


// Custom.h

#import <UIKit/UIKit.h>

@protocol CustomDelegate <NSObject>

@required

// method
- (void)myRequiredDelegateMethod;

@optional

// method
- (void)myOptionalDelegateMethod;

@end

@interface Custom : NSObject

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

@end

// Custom.m
#import "Custom.h"

@implementation Custom

- (void)doRequiredSomething {
  [self.delegate myRequiredDelegateMethod];
}

- (void)doOptionalSomething {
  [self.delegate myRequiredDelegateMethod];
}

@end

// UseCustomDelegate.m

@implementation UseCustomDelegate

- (void)myRequiredDelegateMethod {
  // Do something (for Custom)
}


- (void)myOptionalDelegateMethod {
  // Do something (for Custom)
}

@end

위와 같은 예는 보통 1:1 관계에서의 일이다. 즉, ViewControllerUITableViewDelegate를 선언할 때와 비슷한 경우이다.

하지만, 만약 하나의 Delegate가 여러 클래스가 유기적을 연동되어야 할 경우는 어떻게 해야할까?

즉, 한 인스턴스의 수행이 그 인스턴스를 지니고 있는 인스턴스, 그리고 그 지닌 인스턴스를 지닌 인스턴스에 순서적으로 영향을 주고 싶은 경우이다. 말로 표현하니 이상해 지는데 아래와 같이…

UITextView의 변화를 UITextView를 감싸고 있는 UIView에 그리고 그 UIView는 UIViewController에 영향을 주는 식이다.

이 경우에는 아래와 같이 표현할 수 있다..

// CustomDelegate.h

#import <UIKit/UIKit.h>

@protocol CustomDelegate <NSObject>

@required

// method
- (void)myRequiredDelegateMethod;

@optional

// method
- (void)myOptionalDelegateMethod;

@end

먼저 Delegate를 만들고 수행되어야 하는 메소드들을 생성한다.

// CustomThird.h

#import <UIKit/UIKit.h>

@interface CustomThird : NSObject

@property (nonatomic, weak) id<CustomDelegate> parent;

@end

// CustomThird.m
#import "CustomThird.h"

@implementation CustomThird

- (void)doRequireSomethinigForParent {
  // CustomSecond의 myRequiredDelegateMethod 호출
  [self.parent myRequiredDelegateMethod];
}

- (void)doOptionalSomethingForParent {
  // CustomSecond의 myOptionalDelegateMethod 호출
  [self.parent myRequiredDelegateMethod];
}

@end

CustomThird에서 수행된 함수에서는 CustomThird와 연결된 인스턴스인 CustomSecond의 Delegate메소드를 호출한다.

// CustomSecond.h

#import <UIKit/UIKit.h>

@interface CustomSecond : NSObject <CustomDelegate>

@property (nonatomic, weak) id<CustomDelegate> parent;

@end

// CustomSecond.m
#import "CustomSecond.h"

@implementation CustomSecond

- (id)init {
  self = [super init];

  if(self) {
    CustomThird *third = [[CustomThird init] alloc];
    third.parent = self;
  }

  return self;
}

- (void)myRequiredDelegateMethod {
  // CustomFirst의 myOptionalDelegateMethod 호출
  [self.parent myRequiredDelegateMethod];
}

- (void)myRequiredDelegateMethod {
  // CustomFirst의 myOptionalDelegateMethod 호출
  [self.parent myRequiredDelegateMethod];
}

@end

이렇게 되면 CustomThird에서 CustomSecond의 Delegate메소드로 오게되고, 여기서 CustomSecond는 해야할 수행을 한 후에 다시 CustomFirst의 Delegate메소드를 호출한다.

// CustomFirst.h

#import <UIKit/UIKit.h>

@interface CustomFirst : NSObject <CustomDelegate>

@end

// CustomFirst.m
#import "CustomFirst.h"

@implementation CustomFirst

- (id)init {
  self = [super init];

  if(self) {
    CustomSecond *second = [CustomSecond alloc] init];
    second.parent = self;
  }

  return self;
}

- (void)myRequiredDelegateMethod {
  // Do somthing.
}

- (void)myRequiredDelegateMethod {
  // Do somthing.
}

@end

이렇게 되면 CustomFirst의 인스턴스는 CustomThird에서의 변화를 감지할 수 있게된다.

당연히, 인스턴스 자체를 넘겨주는 것도 가능하지만 이 경우에는 memory관련 문제가 일어날 수 있으므로 최대한 메소드만으로 처리가 가능할 경우에는 Delegate를 이용하는 것이 좋은 것 같다.