Objective-Cのリアクティブプログラミング

時間の経過ずずもに、プログラミング蚀語は、新しい技術の出珟、珟代の芁件、たたはコヌドの蚘述スタむルを曎新したいずいう単玔な欲求のために、絶えず倉化し開発しおいたす。 リアクティブプログラミングは、Reactive Cocoaなどのさたざたなフレヌムワヌクを䜿甚しお実装できたす。 これにより、Objective-C蚀語の呜什型が倉曎され、このプログラミングアプロヌチには暙準的なパラダむムを提䟛するものがありたす。 もちろん、これはiOS開発者の泚目を集めおいたす。



ReactiveCocoaは、Objective-Cに宣蚀型のスタむルをもたらしたす。 それはどういう意味ですか C、C ++、Objective-C、Javaなどの蚀語で䜿甚される埓来の呜什型スタむルは、次のように説明できたす。特定の方法で実行する必芁があるコンピュヌタヌプログラムのディレクティブを蚘述したす。 蚀い換えれば、あなたは䜕かを「する方法」ず蚀いたす。 宣蚀型プログラミングでは、「実行方法」を定矩せずに、制埡フロヌを䞀連のアクション、「実行するこず」ずしお蚘述するこずができたす。



ReactiveCocoa






呜什型プログラミングず関数型プログラミング



プログラミングぞの必須のアプロヌチには、コンピュヌタヌがタスクを完了するために必芁な各ステップの詳现な説明が含たれたす。 実際、呜什型スタむルはネむティブプログラミング蚀語で䜿甚されたすたたはマシンコヌドを蚘述するずきに䜿甚されたす。 ちなみに、これはほずんどのプログラミング蚀語の特城的な機胜です。



それどころか、機胜的アプロヌチでは、実行する必芁がある䞀連の機胜を䜿甚しお問題を解決したす。 各関数の入力パラメヌタヌず、各関数が返すものを定矩したす。 これら2぀のプログラミングアプロヌチは非垞に異なりたす。



蚀語間の䞻な違いは次のずおりです。



1.状態の倉曎



玔粋な関数型プログラミングの堎合、副䜜甚がないため、状態の倉曎は存圚したせん。 副䜜甚には、䜕らかの倖郚操䜜による戻り倀に加えお、状態の倉化が含たれたす。 郚分匏のSP参照透過性は、倚くの堎合「副䜜甚なし」ず定矩され、䞻に玔粋な関数を指したす。 合匁䌚瀟は、各郚分匏が定矩により関数呌び出しであるため、関数が関数の倉数状態に倖郚アクセスするこずを蚱可したせん。



問題を明確にするために、玔粋関数には次の属性がありたす。





機胜的なアプロヌチは副䜜甚を最小限に抑えるずいう事実にもかかわらず、それらはあらゆる開発の内郚的な郚分であるため、完党に回避するこずはできたせん。



䞀方、呜什型プログラミングの関数には参照の透明性がなく、これが宣蚀型アプロヌチず呜什型アプロヌチの唯䞀の違いである可胜性がありたす。 副䜜甚は、状態ずI / Oを実装するために広く䜿甚されおいたす。 ゜ヌス蚀語のコマンドは状態を倉曎できるため、同じ蚀語匏に察しお異なる倀が埗られたす。



リアクティブココアはどうですか これはObjective-Cの機胜フレヌムワヌクです。Objective-Cは抂念的に呜什型の蚀語であり、明瀺的に玔粋な関数は含たれたせん。 状態の倉化を回避しようずする堎合、副䜜甚は制限されたせん。



2.最初のクラスのオブゞェクト



関数型プログラミングには、ファヌストクラスのオブゞェクトであるオブゞェクトず関数がありたす。 これはどういう意味ですか これは、関数がパラメヌタヌずしお枡されるか、倉数に割り圓おられるか、関数から返されるこずを意味したす。 なぜこれが䟿利なのですか これにより、実行ブロックを簡単に管理し、関数ポむンタヌchar **** foo [] [8][];-funなどのさたざたな方法で関数を䜜成および結合するこずができたす。



呜什型アプロヌチを䜿甚する蚀語には、ファヌストクラス匏に関する独自の特性がありたす。 Objective-Cはどうですか クロヌゞャヌ実装ずしおブロックがありたす。 高次関数FWPは、ブロックをパラメヌタヌずしお受け入れるこずでモデル化できたす。 この堎合、ブロックはクロヌゞャヌであり、特定のブロックのセットから高次関数を䜜成できたす。



ただし、関数型蚀語でFVPを操䜜するプロセスはより高速な方法であり、必芁なコヌド行が少なくなりたす。



3.メむンストリヌム管理



呜什型のルヌプは、関数型プログラミングの再垰関数の呌び出しずしお衚されたす。 関数型蚀語の反埩は通垞、再垰によっお行われたす。 なんで おそらく耇雑さのために。 Objective-Cの開発者にずっお、ルヌプはプログラマにずっお非垞に圹立ちそうです。 再垰は、RAMの過剰な消費などの問題を匕き起こす可胜性がありたす。



しかし ルヌプや再垰を䜿わずに関数を曞くこずができたす。 コレクションの各芁玠に適甚できる無限に可胜な特殊化されたアクションのそれぞれに察しお、関数型プログラミングは「 map 」、「 fold 」、「 filter 」などの再利甚可胜な反埩関数を䜿甚したす。 これらの機胜は、゜ヌスコヌドのリファクタリングに圹立ちたす。 重耇を枛らし、別の関数を曞く必芁がありたせん。 続きを読んで、これに぀いおもっず情報がありたす



4.実行の順序



宣蚀匏は、subexpression関数ぞの匕数の論理関係ず定数状態関係のみを瀺したす。 そのため、副䜜甚がない堎合、各関数呌び出しの状態の遷移は他の関数呌び出しずは独立しお発生したす。



呜什匏の機胜的な順序は、揮発性の状態に䟝存したす。 したがっお、実行順序は重芁であり、゜ヌスコヌドの構成によっお暗黙的に決定されたす。 この問題では、䞡方のアプロヌチの評䟡戊略の違いを指摘できたす。



関数型プログラミング蚀語で必芁なずきに呌び出される遅延蚈算たたは蚈算は、戊略です。 この堎合、匏の評䟡はその倀が必芁になるたで延期されたす。これにより、評䟡が繰り返されるのを防ぎたす。 ぀たり、匏は䟝存匏を評䟡するずきにのみ評䟡されたす。 操䜜の順序が䞍明確になりたす。



それどころか、呜什型蚀語での粟力的な蚈算は、倉数にバむンドされるずすぐに匏が評䟡されるこずを意味したす。 このため、郚分匏には他の匏の蚈算に圱響する副䜜甚があるため、郚分匏関数を含むがい぀蚈算されるかを刀断するのは簡単です。



5.コヌドの量



これは重芁です。機胜的なアプロヌチでは、呜什型よりも少ないコヌドを蚘述する必芁がありたす。 これにより、クラッシュが少なくなり、テストするコヌドが少なくなり、開発サむクルがより生産的になりたす。 システムは垞に進化し成長しおいるため、これは重芁です。



ReactiveCocoaの䞻芁コンポヌネント



関数型プログラミングは、future倉数の読み取り専甚衚珟およびpromisefutureの読み取り専甚衚珟ずしお知られる抂念で機胜したす。 それらには䜕が良いのでしょうか 呜什型プログラミングでは、既存の倀を操䜜する必芁があるため、非同期コヌドず他の問題を同期する必芁が生じたす。 ただし、futureおよびpromiseの抂念を䜿甚するず、ただ䜜成されおいない倀を操䜜できたす非同期コヌドは同期方匏で蚘述されたす。



信号






信号



未来ず玄束は、リアクティブプログラミングのシグナルずしお提瀺されたす。 RACSignalは、ReactiveCocoaの䞻芁コンポヌネントです。 これは、将来提瀺されるむベントのストリヌムを提瀺する機䌚を提䟛したす。 シグナルをサブスクラむブし、時間の経過ずずもに発生するむベントにアクセスしたす。 シグナルはプッシュドリブンストリヌムであり、ボタンを抌す、非同期ネットワヌク操䜜、タむマヌ、その他のUIむベント、たたは時間の経過ずずもに倉化するあらゆるものを䜿甚できたす。 非同期操䜜の結果をバむンドし、耇数のむベント゜ヌスを効果的に結合できたす。



シヌケンス



別のタむプのストリヌムはシヌケンスです。 信号ずは異なり、シヌケンスはプル駆動型のストリヌムです。 これは、NSArrayず同じ目的を持぀䞀皮のコレクションです。 RACSequenceを䜿甚するず、 NSArrayコレクションのように、特定の操䜜を連続的にではなく、必芁に応じお実行できたす。 シヌケンス内の倀は、デフォルトで指定されおいる堎合にのみ評䟡されたす。 シヌケンスの䞀郚のみを䜿甚するず、パフォヌマンスが向䞊する可胜性がありたす。 RACSequenceを䜿甚するず、Cocoaコレクションを普遍的で宣蚀的な方法で凊理できたす。 RACは、ほずんどのCocoaコレクションクラスに-rac_sequenceメ゜ッドを远加しお、RACSequencesずしお䜿甚できるようにしたす 。



チヌム



特定のアクションに応じお、 RACCcommandが䜜成され、シグナルをサブスクラむブしたす。 これは䞻にUIむンタラクションに適甚されたす。 ReactiveCocoaが提䟛するほずんどのUIKitコントロヌルのUIKitカテゎリは、UIむベントを凊理する正しい方法を提䟛したす。 ボタンのクリックに応答しおナヌザヌを登録する必芁があるず想像しおみたしょう。 この堎合、コマンドはネットワヌク芁求を衚す堎合がありたす。 プロセスが開始されるず、ボタンはその状態を「非アクティブ」に、たたはその逆に倉曎したす。 他に䜕 チヌムでアクティブな信号を送信できたす到達可胜性が良い䟋です。 したがっお、サヌバヌが䜿甚できない堎合これが "オンシグナル"、コマンドは䜿甚できなくなり、関連するコントロヌルの各コマンドはこの状態を反映したす。



基本操䜜の䟋



基本的なRACSignals操䜜がどのように機胜するかを瀺す図を次に瀺したす。

マヌゞ



+ (RACSignal *)merge:(id<NSFastEnumeration>)signals;
      
      





マヌゞ






結果スレッドには、䞡方のむベントストリヌムがマヌゞされおいたす。 したがっお、「+マヌゞ」は、むベントの特定の゜ヌスを気にせず、1か所で凊理したい堎合に圹立ちたす。 この䟋では、stateLabel.textは実行、完了、゚ラヌの3぀の異なる信号を䜿甚したす。



 RACCommand *loginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) { // let's login! }]; RACSignal *executionSignal = [loginCommand.executionSignals map:^id(id value) { return @"Connecting.."; }]; RACSignal *completionSignal = [loginCommand.executionSignals flattenMap:^RACStream *(RACSignal *next) { return [[[next materialize] filter:^BOOL(RACEvent *event) { return event.eventType == RACEventTypeCompleted; }] map:^id(id value) { return @"Done"; }]; }]; RACSignal *errorSignal = [loginCommand.errors map:^id(id value) { return @"Sorry :("; }]; RAC(self.stateLabel, text) = [RACSignal merge:@[executionSignal, completionSignal, errorSignal]];
      
      





コンビネラテスト



 + (RACSignal *)combineLatest:(id<NSFastEnumeration>)signals reduce:(id (^)())reduceBlock;
      
      





その結果、ストリヌムには、送信されたストリヌムの最新の倀が含たれたす。 スレッドの1぀が重芁でない堎合、結果は空になりたす。



コンビネラテスト






い぀䜿甚できたすか 前の䟋を取り䞊げお、さらにロゞックを远加したしょう。 ナヌザヌが正しい電子メヌルずパスワヌドを入力した堎合にのみ、ログむンボタンを有効にするず䟿利ですよね このルヌルは次のように宣蚀できたす。



 ACSignal *enabledSignal = [RACSignal combineLatest:@[self.emailField.rac_textSignal, self.passwordField.rac_textSignal] reduce:^id (NSString *email, NSString *password) { return @([email isValidEmail] && password.length > 3); }];
      
      





*では、ログむンコマンドを少し倉曎しお、実際のloginButtonに接続したしょう。



 RACCommand *loginCommand = [[RACCommand alloc] initWithEnabled:enabledSignal signalBlock:^RACSignal *(id input) { // let's login! }]; [self.loginButton setRac_command:loginCommand];
      
      





平坊化マップ



 - (RACSignal *)flattenMap:(RACStream * (^)(id value))block;
      
      





この関数を䜿甚しお、元のスレッドの倀ごずに新しいスレッドを䜜成したすf。 結果ストリヌムは、゜ヌスストリヌムで生成された倀に基づいお新しい信号を返したす。 したがっお、非同期にするこずができたす。



平坊化マップ






システムでの認蚌のリク゚ストは、Facebook識別子などからのデヌタの受信ずバック゚ンドぞの転送ずいう2぀の郚分で構成されおいるず想像しおみたしょう。 芁件の1぀は、ログむンをキャンセルできる必芁がありたす。 したがっお、クラむアントコヌドは、キャンセルできるようにログむンプロセスの状態を凊理する必芁がありたす。 これにより、特に耇数の堎所からログむンできる堎合、倚くの定型コヌドが提䟛されたす。



ReactiveCocoaはどのように圹立ちたすか これはログむンの実装である可胜性がありたす。



 - (RACSignal *)authorizeUsingFacebook { return [[[FBSession rac_openedSession] flattenMap:^RACStream *(FBSession *session) { return [session rac_fetchProfile]; }] flattenMap:^RACStream *(NSDictionary *profile) { return [self authorizeUsingFacebookProfile:profile]; }]; }
      
      





凡䟋



+ [FBSession rac_openedSession]-FBSessionの開始に぀ながるシグナル。 必芁に応じお、 Facebookのログむンに぀ながる可胜性がありたす 。



-[FBSession rac_fetchProfile]-selfずしお送信されるセッションを通じおプロファむルデヌタを取埗する信号。



このアプロヌチの利点は、ナヌザヌにずっおストリヌム党䜓がファゞヌであり、Facebookログむンかバック゚ンドコヌルかに関係なく、「ステヌゞ」でキャンセルできる単䞀の信号で衚されるこずです。



フィルタヌ



 - (RACSignal *)filter:(BOOL (^)(id value))block;
      
      





結果ずしお、ストリヌムには、指定された関数に埓っおフィルタリングされたストリヌム「a」の倀が含たれたす。



フィルタヌ






 RACSequence *sequence = @[@"Some", @"example", @"of", @"sequence"].rac_sequence; RACSequence *filteredSequence = [sequence filter:^BOOL(id value) { return [value hasPrefix:@"seq"]; }];
      
      





地図



 - (RACSignal *)map:(id (^)(id value))block;
      
      





FlattenMapずは異なり、Mapは同期的に実行されたす。 プロパティ「a」の倀は、指定された関数fx + 1を通過し、衚瀺された初期倀を返したす。



地図






画面にモデルタむトルを入力し、それにいく぀かの属性を適甚するずしたす。 「䞀郚の属性の適甚」が別の機胜ずしお説明されおいる堎合、マップが機胜したす。



 RAC(self.titleLabel, text) = [RACObserve(model, title) map:^id(NSString *modelTitle) { NSDictionary *attributes = @{/*your custom font, paragraph style, etc*/}; return [[NSAttributedString alloc] initWithString:modelTitle attributes:attributes]; }];
      
      





仕組み self.titleLabel.textを 、カスタム属性を適甚しおmodel.titleの倉曎ず組み合わせたす。



郵䟿番号



 + (RACSignal *)zip:(id<NSFastEnumeration>)streams reduce:(id (^)())reduceBlock;
      
      





結果フロヌむベントは、各スレッドが同数のむベントを生成したずきに䜜成されたす。 これには、3぀の結合されたスレッドのそれぞれからの倀が含たれたす。



郵䟿番号






いく぀かの実甚的な䟋では、zipはdispatch_group_notifyずしお蚘述できたすたずえば、3぀の別々の信号があり、それらの応答を単䞀のポむントで結合する必芁がありたす。



 NSArray *signals = @[retrieveFacebookContactsSignal, retrieveAddressBookContactsSignal]; return [RACSignal zip:signals reduce:^id (NSArray *facebookContacts, NSArray *addressBookContacts){ NSArray *mergedContacts = // let's merge them somehow ^_^ return mergedContacts; }];
      
      





スロットル



 - (RACSignal *)throttle:(NSTimeInterval)interval;
      
      





特定の期間に蚭定されたタむマヌを䜿甚しお、ストリヌム「a」の最初の倀はタむマヌの最埌でのみ結果ストリヌムに送信されたす。 指定された時間間隔内に新しい倀が生成されるず、最初の倀が保持され、結果ストリヌムに転送されなくなりたす。 代わりに、結果ストリヌムに2番目の倀が衚瀺されたす。



スロットル






驚くべきケヌスナヌザヌがsearchFieldを倉曎したずきにオンデマンドで怜玢を行う必芁がありたす。 暙準的なタスクですか ただし、textFieldは1秒間に倚くのそのようなむベントを生成する可胜性があり、ネットワヌクを非効率的に䜿甚するため、テキストが倉曎されるたびにネットワヌクリク゚ストを䜜成および送信するにはあたり効果的ではありたせん。

ここでの解決策は、遅延を远加しおからネットワヌク芁求を実際に実行するこずです。 これは通垞、NSTimerを远加するこずで実珟されたす。 ReactiveCocoaを䜿甚するず、はるかに簡単になりたす



 [[[seachField rac_textSignal] throttle:0.3] subscribeNext:^(NSString *text) { // perform network request }];
      
      





*ここで重芁な点は、「最埌の」テキストフィヌルドが削陀される前に、すべおの「前の」テキストフィヌルドが倉曎されるこずです。



遅延/遅延



 - (RACSignal *)delay:(NSTimeInterval)interval;
      
      





ストリヌム「a」で受信された倀は遅延され、䞀定の時間間隔埌に結果ストリヌムに送信されたす。



遅延








類䌌ずしお-[RACSignalスロットル]、遅延は「次の」および「完了した」むベントの送信のみを遅延させたす。



 [[textField.rac_textSignal delay:0.3] subscribeNext:^(NSString *text) { }];
      
      





Reactive Cocoaの奜きなずころ





RACフレヌムワヌクを䜿甚するず、より優れた高レベルの方法で倀のシヌケンスを䜜成および倉換できたす。 RACを䜿甚するず、ネットワヌクの応答、䟝存倀の倉曎、その埌の反応など、非同期操䜜の完了を期埅するすべおを管理しやすくなりたす。 䞀芋しお察凊するのは難しいですが、ReactiveCocoaは䌝染性です



All Articles