ReactiveCocoa入门教程:第二部分

- (BOOL)isValidSearchText:(NSString *)text {
return text.length > ;
}
这就简单的保证了搜索的字符串大于两个字符。写这个很简单的逻辑你可能会问:为什么要分开该方法到工程文件里面呢?
#import <ReactiveCocoa.h>
[[self.searchText.rac_textSignal
map:^id(NSString *text) {
return [self isValidSearchText:text] ?
[UIColor whiteColor] : [UIColor yellowColor];
}]
subscribeNext:^(UIColor *color) {
self.searchText.backgroundColor = color;
}];
想想这是做什么呢?上面的代码:


RACSignal *backgroundColorSignal = [self.searchText.rac_textSignal
map:^id(NSString *text) {
return [self isValidSearchText:text] ? [UIColor whiteColor] : [UIColor yellowColor];
}]; RACDisposable *subscription = [backgroundColorSignal
subscribeNext:^(UIColor *color) {
self.searchText.backgroundColor = color;
}]; // at some point in the future ...
[subscription dispose];
你不会经常做这些,但是你必须知道可能性的存在。
Note:作为这些的一个推论,如果你创建了一个管道,但是你不给他订阅,这个管道将不会执行,这些包括任何侧面的影响,例如doNext:blocks。
Avoiding Retain Cycles
当ReactiveCocoa在场景背后做了好多聪明的事情—这就意味着你不必要担心太多关于信号量的内存管理——这里有一个很重要的内存喜爱那个管的问你你需要考虑。
[[self.searchText.rac_textSignal
map:^id(NSString *text) {
return [self isValidSearchText:text] ?
[UIColor whiteColor] : [UIColor yellowColor];
}]
subscribeNext:^(UIColor *color) {
self.searchText.backgroundColor = color;
}];
__weak RWSearchFormViewController *bself = self; // Capture the weak reference [[self.searchText.rac_textSignal
map:^id(NSString *text) {
return [self isValidSearchText:text] ?
[UIColor whiteColor] : [UIColor yellowColor];
}]
subscribeNext:^(UIColor *color) {
bself.searchText.backgroundColor = color;
}];
#import "RACEXTScope.h"
@weakify(self)然后代码修改后如下:
[[self.searchText.rac_textSignal
map:^id(NSString *text) {
return [self isValidSearchText:text] ?
[UIColor whiteColor] : [UIColor yellowColor];
}]
subscribeNext:^(UIColor *color) {
@strongify(self)
self.searchText.backgroundColor = color;
}];
@weakify和@strongify语句是在Extended Objective-C库的宏定义,他们也包含在ReactiveCocoa中。@weakify 宏定义允许你创建一个若饮用的影子变量,@strongify宏定义允许你创建一个前面使用@weakify传递的强引用变量。
Note:如果你对@weakify和@strongify感兴趣,可以进入RACEXTSCope.h中查看其实现。


#import <Accounts/Accounts.h>
#import <Social/Social.h>
然后在引入的头文件下面写如下的代码:
typedef NS_ENUM(NSInteger, RWTwitterInstantError) {
RWTwitterInstantErrorAccessDenied,
RWTwitterInstantErrorNoTwitterAccounts,
RWTwitterInstantErrorInvalidResponse
};
static NSString * const RWTwitterInstantDomain = @"TwitterInstant";
你将会使用这些简单地鉴定错误。然后在interface和end之间声明两个属性:
@property (strong, nonatomic) ACAccountStore *accountStore;
@property (strong, nonatomic) ACAccountType *twitterAccountType;
ACAccountsStore类提供访问你当前设备有的social账号,ACAccountType类代表指定类型的账户。
self.accountStore = [[ACAccountStore alloc] init];
self.twitterAccountType = [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
这些代码创建了账户存储和Twitter账号标示。在.m中添加如下方法:
- (RACSignal *)requestAccessToTwitterSignal {
// 1 - define an error
NSError *accessError = [NSError errorWithDomain:RWTwitterInstantDomain
code:RWTwitterInstantErrorAccessDenied
userInfo:nil];
// 2 - create the signal
@weakify(self)
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3 - request access to twitter
@strongify(self)
[self.accountStore
requestAccessToAccountsWithType:self.twitterAccountType
options:nil
completion:^(BOOL granted, NSError *error) {
// 4 - handle the response
if (!granted) {
[subscriber sendError:accessError];
} else {
[subscriber sendNext:nil];
[subscriber sendCompleted];
}
}];
return nil;
}];
}
这个方法的作用是:
[[self requestAccessToTwitterSignal]
subscribeNext:^(id x) {
NSLog(@"Access granted");
} error:^(NSError *error) {
NSLog(@"An error occurred: %@", error);
}];

[[[self requestAccessToTwitterSignal]
then:^RACSignal *{
@strongify(self)
return self.searchText.rac_textSignal;
}]
subscribeNext:^(id x) {
NSLog(@"%@", x);
} error:^(NSError *error) {
NSLog(@"An error occurred: %@", error);
}];
then方法会一直等待,知道completed事件发出,然后订阅者通过自己的block参数返回,这有效地将控制从一个信号传递给下一个。
Note:上面已经写过了@weakly(self);所以这里就不用再写了。
then方法传递error事件。因此最后的subscribeNext:error: block还接收初始的访问请求错误。
[[[[self requestAccessToTwitterSignal]
then:^RACSignal *{
@strongify(self)
return self.searchText.rac_textSignal;
}]
filter:^BOOL(NSString *text) {
@strongify(self)
return [self isValidSearchText:text];
}]
subscribeNext:^(id x) {
NSLog(@"%@", x);
} error:^(NSError *error) {
NSLog(@"An error occurred: %@", error);
}];

- (SLRequest *)requestforTwitterSearchWithText:(NSString *)text {
NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/1.1/search/tweets.json"];
NSDictionary *params = @{@"q" : text};
SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeTwitter
requestMethod:SLRequestMethodGET
URL:url
parameters:params];
return request;
}
下一步就是创建一个基于request的信号量。添加如下方法:这创建了一个请求:搜索Twitter(V.1.1REST API)。这个是调用Twitter的api。
- (RACSignal *)signalForSearchWithText:(NSString *)text {
// 1 - define the errors
NSError *noAccountsError = [NSError errorWithDomain:RWTwitterInstantDomain
code:RWTwitterInstantErrorNoTwitterAccounts
userInfo:nil];
NSError *invalidResponseError = [NSError errorWithDomain:RWTwitterInstantDomain
code:RWTwitterInstantErrorInvalidResponse
userInfo:nil];
// 2 - create the signal block
@weakify(self)
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self);
// 3 - create the request
SLRequest *request = [self requestforTwitterSearchWithText:text];
// 4 - supply a twitter account
NSArray *twitterAccounts = [self.accountStore
accountsWithAccountType:self.twitterAccountType];
if (twitterAccounts.count == ) {
[subscriber sendError:noAccountsError];
} else {
[request setAccount:[twitterAccounts lastObject]];
// 5 - perform the request
[request performRequestWithHandler: ^(NSData *responseData,
NSHTTPURLResponse *urlResponse, NSError *error) {
if (urlResponse.statusCode == ) {
// 6 - on success, parse the response
NSDictionary *timelineData =
[NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONReadingAllowFragments
error:nil];
[subscriber sendNext:timelineData];
[subscriber sendCompleted];
}
else {
// 7 - send an error on failure
[subscriber sendError:invalidResponseError];
}
}];
}
return nil;
}];
}
[[[[[self requestAccessToTwitterSignal]
then:^RACSignal *{
@strongify(self)
return self.searchText.rac_textSignal;
}]
filter:^BOOL(NSString *text) {
@strongify(self)
return [self isValidSearchText:text];
}]
flattenMap:^RACStream *(NSString *text) {
@strongify(self)
return [self signalForSearchWithText:text];
}]
subscribeNext:^(id x) {
NSLog(@"%@", x);
} error:^(NSError *error) {
NSLog(@"An error occurred: %@", error);
}];
运行:
[[[[[[self requestAccessToTwitterSignal]
then:^RACSignal *{
@strongify(self)
return self.searchText.rac_textSignal;
}]
filter:^BOOL(NSString *text) {
@strongify(self)
return [self isValidSearchText:text];
}]
flattenMap:^RACStream *(NSString *text) {
@strongify(self)
return [self signalForSearchWithText:text];
}]
deliverOn:[RACScheduler mainThreadScheduler]]
subscribeNext:^(id x) {
NSLog(@"%@", x);
} error:^(NSError *error) {
NSLog(@"An error occurred: %@", error);
}];
这样就会在主线程中运行。也就是更新了管道:添加了deliverOn:操作。
#import "RWTweet.h"
#import "NSArray+LinqExtensions.h"
[[[[[[self requestAccessToTwitterSignal]
then:^RACSignal *{
@strongify(self)
return self.searchText.rac_textSignal;
}]
filter:^BOOL(NSString *text) {
@strongify(self)
return [self isValidSearchText:text];
}]
flattenMap:^RACStream *(NSString *text) {
@strongify(self)
return [self signalForSearchWithText:text];
}]
deliverOn:[RACScheduler mainThreadScheduler]]
subscribeNext:^(NSDictionary *jsonSearchResult) {
NSArray *statuses = jsonSearchResult[@"statuses"];
NSArray *tweets = [statuses linq_select:^id(id tweet) {
return [RWTweet tweetWithStatus:tweet];
}];
[self.resultsViewController displayTweets:tweets];
} error:^(NSError *error) {
NSLog(@"An error occurred: %@", error);
}];
运行:

-(RACSignal *)signalForLoadingImage:(NSString *)imageUrl {
RACScheduler *scheduler = [RACScheduler
schedulerWithPriority:RACSchedulerPriorityBackground];
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
UIImage *image = [UIImage imageWithData:data];
[subscriber sendNext:image];
[subscriber sendCompleted];
return nil;
}] subscribeOn:scheduler];
}
这会你一ing该就会很熟悉这种模式了。然后在tableview:cellForRowAtIndex:方法里面添加:
cell.twitterAvatarView.image = nil; [[[self signalForLoadingImage:tweet.profileImageUrl]
deliverOn:[RACScheduler mainThreadScheduler]]
subscribeNext:^(UIImage *image) {
cell.twitterAvatarView.image = image;
}];
再次运行就可以出来效果了:

[[[[[[[self requestAccessToTwitterSignal]
then:^RACSignal *{
@strongify(self)
return self.searchText.rac_textSignal;
}]
filter:^BOOL(NSString *text) {
@strongify(self)
return [self isValidSearchText:text];
}]
throttle:0.5]
flattenMap:^RACStream *(NSString *text) {
@strongify(self)
return [self signalForSearchWithText:text];
}]
deliverOn:[RACScheduler mainThreadScheduler]]
subscribeNext:^(NSDictionary *jsonSearchResult) {
NSArray *statuses = jsonSearchResult[@"statuses"];
NSArray *tweets = [statuses linq_select:^id(id tweet) {
return [RWTweet tweetWithStatus:tweet];
}];
[self.resultsViewController displayTweets:tweets];
} error:^(NSError *error) {
NSLog(@"An error occurred: %@", error);
}];
你会发现这样就可以了。throttle操作只是发送一个操作,这个操作在时间到之后继续进行。

ReactiveCocoa入门教程:第二部分的更多相关文章
- iOS开发 ReactiveCocoa入门教程 第二部分
ReactiveCocoa 是一个框架,它允许你在你的iOS程序中使用函数响应式(FRP)技术.加上第一部分的讲解,你将会学会如何使用信号量(对事件发出数据流)如何替代标准的动作和事件处理逻辑.你也会 ...
- ReactiveCocoa入门教程--第二部分
翻译自:http://www.raywenderlich.com/62796/reactivecocoa-tutorial-pt2 ReactiveCocoa 是一个框架,它允许你在你的iOS程序中使 ...
- ReactiveCocoa入门教程——第二部分(转)
ReactiveCocoa是一个框架,它能让你在iOS应用中使用函数响应式编程(FRP)技术.在本系列教程的第一部分中,你学到了如何将标准的动作与事件处理逻辑替换为发送事件流的信号.你还学到了如何转换 ...
- ReactiveCocoa入门教程——第二部分【转载】
ReactiveCocoa是一个框架,它能让你在iOS应用中使用函数响应式编程(FRP)技术.在本系列教程的第一部分中,你学到了如何将标准的动作与事件处理逻辑替换为发送事件流的信号.你还学到了如何转换 ...
- iOS开发 ReactiveCocoa入门教程 第一部分
作为一个iOS开发者,你写的每一行代码几乎都是在响应某个事件,例如按钮的点击,收到网络消息,属性的变化(通过KVO)或者用户位置的变化(通过CoreLocation).但是这些事件都用不同的方式来处理 ...
- ReactiveCocoa入门教程:第一部分
http://www.cocoachina.com/ios/20150123/10994.html 本文翻译自RayWenderlich,原文:ReactiveCocoa Tutorial--The ...
- ReactiveCocoa入门教程——第一部分(转)
作为一个iOS开发者,你写的每一行代码几乎都是在响应某个事件,例如按钮的点击,收到网络消息,属性的变化(通过KVO)或者用户位置的变化(通过CoreLocation).但是这些事件都用不同的方式来处理 ...
- ReactiveCocoa入门教程——第一部分
ReactiveCocoa iOS 翻译 2015-01-22 02:33:37 11471 6 15 本文翻译自RayWenderlich ReactiveCocoa ...
- ReactiveCocoa入门教程——第一部分【转载】
作为一个iOS开发者,你写的每一行代码几乎都是在响应某个事件,例如按钮的点击,收到网络消息,属性的变化(通过KVO)或者用户位置的变化(通过CoreLocation).但是这些事件都用不同的方式来处理 ...
随机推荐
- Android通过编码实现GPS开关
在Android 2.2以后才可使用 import android.content.ContentResolver; import android.content.Context; import an ...
- 将XmlDocument转换成XDocument
XmlDocument xml=new XmlDocument(); xml.LoadXml(strXmlText); XmlReader xr=new XmlNodeReader(xml); XDo ...
- 2次成功投诉EMS和中国移动的经验
上个月要找房子,搬家很多事情,真实头疼...搬家还把腰闪了....现在还有点痛.然后中间碰到 移动宽带 移机的事情,搞得我非常火.然后想起去年投诉EMS的事情,在事情处理完成后,我果断总结了下来,让大 ...
- 栈的图文解析 和 对应3种语言的实现(C/C++/Java)
概要 本章会先对栈的原理进行介绍,然后分别通过C/C++/Java三种语言来演示栈的实现示例.注意:本文所说的栈是数据结构中的栈,而不是内存模型中栈.内容包括:1. 栈的介绍2. 栈的C实现3. 栈的 ...
- Tools - Oracle SQL Developer
Oracle SQL Developer Oracle公司出品的一个免费非开源的用以开发数据库应用程序的图形化工具. 可以浏览数据库对象.运行SQL语句和脚本.编辑和调试PL/SQL语句,也可以创建执 ...
- Qt之QAbstractItemView视图项拖拽(一)
一.需求说明 最近在搞视图项的拖拽,也上网查了一些资料,好多的文档都是一样的,只是被不通的网站所收录了(也有可能是被爬过去的,不明所以),不过也有一些文档写的不错,不过就是太简易,都是点睛之笔,总之功 ...
- mysql根据身份证信息来获取用户属性信息
需要:根据身份证信息来获取用户属性 方法:可以使用如下sql语句: ) ' then '北京市' ' then '天津市' ' then '河北省' ' then '山西省' ' then '内蒙古自 ...
- Python 3.x自定义迭代器对象
Python 3.x与Python 2.x之间存在着较多的语法细节差异.今天在看Python核心编程的时候,说到了自定义迭代器对象.于是动手将源码打了一遍,原书代码如下: class AnyIter( ...
- CCFlow SDK模式开发
需求: 1.业务数据要保存在我们自己的数据库里 2.CCFlow有保存草稿的功能,但是领导要求每个业务都要有草稿箱,流程从草稿箱发起,每个业务单独查询,而不要在CCFlow的统一界面查询,所以每 ...
- 给文本框添加模糊搜索功能(“我记录”MVC框架下实现)
步骤: 1.在文本框中输入内容时,触发keyup事件: 2.在keyup事件的处理方法中,通过Ajax调用控制器的方法: 3.在控制器方法中,搜索满足条件的数据,这里分页获取数据,且只取第一页的数据, ...