ReactiveCocoa 입문 지도

9463 단어 ReactiveCocoaios
원본 주소:http://www.teehanlax.com/blog/getting-started-with-reactivecocoa/
       이전 글 에서 ReactiveCocoa 개념 을 소 개 했 는데 ReactiveCocoa 는 Objective - C 에서 성명 식 프로 그래 밍 에 사용 되 는 라 이브 러 리 입 니 다.다음은 리 액 티 브 코코아 의 모델 을 소개 하고 가장 좋 은 실천 을 토론 하 며 흔히 볼 수 있 는 함정 을 지적 할 것 이다.Reactive Cocoa 의 공 부 는 시간 이 필요 합 니 다. 천천히 합 시다.
패턴
      ReactiveCocoa 에는 세 가지 기본 적 인 모델 이 있 는데 그것 이 바로 책임 체인, 분할 과 조합 모델 (chaining, splitting, and combining) 이다.이전 글 에 서 는 책임 체인 과 조합 모델 을 조금 소 개 했 고 그 다음은 더욱 깊이 있 을 것 이다.
        돌 이 켜 보면 ReactiveCocoa 의 핵심 은 signal: (신호) 로 끊임없이 변화 하 는 상 태 를 나타 낸다.우리 가 chain, split, combine 을 사용 할 때, 실제로 우 리 는 이 signal 을 조작 하고 있 습 니 다.
       Chaining 은 ReactiveCocoa 에서 가장 자주 사용 하 는 모드 입 니 다. 기 존의 signal 을 새로운 signal 로 변환 합 니 다.자주 사용 하 는 동작 은 새로운 signal 을 만 들 고 filter:, map: 또는 startWith: 등 방법 을 사용 하 는 것 입 니 다.예:
RAC(self.textField.text) = [[[RACSignal interval:1] startWith:[NSDate date]] map:^id(NSDate *value) {
    NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:value];
    
    return [NSString stringWithFormat:@"%d:%02d", dateComponents.minute, dateComponents.second];
}];

       이 예 에서, 우 리 는 textFiled 의 text 속성 을 세 개의 연 결 된 signals 의 결과 로 연결 합 니 다.우선, 우 리 는 간격 신 호 를 만 듭 니 다. 이 신 호 는 1 초 마다 현재 시간 을 보 냅 니 다.간격 신 호 는 시작 되 지 않 았 을 때 값 이 없 기 때문에 startWith: 를 사용 하여 시작 합 니 다.마지막 으로 map: signal 의 NSDate 값 을 NSString 문자열 로 변환 합 니 다. 이 문자열 은 textField 의 text 속성 에 대 입 됩 니 다.
        Chaining 은 가장 자주 사용 하 는 작업 이 고 국부 변 수 를 사용 하지 않 고 위 와 같이 연결 되 어 작 동 합 니 다.아래 의 코드 는 위의 코드 와 같다.
RACSignal *intervalSignal = [RACSignal interval:1];
RACSignal *startedIntervalSignal = [intervalSignal startWith:[NSDate date]];
RACSignal *mappedIntervalSignal = [startedIntervalSignal map:^id(NSDate *value) {
    NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:value];
    
    return [NSString stringWithFormat:@"%d:%02d", dateComponents.minute, dateComponents.second];
}];
 
RAC(self.textField.text) = mappedIntervalSignal;

       Splitting 은 chaining 과 유사 하 며, signal 을 다른 sginal 로 변환 하 는 것 입 니 다. 다른 점 은 Splitting 은 중간 signals 를 반복 적 으로 사용 합 니 다.Splitting 은 좀 복잡 해 보이 지만 사실은 하나의 signals 가 여러 번 사 용 했 을 뿐이다.예:
RACSignal *dateComponentsSignal = [[[RACSignal interval:1] startWith:[NSDate date]] map:^id(NSDate *value) {
    NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:value];
    return dateComponents;
}];
 
RAC(self.minuteTextField.text) = [dateComponentsSignal map:^id(NSDateComponents *dateComponents) {
    return [NSString stringWithFormat:@"%d", dateComponents.minute];
}];
 
RAC(self.secondTextField.text) = [dateComponentsSignal map:^id(NSDateComponents *dateComponents) {
    return [NSString stringWithFormat:@"%d", dateComponents.second];
}];

        위의 예 에서 직렬 signal, 즉 부분 변수: dateComponentssignal 을 만 들 었 습 니 다.이 어 dateComponentsSignal 으로 두 개의 새로운 signal 을 만 들 고 각각 두 개의 textfield 의 text 속성 과 연결 합 니 다.
       세 번 째 상용 모델 은 combining 이다.combining 은 몇 개의 signal 을 결합 하여 새로운 signal 을 만 드 는 것 입 니 다.예 를 들 어 '로그 인' 단 추 는 '사용자 이름' 과 '비밀번호' 입력 상자 의 텍스트 길이 가 6 을 초과 할 때 만 클릭 할 수 있 습 니 다. 그렇지 않 으 면 사용 할 수 없 는 상태 입 니 다.그러면 우 리 는 '로그 인' 단추 의 enabled 상태 에 signal 을 만 들 수 있 습 니 다. 이 signal 은 '사용자 이름' 과 '비밀번호' 상자 두 개의 signal 을 조합 합 니 다.
RAC(self.submitButton.enabled) = [RACSignal combineLatest:@[self.usernameField.rac_textSignal, self.passwordField.rac_textSignal] reduce:^id(NSString *userName, NSString *password) {
    return @(userName.length >= 6 && password.length >= 6);
}];

         여기 서 "로그 인" 단추 의 enable 상 태 를 combineLatest: reduce: 방법 으로 만 든 signal 에 연결 합 니 다.이 방법의 두 번 째 매개 변 수 는 block 입 니 다. 이 block 의 매개 변 수 는 combineLatest 의 매개 변수의 최신 값 조합 입 니 다.저 희 는 두 텍스트 상자 의 text signal 을 combineLatest 에 함께 전달 합 니 다. reduce 의 block 에서 이 block 은 두 개의 NSString 인 자 를 받 습 니 다. 이 block 의 작업 은 두 개의 매개 변수 값 을 조합 하여 하나의 값 을 만 들 고 돌아 오 는 것 입 니 다.이 방법의 설명:
// +combineLatest:reduce: takes an array of signals, executes the block with the
// latest value from each signal whenever any of them changes, and returns a new
// RACSignal that sends the return value of that block as values.
Combining 은 항상 두 가지 상황 에 사 용 됩 니 다.
1. 여러 가지 조건 을 동시에 만족 시 켜 야 합 니 다.
2. 여러 signal 에서 선택 합 니 다.
       이러한 선형 논리 (linear flow of logic) 의 사고 에 중점 을 두 고 이 signals 를 어떻게 직렬, 분할 또는 조합 하 느 냐 에 있다.이런 기본 적 인 조작 을 보면 너 로 하여 금 이 모델 들 에 대해 더욱 익숙 하 게 할 수 있다.
최선 의 실천
        우 리 는 이미 Reactive Cocoa 모델 의 기본 지식 을 소 개 했 으 니, 다음은 최고의 실천 을 살 펴 보 자.
        ReactiveCocoa 는 상 태 를 제거 함으로써 프로그램 을 쉽게 쓸 수 있 습 니 다.그러나 '반응 완료 (completely reactive)' 식 응용 프로그램 에서 도 우 리 는 table view 와 같은 delegate 방법 과 같은 비 Reactive Cocoa 코드 를 써 야 한다.RACSubjects 는 비 reactive 와 reactive 코드 의 교량 역할 을 했다.
        RACSubject 는 새 값 을 수 동 으로 보 낼 수 있 는 signal 입 니 다.예 를 들 어 gesture recognizers 는 ReactiveCocoa 의 일부분 이 아 닙 니 다. 이때 우 리 는 두 개의 RACSubject 속성 을 사용 할 수 있 습 니 다. 하 나 는 gesture recognizer: 사건 을 받 아들 이 는 데 사 용 됩 니 다. 이 recognizer 가 처리 하고 있 는 지 여 부 를 표시 합 니 다.다른 하 나 는 현재 위 치 를 기록 하 는 데 쓰 인 다.
self.gestureRecognizerIsRunningSubject = [RACSubject subject];
self.gestureRecognizerValueSubject = [RACSubject subject];
 
RAC(self.someView.frame) = [self.gestureRecognizerValueSubject map:^id(NSValue *value) {
   CGPoint location = [value CGPointValue];
 
   CGFloat size = 100.0f;
 
   return [NSValue valueWithCGRect:CGRectMake(location.x - size/2.0f, location.y - size/2.0f, size, size)];
}];

      우 리 는 하나의 view 를 gesture recognizer 의 마지막 위치 중심 에 놓 을 것 이다.
      이 subjects 이 벤트 를 보 내 는 것 은 매우 간단 합 니 다. gesture recognizer 방법 을 간단하게 실현 하면 됩 니 다. (주: 이 방법 은 에이전트 방법 - gesture Recognizer: shouldReceiveTouch: 에서 호출 할 수 있 습 니 다)
-(void)gestureRecognizerReceivedTouch:(UIPanGestureRecognizer *)recognizer {
   if (recognizer.state == UIGestureRecognizerStateBegan) {
       [self.gestureRecognizerIsRunningSubject sendNext:@(YES)];
   }
 
   else if (recognizer.state == UIGestureRecognizerStateChanged) {
       [self.gestureRecognizerValueSubject sendNext:[NSValue valueWithCGPoint:[recognizer locationInView:self.view]]];
   }
 
   else if (recognizer.state == UIGestureRecognizerStateEnded) {
       [self.gestureRecognizerIsRunningSubject sendNext:@(NO)];
   }
}

        RACSubjects 는 비 reactive 코드 와 Reactive Cocoa 코드 의 교량 이지 만 지나치게 남용 하 는 것 도 위험 하 다.우리 가 chaining signals 를 통 해 임 무 를 완성 할 수 있다 면 RACSubjects 의 값 에 의존 하지 마 세 요.
        ReactiveCocoa 의 디자인 은 프로그램 이 가능 한 한 여러 가지 상 태 를 줄 이 는 것 이다. 이러한 상 태 를 줄 이 는 논 리 는 부작용 (perform side - effects) 을 적 게 사용 해 야 한다.예 를 들 어 위의 코드 를 바탕 으로 저 희 는 table view 의 제스처 이벤트 가 완 료 될 때 슬라이더 를 반 짝 여 사용자 에 게 어디 까지 미 끄 러 졌 는 지 알려 주 고 싶 습 니 다.우 리 는 subscription 을 호출 할 수 있 습 니 다.
[[self.gestureRecognizerIsRunningSubject filter:^BOOL(NSNumber *gestureRecognizerIsRunning) {
   return !(gestureRecognizerIsRunning.boolValue);
}] subscribeNext:^(id x) {
   [self.tableView flashScrollIndicators];
}];

        여기 서 우 리 는 제스처 가 실행 중인 이 벤트 를 걸 러 내 고 미끄럼 이 완 료 될 때 만 subcribe 를 걸 러 냅 니 다.이때 subscribe next: bloc 에서 부작용 을 실 행 했 습 니 다.
        subscriptions 는 유용 하고 부작용 을 수행 하 는 것 도 필요 하지만 과도 한 사용 은 조심해 야 한다.그것들 이 바로 가 변 적 인 변수, 상태 이다. 이것 이 바로 ReactiveCocoa 가 피 하 는 것 이다.바 인 딩 속성 을 통 해 signals 를 매 핑 하여 작업 을 수행 할 수 있 을 때 RACSubjects 를 사용 하지 마 십시오.
함정.
       어떤 새로운 것 이 든 초보 자 에 게 는 함정 이 있 을 것 이다.예 를 들 어 아래 코드 는 하나의 속성 에서 새로운 signal 을 만 들 때 somestring 의 값 이 바 뀌 기 전에 아무것도 일어나 지 않 습 니 다.
RAC(self.label.text) = RACAble(self.someString);

        someSting , RACAbleWithStart。 “starts” signal someString 。

RAC(self.label.text) = RACAbleWithStart(self.someString);

        이와 유사 합 니 다. interval: 주기 타 이 머 를 설정 할 때 이 타 이 머 는 이 첫 번 째 interval 로 바로 사용 되 지 않 습 니 다. NSTimer 를 사용 하 는 것 같 습 니 다.첫 번 째 예 기억 나 시 죠?저 희 는 text field 의 text 값 을 현재 시간 과 연결 합 니 다. 저 희 는 startWith 를 수 동 으로 사용 하고 현재 시간 을 전송 하여 열 었 습 니 다.만약 우리 가 이렇게 하지 않 는 다 면, text field 는 첫 번 째 간격 1 초 전에 비어 있 습 니 다.
        interval: 또 하나의 중점 이 있 을 때 이 방법 은 그 결 과 를 높 은 최적화 등급 의 스케줄 링 에 전달 합 니 다 (GCD 대기 열 과 유사).즉, 이전 코드 는 실제로 1 초 동안 BUG 가 있 었 다 는 것 이다. UI 를 업데이트 하 는 코드 를 직접 실행 할 수 없다.interval: signal 의 결 과 를 주 스 레 드 스케줄 에 전달 할 수 있 습 니 다.
RAC(self.textField.text) = [[[[RACSignal interval:1] startWith:[NSDate date]] map:^id(NSDate *value) {
   NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:(NSMinuteCalendarUnit|NSSecondCalendarUnit) fromDate:value];
 
   return [NSString stringWithFormat:@"%d:%02d", dateComponents.minute, dateComponents.second];
}] deliverOn:[RACScheduler mainThreadScheduler]];

       그 랬 으 면 좋 겠 다.
       이 글 은 자주 사용 하 는 모델 을 소개 하 였 으 며, 가장 좋 은 실천 에는 또 약간의 함정 이 있다.ReactiveCocoa 를 이용 해 성명 식 애플 리 케 이 션 을 구축 하 는 데 도움 이 되 기 를 바 랍 니 다.

좋은 웹페이지 즐겨찾기