이번에 소개할 패턴은 Block패턴이다 애플 레퍼런스에서 소해주지는 않은 방법이고 혼자 개발을 하면서 애플에서 추천하는 Block 문법과 KeyValue 코딩을 반반 섞은 방법이다.
처음 이 패턴을 고민한건 MVC패턴을 통해 작업을 진행하고 있었다. 그러다 뷰컨트롤러 안에서 커스텀 뷰를 만들어가지고 작업을 진행하는데 뷰 안에 UI처리가 아닌 데이터 처리등이 섞이는게 싫어서 어떤 방법을 써볼까 고민하다가 뷰 안에 블록을 통해 객체를 정하고 컨트롤러단에서 뷰의 액션을 받아 처리하면 어떨까! 하다 만들게 되었다. 지금은 커스텀 뷰에서 받아오는 이벤트등은 대부분은 이 패턴을 통해 컨트롤러에서 처리하고 있다.
1 2 3 4 5 6 7 8 |
// TPBlockViewController.h typedef void (^TPBlockAction)(void); @interface TPBlockViewController : UIViewController{ TPBlockAction clooseAction; TPBlockAction changeAction; } @end |
이벤트를 처리할 하위 객체에서는 딱히 할게 없다. 액션이 저장될 블록문을 저장해둘 객체만 선언해주면 된다. 굳이 typedef를 사용안해도 되지만 해주는 편이 소스가 깔끔해져서 개인적으로 선호한다.
1 2 3 4 5 6 7 8 9 |
- (IBAction)closeBlockViewButtonAction:(id)sender { //블록문을 호출한다. clooseAction(); }- (IBAction)changeBlockButtonAction:(id)sender { //블록문을 호출한다. 여기서 블록문이 셋팅되있지 않으면 참조 에러로 앱이 강제로 종료가 될 수 있다. if문을 통해 좀 더 안전하게 코딩 할 수 있다. if (changeAction) { changeAction(); } } |
m 파일에서는 액션에 맞춰 블록문을 호출하기만 하면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
- (IBAction)addBlockButtonAction:(id)sender { blockViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"TPBlockViewController"]; blockViewController.view.frame = CGRectMake(20, 140, 300, 500); blockViewController.view.backgroundColor = [self randomColor]; [self.view addSubview:blockViewController.view]; [blockViewController setValue:^{ NSLog(@"닫기 액션입니다"); [blockViewController.view removeFromSuperview]; } forKey:@"clooseAction"]; [blockViewController setValue:^{ NSLog(@"색상변경 액션입니다"); textLabel.backgroundColor = [self randomColor]; } forKey:@"changeAction"]; } |
상위 객체에서는 하위객체를 생성하면서 블록문에 해당 이벤트를 지정해두면 해당 블록문을 하위객체에서 부를때마다 실행된다. 블록문을 설정할때는 KeyValue문법을 사용하는 방법을 사용하는데 [Object setValue:블록문 forKey:블록문 변수이름] 의 문법으로 작성한다. 이와같은 방법을 사용하면 델리게이트보다 좀더 적은 작업양으로 델리게이트와 비슷한 느슨함을 유지한 코딩을 할 수 있어서 나는 즐겨 사용하고 있다.
하지만 마냥 좋은것만은 아니다. 블록문을 사용하면서 순환참조 경고가 보일것이다. 경고를 해결하기 위해서는 그 안에 호출하는 변수들을 __weak타입으로 지정해줘야한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
- (IBAction)addBlockButtonAction:(id)sender { blockViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"TPBlockViewController"]; blockViewController.view.frame = CGRectMake(20, 140, 300, 500); blockViewController.view.backgroundColor = [self randomColor]; [self.view addSubview:blockViewController.view]; __weak UILabel *weakTextLabel = textLabel; __weak UIViewController *weakBlockVC = blockViewController; __weak typeof(self) weakSelf = self; [blockViewController setValue:^{ NSLog(@"닫기 액션입니다"); [weakBlockVC.view removeFromSuperview]; } forKey:@"clooseAction"]; [blockViewController setValue:^{ NSLog(@"색상변경 액션입니다"); weakTextLabel.backgroundColor = [weakSelf randomColor]; } forKey:@"changeAction"]; } |
경고가 있더라도 개발자가 잘 컨트롤을 하면 에러가 안나게 할 수 있지만 그래도 기분상 경고는 하나라도 없는게 더 좋지 않은가? 경고가 뜨는 객체들을 __weak타입으로 다시 선언하여 weakObject 형식으로 만들어서 블록문 안에서 사용하면 경고가 사라지는것을 확인 할 수 있다.
델리게이트나 노티피케이션을 사용한 패턴은 애플 레퍼런스에서도 잘 나와있지만 블록문을 이용한 예제는 많이 안보였다. AFNetworking에서 결과문을 블록문을 통해 처리하는 부분을 보고 참 깔끔하고 좋다고 생각하여 이런식으로 사용하다 보니 이거도 나름 괜찮구나….싶어서 자주 사용하고 있다. 무었보다 델리게이트나 노티피케이션은 따로 처리하는 메소드를 다른곳에 만들어야 하는데 블록문으로 처리할면 굳이 화면을 다른곳으로 옴기지 않고 처리 할 수 있는 점도 강점인거 같다. 물론 작업자만 편한거 같기는 하다. 🙂