当前位置: 首页 > 编程日记 > 正文

ReactiveCocoa入门-part2

ReactiveCocoa是一个框架,它能让你在iOS应用中使用函数响应式编程(FRP)技术。在本系列教程的第一部分中,你学到了如何将标准的动作与事件处理逻辑替换为发送事件流的信号。你还学到了如何转换、分割和聚合这些信号。

在本系列教程的第二部分,你将会学到一些ReactiveCocoa的高级功能,包括:

  • 另外两个事件类型:error 和 completed
  • 节流
  • 线程
  • 延伸
  • 其他

是时候深入研究一下了。

Twitter Instant

在本教程中你将要开发的应用叫Twitter Instant(基于Google Instant的概念),这个应用能搜索Twitter上的内容,并根据输入实时更新搜索结果。

这个应用的初始工程包括一些基本的UI和必须的代码。和第一部分一样,你需要使用CocoaPods来获取ReactiveCocoa框架,并集成到项目中。

验证搜索文本的有效性

首先要做的就是验证搜索文本,来确保文本长度大于2个字符。如果你完成了本系列教程的第一部分,那这个应该很熟悉。

RWSearchFormViewController.m中的viewDidLoad 下面添加下面的方法:

- (BOOL)isValidSearchText:(NSString *)text {return text.length > 2;
}

这个方法就只是确保要搜索的字符串长度大于2个字符。这个逻辑很简单,你可能会问“为什么要在工程文件中写这么一个单独的方法呢?”。

目前验证输入有效性的逻辑的确很简单,但如果将来逻辑需要变得更复杂呢?如果是像上面的例子中那样,那你就只需要修改一个地方。而且这样写能让你代码的可读性更高,代码本身就说明了你为什么要检查字符串的长度。

把下面的代码加到viewDidLoad的最下面 :

[[self.searchText.rac_textSignal map:^id(NSString *text) {return [self isValidSearchText:text] ? [UIColor whiteColor] : [UIColor yellowColor];}] subscribeNext:^(UIColor *color) { self.searchText.backgroundColor = color; 
}];​

上面的代码做了什么呢?

  • 获取search text field 的text signal
  • 将其转换为颜色来标示输入是否有效
  • 然后在subscribeNext:block里将颜色应用到search text field的backgroundColor属性

当text field中的文字每次发生变化时,rac_textSignal都会发送一个next 事件,事件包含当前text field中的文字。map这一步将文本值转换成了颜色值,所以subscribeNext:这一步会拿到这个颜色值,并应用在text field的背景色上。

避免循环引用

ReactiveCocoa已经在幕后做了很多事情,这也就意味着你并不需要太多关注signal的内存管理。但是还有一个很重要的内存相关问题你需要注意。

看一下你刚才添加的代码:

[[self.searchText.rac_textSignal map:^id(NSString *text) { return [self isValidSearchText:text] ? [UIColor whiteColor] : [UIColor yellowColor]; }] subscribeNext:^(UIColor *color) { self.searchText.backgroundColor = color; }];​

subscribeNext:block中使用了self来获取text field的引用。block会捕获并持有其作用域内的值。因此,如果self和这个信号之间存在一个强引用的话,就会造成循环引用。循环引用是否会造成问题,取决于self对象的生命周期。如果self的生命周期是整个应用运行时,比如说本例,那也就无伤大雅。但是在更复杂一些的应用中,就不是这么回事了。

为了避免潜在的循环引用,Apple的文档Working With Blocks中建议获取一个self的弱引用。用本例来说就是下面这样的:

__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; }];​

在上面的代码中,__weak修饰符使bself成为了self的一个弱引用。注意现在subscribeNext:block中使用bself变量。不过这种写法看起来不是那么优雅。

ReactiveCocoa框架包含了一个语法糖来替换上面的代码。在文件顶部添加下面的代码:

#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对象的强引用。

就在引用的下面,添加下面的枚举和常量:

typedef NS_ENUM(NSInteger, RWTwitterInstantError) {RWTwitterInstantErrorAccessDenied, RWTwitterInstantErrorNoTwitterAccounts, RWTwitterInstantErrorInvalidResponse 
}; static NSString * const RWTwitterInstantDomain = @"TwitterInstant";​

一会儿你就要用到它们来标示错误。

还是在这个文件中,在已有属性声明的下面,添加下面的代码:

@property (strong, nonatomic) ACAccountStore *accountStore; 
@property (strong, nonatomic) ACAccountType *twitterAccountType;​

ACAccountsStore类能让你访问你的设备能连接到的多个社交媒体账号,ACAccountType类则代表账户的类型。

还是在这个文件中,把下面的代码添加到viewDidLoad的最下面:

self.accountStore = [[ACAccountStore alloc] init]; 
self.twitterAccountType = [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];

上面的代码创建了一个account store和Twitter账户标识符。

当应用获取访问社交媒体账号的权限时,用户会看见一个弹框。这是一个异步操作,因此把这封装进一个signal是很好的选择。

还是在这个文件中,添加下面的代码:

- (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 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; }]; 
}​

这个方法做了下面几件事:

  1. 定义了一个error,当用户拒绝访问时发送。
  2. 和第一部分一样,类方法createSignal返回一个RACSignal实例。
  3. 通过account store请求访问Twitter。此时用户会看到一个弹框来询问是否允许访问Twitter账户。
  4. 在用户允许或拒绝访问之后,会发送signal事件。如果用户允许访问,会发送一个next事件,紧跟着再发送一个completed事件。如果用户拒绝访问,会发送一个error事件。

在signal的生命周期中,它可能不发送事件,发送一个或多个next事件,在这之后还能发送一个completed事件或一个error事件。

最后,为了使用这个signal,把下面的代码添加到viewDidLoad的最下面:

[[self requestAccessToTwitterSignal] subscribeNext:^(id x) { NSLog(@"Access granted"); } error:^(NSError *error) { NSLog(@"An error occurred: %@", error); }];​

链接signal

一旦用户允许访问Twitter账号(希望如此),应用就应该一直监测search text filed的变化,以便搜索Twitter的内容。

应用应该等待获取访问Twitter权限的signal发送completed事件,然后再订阅text field的signal。按顺序链接不同的signal是一个常见的问题,但是ReactiveCocoa处理的很好。

把viewDidLoad中当前管道的代码替换成下面的:

[[[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事件的发送,然后再订阅由then block返回的signal。这样就高效地把控制权从一个signal传递给下一个。

接下来,在管道中添加一个filter操作来过滤掉无效的输入。在本例里就是长度不够3个字符的字符串:

[[[[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); }];​

现在用图形来表示管道,就和下图类似:

管道从requestAccessToTwitterSignal 开始,然后转换为rac_textSignal。同时,next事件通过一个filter,最终到达订阅者的block。你还能看到第一步发送的error事件也是由subscribeNext:error:block来处理的。

搜索Twitter的内容

你可以使用Social Framework来获取Twitter搜索API,但的确如你所料,Social Framework不是响应式的。那么下一步就是把所需的API调用封装进signal中。你现在应该熟悉这个过程了。

RWSearchFormViewController.m中,添加下面的方法:

- (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; 
}​

方法创建了一个请求,请求通过v1.1 REST API来搜索Twitter。上面的代码使用q这个搜索参数来搜索Twitter中包含有给定字符串的微博。你可以在Twitter API 文档中来关于搜索API和其他传入参数的信息。

下一步是基于这个请求创建signal。在同一个文件中,添加下面的方法:

- (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 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 == 0) { [subscriber sendError:noAccountsError]; } else { [request setAccount:[twitterAccounts lastObject]]; // 5 - perform the request [request performRequestWithHandler: ^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) { if (urlResponse.statusCode == 200) { // 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; }];
}

分别讲一下每个步骤:

  1. 首先需要定义2个不同的错误,一个表示用户还没有添加任何Twitter账号,另一个表示在请求过程中发生了错误。
  2. 和之前的一样,创建一个signal。
  3. 用你之前写的方法,给需要搜索的文本创建一个请求。
  4. 查询account store来找到可用的Twitter账号。如果没有账号的话,发送一个error事件。
  5. 执行请求。
  6. 在请求成功的事件里(http响应码200),发送一个next事件,返回解析好的JSON数据,然后再发送一个completed事件。
  7. 在请求失败的事件里,发送一个error事件。

现在来使用这个新的signal!

在本教程的第一部分,你学过了如何使用flattenMap来把每个next事件映射到一个新的signal。现在又要用到了。在viewDidLoad的末尾更新你的管道,添加flattenMap这一步:

[[[[[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); }];

构建运行,在search text field中输入一些文字。当文本长度超过3个字符时,你应该就能在控制台看到搜索Twitter的结果了。

下面是一段你将会看到的数据:

2014-01-05 07:42:27.697 TwitterInstant[40308:5403] { 
"search_metadata" = { "completed_in" = "0.019"; count = 15; "max_id" = 419735546840117248; "max_id_str" = 419735546840117248; "next_results" = "?max_id=419734921599787007&q=asd&include_entities=1"; query = asd; "refresh_url" = "?since_id=419735546840117248&q=asd&include_entities=1"; "since_id" = 0; "since_id_str" = 0; 
}; 
statuses = ( { contributors = ""; coordinates = ""; "created_at" = "Sun Jan 05 07:42:07 +0000 2014"; entities = { hashtags = ...​

线程

我相信你已经想把搜索Twitter返回的JSON值和UI连接起来了,但是在这之前还有最后一个需要做的事情。现在需要稍微做一些探索,来看一下这到底是什么!

在subscribeNext:error:中如下图所示的地方加一个断点:

重新运行应用。如果需要的话,再次输入Twitter登录信息。在search field中输入一些内容。当在断点停止时,你应该能看到和下图类似的东西:

注意断点停在的代码并没有在主线程,也就是截图中的Thread 1中执行。请记住你只能在主线程中更新UI。因此你需要切换线程来在UI中展示微博的列表。

这展示了ReactiveCocoa框架很重要的一点。上面显示的操作会在signal最开始发送事件的线程中执行。尝试在管道的其他步骤添加断点,你可能会惊奇的发现它们也是在不同线程上执行的。

所以接下来你要怎么更新UI呢?通常的做法是使用操作队列(参见教程如何使用 NSOperations 和 NSOperationQueues)。但是ReactiveCocoa有更简单的解决办法。

像下面的代码一样,在flattenMap:之后添加一个deliverOn:操作:

[[[[[[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); }];

现在重新运行,输入一些内容,停在断点。你应该能看到subscribeNext:error:block中的代码现在实在主线程执行了。

更新UI

如果你打开RWSearchResultsViewController.h 就会发现已经有一个displayTweets:方法了,它会让右边的view controller根据提供的微博数组来展示内容。实现非常简单,就是一个标准的UITableView数据源。displayTweets:方法需要的唯一一个参数就是包含RWTweet实例的数组。RWTweet模型已经包含在初始工程里了。

subscibeNext:error:里收到的数据目前是在signalForSearchWithText:里由返回的JSON值转换得到的一个NSDictionary。所以你怎么确定字典里的内容呢?

看一下Twitter的API文档,那里有返回值的样例。NSDictionary和这个结构对应,所以你能找到一个叫“statuses”的键,它对应的值是一个包含微博的NSArray,每个条文也是NSDictionary实例。

RWTweet已经有一个类方法tweetWithStatus:,方法从NSDictionary中取得需要的数据。所以你需要的做的就是写一个for循环,遍历数组,为每条微博创建一个RWTweet实例。

但我们这次不这么做。还有更好的方法。

这篇文章是关于ReactiveCocoa和函数式编程。如果用函数式API来实现把数据从一个格式转换为另一个会优雅很多。你将会用到LinqToObjectiveC来完成这个任务。

NSArray+LinqExtensions.h头文件是LinqToObjectiveC里的,它为NSArray添加了许多方法,能让你用流式API来转换、排序、分组和过滤其中的数据。现在就来用一下

把viewDidLoad中的代码更新成下面这样的:

[[[[[[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); }];

在上面的代码中,subscribeNext:block首先获取包含微博的数组。然后linq_select方法对数组中的每个元素执行提供的block,来把NSDictionary的数组转换成RWTweet的数组。

异步加载图片

你可能注意到了每条微博的左侧有一段空隙,这是用来显示Twitter用户头像的。

RWTweet类有一个属性profileImageUrl来存放头像的URL。为了让table view能流畅地滚动,你需要让用URL获取图像的代码不在主线程中执行。你可以使用Grand Central Dispatch或者NSOperationQueue来实现。但是为什么不用ReactiveCocoa呢?

打开RWSearchResultsViewController.m,添加下面的方法:

-(RACSignal *)signalForLoadingImage:(NSString *)imageUrl { RACScheduler *scheduler = [RACScheduler schedulerWithPriority:RACSchedulerPriorityBackground]; return [[RACSignal createSignal:^RACDisposable *(id subscriber) { NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]]; UIImage *image = [UIImage imageWithData:data]; [subscriber sendNext:image]; [subscriber sendCompleted]; return nil; }] subscribeOn:scheduler]; 
}

面的方法首先获取一个后台scheduler,来让signal不在主线程执行。然后,创建一个signal来下载图片数据,当有订阅者时创建一个UIImage。最后是subscribeOn:来确保signal在指定的scheduler上执行。

现在还是在这个文件中,在tableView:cellForRowAtIndex:方法的return语句之前添加下面的代码:

cell.twitterAvatarView.image = nil; [[[self signalForLoadingImage:tweet.profileImageUrl] deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(UIImage *image) { cell.twitterAvatarView.image = image; }];

因为cell是重用的,可能有脏数据,所以上面的代码首先重置图片。然后创建signal来获取图片数据。你之前也遇到过deliverOn:这一步,它会把next事件发送到主线程,这样subscribeNext:block就能安全执行了。

节流

你可能注意到了,每次输入一个字,搜索Twitter都会马上执行。如果你输入很快(或者只是一直按着删除键),这可能会造成应用在一秒内执行好几次搜索。这很不理想,原因如下:首先,多次调用Twitter搜索API,但大部分返回结果都没有用。其次,不停地更新界面会让用户分心。

更好的解决方法是,当搜索文本在短时间内,比如说500毫秒,不再变化时,再执行搜索。

你可能也猜到了,用ReactiveCocoa来处理这个问题非常简单!

打开RWSearchFormViewController.m,在viewDidLoad中,在filter之后添加一个throttle步骤:

[[[[[[[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); }];

只有当,前一个next事件在指定的时间段内没有被接收到后,throttle操作才会发送next事件。就是这么简单。

构建运行,确认一下当停止输入超过500毫秒后,才会开始搜索。感觉比之前好一些吧?你的用户也会这么想的。

PS:本文翻译自RayWenderlich  ReactiveCocoa Tutorial – The Definitive Introduction: Part 2/2

转载于:https://www.cnblogs.com/yulang314/p/5008170.html

相关文章:

VirtualBox虚拟机安装RedHat7.3编译Linux0.01内核

引子 由于需要编译linux0.01内核,而目前的linux版本太高需要降低gcc版本等等,需要做不少调整非常不方便。 所以,直接安装RedHat7.3,这样就好编译linux0.01的内核了。 但是,安装RedHat7.3需要注意一些问题。 下载老…

远程办公是巨头游戏?十倍扩容,他们如何做到百万级并发流量

疫情发生后,除了Zoom这样深耕视频会议多年的软件,钉钉、企业微信、飞书等一大批互联网巨头也开通了免费服务,凭借着自身庞大的资源四处招揽用户。 据说,远程办公工具是2020年的第一个风口。 疫情发生后,除了Zoom这样深…

linux下使用sort命令升序、降序、随机及组合方式排序方法

示例文件:####################################################序号 优先级 字段1 字段21 5 abc def2 5 ae3 wff6 4 l…

mysql数据库备份、恢复文档

说明:为了加强线上数据库安全,避免研发人员误操作造成数据的丢失,制作本文档。一线运维人员可以参考!一、数据备份:专用数据库备份服务器,定时对数据库进行热备、冷备,即主从设置、mysqldump冷备、mysql-bin-log日志备…

Linux环境ddd安装与使用

ddd是一个优秀的调试器,安装ddd破费周折 必须安装x开发环境 1.下载 http://ftp.gnu.org/gnu/ddd/,下载最新的ddd-3.3.12.tar.gz # wget http://ftp.gnu.org/gnu/ddd/ddd-3.3.12.tar.gz # tar zxvf ddd-3.3.12.tar.gz # cd ddd-3.3.12/ 2.配置 # ./…

华为诺亚、北大提出GhostNet​,使用线性变换生成特征图,准确率超MobileNet v3 | CVPR 2020...

作者 | Kai Han, Yunhe Wang等编译 | Conv出品 | AI科技大本营(rgznai100)受限于内存空间和计算资源,将卷积神经网络部署到嵌入式设备中会比较困难。CNNs中特征图的冗余性是保证其成功的关键,但是在神经网络的结构设计中却鲜有研究…

pap和chap交叉认证

pap和chap交叉认证:R1启动pap,R2启动chap。R1上的配置:Router>enRouter#config tRouter(config)#enable s ciacoRouter(config)#line c 0Router(config-line)#pass ciacoRouter(config-line)#loginRouter(config-line)#logging syRouter(c…

如何在App中实现朋友圈功能之二快速实现用户信息的自定义——箭扣科技Arrownock...

如何在App中实现朋友圈功能之二快速实现用户信息的自定义自我关联社交元素:anSocial中很多的社交元素API,如帖子(Post)、相册(Album)、文件(File)等,这些API的可选参数中…

使用cat /proc/进程id/maps 查看进程内存映射

proc/<PID>/maps 查看进程的虚拟地址空间是如何使用的。 该文件有6列&#xff0c;分别为&#xff1a; 地址&#xff1a;库在进程里地址范围 权限&#xff1a;虚拟内存的权限&#xff0c;r读&#xff0c;w写,x,s共享,p私有&#xff1b; 偏移量&#xff1a;库在进程里地址范…

两成开发者月薪超 1.7 万、算法工程师最紧缺! | 中国开发者年度报告

整理 | 郭芮 责编 | 唐小引 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; “求知若饥&#xff0c;虚心若愚”——这个原本出自《全球概览》的俳句&#xff0c;因为乔布斯在斯坦福大学毕业演讲中的引用而备受推崇&#xff0c;流传成为 IT 界的至理名言之一。在…

怎么处理404 错误页面 、处理404页面、asp.net 处理404页面

说明 On 指定启用自定义错误。如果未指定 defaultRedirect&#xff0c;用户将看到一般性错误。 Off 指定禁用自定义错误。这允许显示标准的详细错误。 RemoteOnly 指定仅向远程客户端显示自定义错误并且向本地主机显示 ASP.NET 错误。这是默认值。 system.web 元素 下添加下边…

转载:python原生态的输入窗口抖动+输入特效

python原生态的输入窗口抖动输入特效 出处&#xff1a;https://coding.net/u/acee/p/PythonPowerInput/git/blob/master/test_power_input.py __author__ Administrator import sys from lib.qm_app import App from PyQt4.QtGui import * from PyQt4.QtCore import * import …

华为提出基于进化算法和权值共享的神经网络结构搜索,CIFAR-10上仅需单卡半天 | CVPR 2020...

作者 | VincentLee来源 | 晓飞的算法工程笔记导读&#xff1a;为了优化进化算法在神经网络结构搜索时候选网络训练过长的问题&#xff0c;参考ENAS和NSGA-III&#xff0c;论文提出连续进化结构搜索方法(continuous evolution architecture search, CARS)&#xff0c;最大化利用…

在.Net Micro Framework中显示汉字

摘要:MF平台支持的字体是专有格式&#xff0c;扩展名为tinyfnt&#xff0c;需要用专门的转化工具才能把windows平台上的字体转换为tinyfnt字体。在.Net Micro Framework SDK中提供了一个叫做TFConvert.exe的工具&#xff0c;我们可以用它在命令行下将PC机上的TrueType或者OpenT…

汇编语言使用C库函数和Linux动态链接

使用printf 代码 #cpuid2.s -- Using C labrary calls .section .data output: .asciz "The processor Vender is %s\n".section .bss .lcomm buffer, 12 .section .text .globl _start _start: movl $0, %eax cpuid …

springJDBC实现查询方法二

无废话&#xff0c;看代码&#xff1a; Overridepublic List<Sites> queryAllSites(Pager pager) {String sql "select * from sakai_site order by SITE_ID limit ?,?";Object[] obj new Object[]{pager.getStart(),pager.getLimit()};List<Sites> …

全球计算机视觉顶会CVPR 2020论文出炉:腾讯优图17篇论文入选

全球计算机视觉顶级会议CVPR2020 (IEEE Conference on Computer Vision and Pattern Recognition&#xff0c;即IEEE国际计算机视觉与模式识别会议) 即将于2020年6月14日-19日在美国西雅图召开。本届大会总共录取来自全球论文1470篇&#xff0c;腾讯优图实验室入选17篇。 作为…

gcc使用总结

1.基本选项 -o 指定输出文件名。如果不给出这个选项&#xff0c;gcc就给出预设的可执行文件a.out。 # cc -o XX XX.c -c 编译、汇编到目标代码&#xff0c;不进行链接 -v 打印较多信息&#xff0c;显示编译器调用的程序。 -E 仅作预处理&#xff0c;不进行编译、汇编…

websecurity - Web Security Testing Framework 超级牛B扫描器

Windows – Websecurify 0.3.exehttp://websecurify.googlecode.com/files/Websecurify%200.3.exeLinux – Websecurify 0.3.tgzhttp://websecurify.googlecode.com/files/Websecurify%200.3.tgzMac – Websecurify 0.3.dmghttp://websecurify.googlecode.com/files/Websecurif…

C中的qsort函数和C++中的sort函数的理解与使用

一、qsort()函数 原型&#xff1a;_CRTIMP void __cdecl qsort (void*, size_t, size_t,int (*)(const void*, const void*)); 参数解释&#xff1a;1、待排序数组首地址&#xff1b;2、数组中待排序元素数量&#xff1b;3、各元素的占用空间的大小&#xff1b;4、指向函数的指…

机器学习新闻综述:2019年AI领域不得不看的6篇文章

作者 | Limarc Ambalina翻译 | 火火酱&#xff0c;编辑 | Carol出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;在这篇机器学习新闻综述中&#xff0c;我们将回顾一些2019年以来在人工智能各个领域广泛传播或产生影响的重大新闻。此外&#xff0c;我们还将…

GDB attach到进程

要调试守护进程等已经启动的进程或是调试陷于死循环的进程可以使用attach命令 格式 attach pid C语言代码 #include <stdio.h> int main(void) { int marks[10]; int i; for(i0;i<12;i) { scanf("%d",&marks[i]); …

Chrome使用技巧和编辑框拖动怪问题。

常用快捷键&#xff1a;ctrlshiftt 重新打开刚关闭的网页ctrlh 打开历史记录ctrl 放大。ShiftEscape 查看任务管理器据说Chrome能调整编辑区大小&#xff0c;我没发现。倒发现Chrome一个问题&#xff0c;选中编辑框中的文字&#xff0c;一直拖动鼠标&a…

Linux中断研究

2019独角兽企业重金招聘Python工程师标准>>> 研究linux系统&#xff0c;不管是做驱动、协议栈还是进程调度等等&#xff0c;都离不开中断。这说明&#xff0c;要想编写正确的linux代码&#xff0c;不了解中断是不行的。 话说曾几何时&#xff0c;在大学的课堂里&…

linux环境内存分配原理

Linux的虚拟内存管理有几个关键概念&#xff1a; Linux 虚拟地址空间如何分布&#xff1f;malloc和free是如何分配和释放内存&#xff1f;如何查看堆内内存的碎片情况&#xff1f;既然堆内内存brk和sbrk不能直接释放&#xff0c;为什么不全部使用 mmap 来分配&#xff0c;munm…

大脑芯片公司Neuralink计划在人脑内植入芯片,他们到底想干什么?

作者 | James Murphy翻译 | 火火酱&#xff0c;编辑 | Carol出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;说实话&#xff0c;科幻电影在遇到Neuralink时也不得不甘拜下风。2019年7月&#xff0c;埃隆马斯克(Elon Musk)宣布&#xff0c;他的公司正在研发…

判断链表是否存在环(及其延伸)

有一个单链表&#xff0c;其中可能有一个环&#xff0c;也就是某个节点的next指向的是链表中在它之前的节点&#xff0c;这样在链表的尾部形成一环。问题&#xff1a;1、如何判断一个链表是不是这类链表&#xff1f;2、如果链表为存在环&#xff0c;如果找到环的入口点&#xf…

iOS跳转到各种系统设置界面

定位服务 定位服务有很多APP都有&#xff0c;如果用户关闭了定位&#xff0c;那么&#xff0c;我们在APP里面可以提示用户打开定位服务。点击到设置界面设置&#xff0c;直接跳到定位服务设置界面。代码如下&#xff1a; //定位服务设置界面 NSURL *url [NSURL URLWithString:…

Linux内存管理大图(第三稿)

网友画的还不错就转了 &#xff0c;该作者一共画了3版 v0.1 v0.2 v0.3 原文地址&#xff1a;http://bbs.chinaunix.net/thread-2018659-1-1.html

VNC的安装与使用

VNC的安装与使用。 说明&#xff1a;文章内容比较简单&#xff0c;献给那些初学者作为参考。 文章分为两部分&#xff0c;第一部分为VNC简介&#xff0c;第二部分为VNC的安装与使用。 文章为小弟结合书籍与小弟的实际操作总结出来的&#xff0c;如有错误与疏漏之处…