成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

ReactiveCocoa自述:工作原理和應(yīng)用

移動開發(fā) iOS
如果你已經(jīng)很熟悉函數(shù)式響應(yīng)式編程編程或者了解ReactiveCocoa的一些基本前提,check outDocumentation文件夾作為框架的概述,這里面有一些關(guān)于它怎么工作的深層次的信息.

ReactiveCocoa (RAC)是一個Objective-C的框架,它的靈感來自函數(shù)式響應(yīng)式編程.

如果你已經(jīng)很熟悉函數(shù)式響應(yīng)式編程編程或者了解ReactiveCocoa的一些基本前提,check outDocumentation文件夾作為框架的概述,這里面有一些關(guān)于它怎么工作的深層次的信息.

什么是ReactiveCocoa?

ReactiveCocoa文檔寫得很厲害,并且詳細(xì)地介紹了RAC是什么以及它是怎么工作的?

如果你多學(xué)一點,我們推薦下面這些資源:

Introduction

When to use ReactiveCocoa

Framework Overview

Basic Operators

Header documentation

Previously answered Stack Overflow questions and GitHub issues

The rest of the Documentation folder

Functional Reactive Programming on iOS(eBook)

如果你有任何其他的問題,請隨意提交issue,

file an issue.

介紹

ReactiveCocoa的靈感來自函數(shù)式響應(yīng)式編程.Rather than using mutable variables which are replaced and modified in-place,RAC提供signals(表現(xiàn)為RACSignal)來捕捉當(dāng)前以及將來的值.

通過對signals進(jìn)行連接,綁定和響應(yīng),不需要連續(xù)地觀察和更新值,軟件就能寫了.

舉個例子,一個text field能夠綁定到最新狀態(tài),即使它在變,而不需要用額外的代碼去更新text field每一秒的狀態(tài).它有點像KVO,但它用blocks代替了重寫-observeValueForKeyPath:ofObject:change:context:.

Signals也能夠呈現(xiàn)異步的操作,有點像futures and promises.這極大地簡化了異步軟件,包括了網(wǎng)絡(luò)處理的代碼.

RAC有一個主要的優(yōu)點,就是提供了一個單一的,統(tǒng)一的方法去處理異步的行為,包括delegate方法,blocks回調(diào),target-action機(jī)制,notifications和KVO.

這里有一個簡單的例子:

  1. // When self.username changes, logs the new name to the console. 
  2. // 
  3. // RACObserve(self, username) creates a new RACSignal that sends the current 
  4. // value of self.username, then the new value whenever it changes. 
  5. // -subscribeNext: will execute the block whenever the signal sends a value. 
  6. [RACObserve(self, username) subscribeNext:^(NSString *newName) { 
  7. NSLog(@"%@", newName); 
  8. }]; 

這不像KVO notifications,signals能夠連接在一起并且能夠同時進(jìn)行操作:

  1. // Only logs names that starts with "j". 
  2. // 
  3. // -filter returns a new RACSignal that only sends a new value when its block 
  4. // returns YES. 
  5. [[RACObserve(self, username) 
  6. filter:^(NSString *newName) { 
  7. return [newName hasPrefix:@"j"]; 
  8. }] 
  9. subscribeNext:^(NSString *newName) { 
  10. NSLog(@"%@", newName); 
  11. }]; 

Signals也能夠用來導(dǎo)出狀態(tài).而不是observing properties或者設(shè)置其他的 properties去反應(yīng)新的值,RAC通過signals and operations讓表示屬性變得有可能:

  1. // Creates a one-way binding so that self.createEnabled will be 
  2. // true whenever self.password and self.passwordConfirmation 
  3. // are equal. 
  4. // 
  5. // RAC() is a macro that makes the binding look nicer. 
  6. // 
  7. // +combineLatest:reduce: takes an array of signals, executes the block with the 
  8. // latest value from each signal whenever any of them changes, and returns a new 
  9. // RACSignal that sends the return value of that block as values. 
  10. RAC(self, createEnabled) = [RACSignal 
  11. combineLatest:@[ RACObserve(self, password), RACObserve(self, passwordConfirmation) ] 
  12. reduce:^(NSString *password, NSString *passwordConfirm) { 
  13. return @([passwordConfirm isEqualToString:password]); 
  14. }]; 

Signals不僅僅能夠用在KVO,還可以用在很多的地方.比如說,它們也能夠展示button presses:

  1. // Logs a message whenever the button is pressed. 
  2. // 
  3. // RACCommand creates signals to represent UI actions. Each signal can 
  4. // represent a button press, for example, and have additional work associated 
  5. // with it. 
  6. // 
  7. // -rac_command is an addition to NSButton. The button will send itself on that 
  8. // command whenever it's pressed. 
  9. self.button.rac_command = [[RACCommand alloc] initWithSignalBlock:^(id _) { 
  10. NSLog(@"button was pressed!"); 
  11. return [RACSignal empty]; 
  12. }]; 

或者異步的網(wǎng)絡(luò)操作:

  1. // Hooks up a "Log in" button to log in over the network. 
  2. // 
  3. // This block will be run whenever the login command is executed, starting 
  4. // the login process. 
  5. self.loginCommand = [[RACCommand alloc] initWithSignalBlock:^(id sender) { 
  6. // The hypothetical -logIn method returns a signal that sends a value when 
  7. // the network request finishes. 
  8. return [client logIn]; 
  9. }]; 
  10. // -executionSignals returns a signal that includes the signals returned from 
  11. // the above block, one for each time the command is executed. 
  12. [self.loginCommand.executionSignals subscribeNext:^(RACSignal *loginSignal) { 
  13. // Log a message whenever we log in successfully. 
  14. [loginSignal subscribeCompleted:^{ 
  15. NSLog(@"Logged in successfully!"); 
  16. }]; 
  17. }]; 
  18. // Executes the login command when the button is pressed. 
  19. self.loginButton.rac_command = self.loginCommand; 

Signals能夠展示timers,其他的UI事件,或者其他跟時間改變有關(guān)的東西.

對于用signals來進(jìn)行異步操作,通過連接和改變這些signals能夠進(jìn)行更加復(fù)雜的行為.在一組操作完成時,工作能夠很簡單觸發(fā):

  1. // Performs 2 network operations and logs a message to the console when they are 
  2. // both completed. 
  3. // 
  4. // +merge: takes an array of signals and returns a new RACSignal that passes 
  5. // through the values of all of the signals and completes when all of the 
  6. // signals complete. 
  7. // 
  8. // -subscribeCompleted: will execute the block when the signal completes. 
  9. [[RACSignal 
  10. merge:@[ [client fetchUserRepos], [client fetchOrgRepos] ]] 
  11. subscribeCompleted:^{ 
  12. NSLog(@"They're both done!"); 
  13. }]; 

Signals能夠順序地執(zhí)行異步操作,而不是嵌套block回調(diào).這個和futures and promises很相似:

  1. // Logs in the user, then loads any cached messages, then fetches the remaining 
  2. // messages from the server. After that's all done, logs a message to the 
  3. // console. 
  4. // 
  5. // The hypothetical -logInUser methods returns a signal that completes after 
  6. // logging in. 
  7. // 
  8. // -flattenMap: will execute its block whenever the signal sends a value, and 
  9. // returns a new RACSignal that merges all of the signals returned from the block 
  10. // into a single signal. 
  11. [[[[client 
  12. logInUser] 
  13. flattenMap:^(User *user) { 
  14. // Return a signal that loads cached messages for the user. 
  15. return [client loadCachedMessagesForUser:user]; 
  16. }] 
  17. flattenMap:^(NSArray *messages) { 
  18. // Return a signal that fetches any remaining messages. 
  19. return [client fetchMessagesAfterMessage:messages.lastObject]; 
  20. }] 
  21. subscribeNext:^(NSArray *newMessages) { 
  22. NSLog(@"New messages: %@", newMessages); 
  23. } completed:^{ 
  24. NSLog(@"Fetched all messages."); 
  25. }]; 

RAC也能夠簡單地綁定異步操作的結(jié)果:

  1. // Creates a one-way binding so that self.imageView.image will be set as the user's 
  2. // avatar as soon as it's downloaded. 
  3. // 
  4. // The hypothetical -fetchUserWithUsername: method returns a signal which sends 
  5. // the user. 
  6. // 
  7. // -deliverOn: creates new signals that will do their work on other queues. In 
  8. // this example, it's used to move work to a background queue and then back to the main thread. 
  9. // 
  10. // -map: calls its block with each user that's fetched and returns a new 
  11. // RACSignal that sends values returned from the block. 
  12. RAC(self.imageView, image) = [[[[client 
  13. fetchUserWithUsername:@"joshaber"
  14. deliverOn:[RACScheduler scheduler]] 
  15. map:^(User *user) { 
  16. // Download the avatar (this is done on a background queue). 
  17. return [[NSImage alloc] initWithContentsOfURL:user.avatarURL]; 
  18. }] 
  19. // Now the assignment will be done on the main thread. 
  20. deliverOn:RACScheduler.mainThreadScheduler]; 

這里僅僅說了RAC能做什么,但很難說清RAC為什么如此強(qiáng)大.雖然通過這個README很難說清RAC,但我盡可能用更少的代碼,更少的模版,把更好的代碼去表達(dá)清楚.

如果想要更多的示例代碼,可以check outC-41 或者 GroceryList,這些都是真正用ReactiveCocoa寫的iOS apps.更多的RAC信息可以看一下Documentation文件夾.

什么時候用ReactiveCocoa

乍看上去,ReactiveCocoa是很抽象的,它可能很難理解如何將它應(yīng)用到具體的問題.

這里有一些RAC常用的地方.

處理異步或者事件驅(qū)動數(shù)據(jù)源

很多Cocoa編程集中在響應(yīng)user events或者改變application state.這樣寫代碼很快地會變得很復(fù)雜,就像一個意大利面,需要處理大量的回調(diào)和狀態(tài)變量的問題.

這個模式表面上看起來不同,像UI回調(diào),網(wǎng)絡(luò)響應(yīng),和KVO notifications,實際上有很多的共同之處。RACSignal統(tǒng)一了這些API,這樣他們能夠組裝在一起然后用相同的方式操作.

舉例看一下下面的代碼:

  1. static void *ObservationContext = &ObservationContext; 
  2. - (void)viewDidLoad { 
  3. [super viewDidLoad]; 
  4. [LoginManager.sharedManager addObserver:self forKeyPath:@"loggingIn" options:NSKeyValueObservingOptionInitial context:&ObservationContext]; 
  5. [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(loggedOut:) name:UserDidLogOutNotification object:LoginManager.sharedManager]; 
  6. [self.usernameTextField addTarget:self action:@selector(updateLogInButton) forControlEvents:UIControlEventEditingChanged]; 
  7. [self.passwordTextField addTarget:self action:@selector(updateLogInButton) forControlEvents:UIControlEventEditingChanged]; 
  8. [self.logInButton addTarget:self action:@selector(logInPressed:) forControlEvents:UIControlEventTouchUpInside]; 
  9. - (void)dealloc { 
  10. [LoginManager.sharedManager removeObserver:self forKeyPath:@"loggingIn" context:ObservationContext]; 
  11. [NSNotificationCenter.defaultCenter removeObserver:self]; 
  12. - (void)updateLogInButton { 
  13. BOOL textFieldsNonEmpty = self.usernameTextField.text.length > 0 && self.passwordTextField.text.length > 0
  14. BOOL readyToLogIn = !LoginManager.sharedManager.isLoggingIn && !self.loggedIn; 
  15. self.logInButton.enabled = textFieldsNonEmpty && readyToLogIn; 
  16. - (IBAction)logInPressed:(UIButton *)sender { 
  17. [[LoginManager sharedManager] 
  18. logInWithUsername:self.usernameTextField.text 
  19. password:self.passwordTextField.text 
  20. success:^{ 
  21. self.loggedIn = YES; 
  22. } failure:^(NSError *error) { 
  23. [self presentError:error]; 
  24. }]; 
  25. - (void)loggedOut:(NSNotification *)notification { 
  26. self.loggedIn = NO; 
  27. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 
  28. if (context == ObservationContext) { 
  29. [self updateLogInButton]; 
  30. else { 
  31. [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 

… 用RAC表達(dá)的話就像下面這樣:

  1. - (void)viewDidLoad { 
  2. [super viewDidLoad]; 
  3. @weakify(self); 
  4. RAC(self.logInButton, enabled) = [RACSignal 
  5. combineLatest:@[ 
  6. self.usernameTextField.rac_textSignal, 
  7. self.passwordTextField.rac_textSignal, 
  8. RACObserve(LoginManager.sharedManager, loggingIn), 
  9. RACObserve(self, loggedIn) 
  10. ] reduce:^(NSString *username, NSString *password, NSNumber *loggingIn, NSNumber *loggedIn) { 
  11. return @(username.length > 0 && password.length > 0 && !loggingIn.boolValue && !loggedIn.boolValue); 
  12. }]; 
  13. [[self.logInButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton *sender) { 
  14. @strongify(self); 
  15. RACSignal *loginSignal = [LoginManager.sharedManager 
  16. logInWithUsername:self.usernameTextField.text 
  17. password:self.passwordTextField.text]; 
  18. [loginSignal subscribeError:^(NSError *error) { 
  19. @strongify(self); 
  20. [self presentError:error]; 
  21. } completed:^{ 
  22. @strongify(self); 
  23. self.loggedIn = YES; 
  24. }]; 
  25. }]; 
  26. RAC(self, loggedIn) = [[NSNotificationCenter.defaultCenter 
  27. rac_addObserverForName:UserDidLogOutNotification object:nil] 
  28. mapReplace:@NO]; 

連接依賴的操作

依賴經(jīng)常用在網(wǎng)絡(luò)請求,當(dāng)下一個對服務(wù)器網(wǎng)絡(luò)請求需要構(gòu)建在前一個完成時,可以看一下下面的代碼:

  1. [client logInWithSuccess:^{ 
  2. [client loadCachedMessagesWithSuccess:^(NSArray *messages) { 
  3. [client fetchMessagesAfterMessage:messages.lastObject success:^(NSArray *nextMessages) { 
  4. NSLog(@"Fetched all messages."); 
  5. } failure:^(NSError *error) { 
  6. [self presentError:error]; 
  7. }]; 
  8. } failure:^(NSError *error) { 
  9. [self presentError:error]; 
  10. }]; 
  11. } failure:^(NSError *error) { 
  12. [self presentError:error]; 
  13. }]; 

ReactiveCocoa 則讓這種模式特別簡單:

  1. __block NSArray *databaseObjects; 
  2. __block NSArray *fileContents; 
  3. NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init]; 
  4. NSBlockOperation *databaseOperation = [NSBlockOperation blockOperationWithBlock:^{ 
  5. databaseObjects = [databaseClient fetchObjectsMatchingPredicate:predicate]; 
  6. }]; 
  7. NSBlockOperation *filesOperation = [NSBlockOperation blockOperationWithBlock:^{ 
  8. NSMutableArray *filesInProgress = [NSMutableArray array]; 
  9. for (NSString *path in files) { 
  10. [filesInProgress addObject:[NSData dataWithContentsOfFile:path]]; 
  11. fileContents = [filesInProgress copy]; 
  12. }]; 
  13. NSBlockOperation *finishOperation = [NSBlockOperation blockOperationWithBlock:^{ 
  14. [self finishProcessingDatabaseObjects:databaseObjects fileContents:fileContents]; 
  15. NSLog(@"Done processing"); 
  16. }]; 
  17. [finishOperation addDependency:databaseOperation]; 
  18. [finishOperation addDependency:filesOperation]; 
  19. [backgroundQueue addOperation:databaseOperation]; 
  20. [backgroundQueue addOperation:filesOperation]; 
  21. [backgroundQueue addOperation:finishOperation]; 

上面的代碼能夠簡單地用合成signals來清理和優(yōu)化:

  1. RACSignal *databaseSignal = [[databaseClient 
  2. fetchObjectsMatchingPredicate:predicate] 
  3. subscribeOn:[RACScheduler scheduler]]; 
  4. RACSignal *fileSignal = [RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(id subscriber) { 
  5. NSMutableArray *filesInProgress = [NSMutableArray array]; 
  6. for (NSString *path in files) { 
  7. [filesInProgress addObject:[NSData dataWithContentsOfFile:path]]; 
  8. [subscriber sendNext:[filesInProgress copy]]; 
  9. [subscriber sendCompleted]; 
  10. }]; 
  11. [[RACSignal 
  12. combineLatest:@[ databaseSignal, fileSignal ] 
  13. reduce:^ id (NSArray *databaseObjects, NSArray *fileContents) { 
  14. [self finishProcessingDatabaseObjects:databaseObjects fileContents:fileContents]; 
  15. return nil; 
  16. }] 
  17. subscribeCompleted:^{ 
  18. NSLog(@"Done processing"); 
  19. }]; 

簡化集合轉(zhuǎn)換

像map, filter, fold/reduce 這些高級功能在Foundation中是極度缺少的m導(dǎo)致了一些像下面這樣循環(huán)集中的代碼:

  1. NSMutableArray *results = [NSMutableArray array]; 
  2. for (NSString *str in strings) { 
  3. if (str.length < 2) { 
  4. continue
  5. NSString *newString = [str stringByAppendingString:@"foobar"]; 
  6. [results addObject:newString]; 

RACSequence能夠允許Cocoa集合用統(tǒng)一的方式操作:

  1. RACSequence *results = [[strings.rac_sequence 
  2. filter:^ BOOL (NSString *str) { 
  3. return str.length >= 2
  4. }] 
  5. map:^(NSString *str) { 
  6. return [str stringByAppendingString:@"foobar"]; 
  7. }]; 

系統(tǒng)要求

ReactiveCocoa 要求 OS X 10.8+ 以及 iOS 8.0+.

引入 ReactiveCocoa

增加 RAC 到你的應(yīng)用中:

1. 增加 ReactiveCocoa 倉庫 作為你應(yīng)用倉庫的一個子模塊.

2. 從ReactiveCocoa文件夾中運(yùn)行 script/bootstrap .

3. 拖拽 ReactiveCocoa.xcodeproj 到你應(yīng)用的 Xcode project 或者 workspace中.

4. 在你應(yīng)用target的"Build Phases"的選項卡,增加 RAC到 "Link Binary With Libraries"

On iOS, 增加 libReactiveCocoa-iOS.a.

On OS X, 增加 ReactiveCocoa.framework.

RAC 必須選擇"Copy Frameworks" . 假如你沒有的話, 需要選擇"Copy Files"和"Frameworks" .

5. 增加 "$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/include"

$(inherited)到 "Header Search Paths" (這需要archive builds, 但也沒什么影響).

6. For iOS targets, 增加 -ObjC 到 "Other Linker Flags" .

7. 假如你增加 RAC到一個project (不是一個workspace), 你需要適當(dāng)?shù)奶砑覴AC target到你應(yīng)用的"Target Dependencies".

假如你喜歡用CocoaPods,這里有一些慷慨地第三方貢獻(xiàn)ReactiveCocoa podspecs .

想看一個用了RAC的工程,check outC-41 或者 GroceryList,這些是真實的用ReactiveCocoa寫的iOS apps.

獨(dú)立開發(fā)

假如你的工作用RAC是隔離的而不是將其集成到另一個項目,你會想打開ReactiveCocoa.xcworkspace 而不是.xcodeproj.

更多信息

ReactiveCocoa靈感來自.NET的ReactiveExtensions (Rx).Rx的一些原則也能夠很好的用在RAC.這里有些好的Rx資源:

Reactive Extensions MSDN entry

Reactive Extensions for .NET Introduction

Rx - Channel 9 videos

Reactive Extensions wiki

101 Rx Samples

Programming Reactive Extensions and LINQ

RAC和Rx靈感都是來自函數(shù)式響應(yīng)式編程.這里有些關(guān)于FRP(functional reactive programming)相關(guān)的資源:

What is FRP? - Elm Language

What is Functional Reactive Programming - Stack Overflow

Specification for a Functional Reactive Language - Stack Overflow

Escape from Callback Hell

Principles of Reactive Programming on Coursera

責(zé)任編輯:chenqingxiang
相關(guān)推薦

2016-03-30 09:34:27

2025-06-06 08:04:17

2014-04-02 17:10:00

虛擬應(yīng)用工作原理

2009-07-09 14:01:22

JVM工作原理

2010-09-26 08:50:11

JVM工作原理

2010-09-16 14:42:44

JVM

2023-09-27 12:22:50

Kafka架構(gòu)

2017-09-11 18:24:39

企業(yè)應(yīng)用自述

2010-09-17 15:32:52

JVM工作原理

2016-12-13 22:51:08

androidmultidex

2010-09-08 20:20:39

2011-07-01 11:16:14

Struts

2023-06-08 15:27:17

CAN網(wǎng)絡(luò)

2021-03-18 09:07:20

Nginx原理實踐

2009-06-18 13:31:03

Spring工作原理

2009-08-14 13:19:23

2012-03-14 14:40:54

Mac用戶PC用戶

2017-05-17 08:51:39

WebView分析應(yīng)用

2011-05-07 17:06:21

熱升華打印機(jī)工作原理應(yīng)用

2021-09-28 08:59:40

UPS蓄電池電源
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 成人午夜视频在线观看 | 国产精品久久久久久久三级 | 在线免费观看欧美 | 天天操,夜夜爽 | 97超级碰碰| 日韩电影一区 | 久久国产精品一区二区三区 | 一区二区三区四区电影视频在线观看 | 久久久无码精品亚洲日韩按摩 | 国产日韩欧美91 | 国产一区二区三区 | 国产精品区一区二区三区 | 亚州综合在线 | www国产成人免费观看视频,深夜成人网 | 日韩中文字幕2019 | 精品久久久久久久久久久久久久 | 亚州精品天堂中文字幕 | 在线观看三级av | 国产成人免费视频网站高清观看视频 | 精品国产一区二区三区四区在线 | 日日摸夜夜爽人人添av | 国产成人在线免费 | 久久国产精99精产国高潮 | 国产91丝袜在线熟 | 欧美片网站免费 | 国产精品夜夜夜一区二区三区尤 | 亚洲va欧美va天堂v国产综合 | 亚洲欧洲精品一区 | 日韩av黄色 | 色资源站| 成人激情视频免费观看 | 久久精品日 | 中文字幕在线观看一区二区 | 91欧美 | 韩日在线 | 免费久久精品视频 | 色在线看| 一级黄色影片在线观看 | 久久国产视频网站 | 美女天天操 | 懂色中文一区二区三区在线视频 |