iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど
外部とのやり取りを管理する機能として、オートマティズムのコネクタを参考に、独自のカスタマイズを施している。
実際に利用していて分かった事は、個々の処理の実装は様々で、別スレッドから返ってくる場合があるが、それをメインスレッドに戻して、返す共通の機能としての役割があることだ。また、オートマティズムでは結果を通知で返していたが、これだと、要求との対応が弱くなるので、要求との対応を強くしたい場合は、要求時にBlocksを渡して、Blocksで応答する様に工夫してしてみた。
ヘッダーファイルを見てみよう。
#import <Foundation/Foundation.h>
#import "RFCResponseParser.h"
extern NSString *ConnectorDidBeginRfc;
extern NSString *ConnectorInProgressRfc;
extern NSString *ConnectorDidFinishRfc;
@interface Connector : NSObject
@property (assign, readonly, nonatomic, getter=isNetworkAccessing) BOOL networkAccessing;
+ (Connector *)sharedConnector;
- (void)rfcIndexWithCompletionHandler:(RFCResponseParserCompletionHandler)completionHandler;
- (void)rfcWithIndex:(NSUInteger)index completionHandler:(RFCResponseParserCompletionHandler)completionHandler;
- (void)cancelWithIndex:(NSUInteger)index;
- (void)cancelAll;
@end
リクエストのメソッドにcompletionHandlerというBlocksを渡せるようにして、これは、パーサに覚えさせておいて、処理が終わった際に呼ぶようにしている。
以下の実装部だ。
#import "Connector.h"
NSString *ConnectorDidBeginRfc = @"ConnectorDidBeginRfc";
NSString *ConnectorInProgressRfc = @"ConnectorInProgressRfc";
NSString *ConnectorDidFinishRfc = @"ConnectorDidFinishRfc";
@interface Connector ()
@property (strong, nonatomic) NSOperationQueue *queue;
@property (strong, nonatomic) NSMutableArray *parsers;
- (void)_notifyRfcStatusWithParser:(RFCResponseParser *)parser;
@end
@implementation Connector
@synthesize queue = _queue;
@synthesize parsers = _parsers;
+ (Connector *)sharedConnector
{
static Connector *_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[Connector alloc] init];
});
return _sharedInstance;
}
- (id)init
{
DBGMSG(@"%s", __func__);
self = [super init];
if (self) {
_queue = [[NSOperationQueue alloc]init];
_parsers = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc
{
DBGMSG(@"%s", __func__);
self.queue = nil;
self.parsers = nil;
}
- (BOOL)isNetworkAccessing
{
return self.parsers.count > 0;
}
- (void)rfcIndexWithCompletionHandler:(RFCResponseParserCompletionHandler)completionHandler
{
DBGMSG(@"%s", __func__);
[self rfcWithIndex:0 completionHandler:completionHandler];
}
- (void)rfcWithIndex:(NSUInteger)index completionHandler:(RFCResponseParserCompletionHandler)completionHandler
{
DBGMSG(@"%s", __func__);
BOOL networkAccessing = self.networkAccessing;
RFCResponseParser *parser = [[RFCResponseParser alloc] init];
parser.index = index;
parser.queue = self.queue;
parser.delegate = self;
parser.completionHandler = completionHandler;
[parser parse];
if (parser.error) {
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:parser forKey:@"parser"];
[[NSNotificationCenter defaultCenter] postNotificationName:ConnectorDidFinishRfc
object:self
userInfo:userInfo];
if (parser.completionHandler) {
parser.completionHandler(parser);
}
return;
}
[self.parsers addObject:parser];
if (networkAccessing != self.networkAccessing) {
[self willChangeValueForKey:@"networkAccessing"];
[self didChangeValueForKey:@"networkAccessing"];
}
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:parser forKey:@"parser"];
[[NSNotificationCenter defaultCenter] postNotificationName:ConnectorDidBeginRfc object:self userInfo:userInfo];
}
- (void)cancelWithIndex:(NSUInteger)index
{
DBGMSG(@"%s", __func__);
NSArray *parsers = [self.parsers copy];
for (RFCResponseParser *parser in parsers) {
if (parser.index == index) {
[parser cancel];
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:parser forKey:@"parser"];
[[NSNotificationCenter defaultCenter] postNotificationName:ConnectorDidFinishRfc
object:self
userInfo:userInfo];
[self willChangeValueForKey:@"networkAccessing"];
[self.parsers removeObject:parser];
[self didChangeValueForKey:@"networkAccessing"];
}
}
}
- (void)cancelAll
{
DBGMSG(@"%s", __func__);
for (RFCResponseParser *parser in self.parsers) {
[parser cancel];
}
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:self.parsers forKey:@"parsers"];
[[NSNotificationCenter defaultCenter] postNotificationName:ConnectorDidFinishRfc object:self userInfo:userInfo];
[self willChangeValueForKey:@"networkAccessing"];
[self.parsers removeAllObjects];
[self didChangeValueForKey:@"networkAccessing"];
}
- (void)parser:(RFCResponseParser*)parser didReceiveResponse:(NSURLResponse*)response
{
DBGMSG(@"%s", __func__);
}
- (void)parser:(RFCResponseParser *)parser didReceiveData:(NSData *)data
{
DBGMSG(@"%s", __func__);
}
- (void)parserDidFinishLoading:(RFCResponseParser *)parser
{
DBGMSG(@"%s", __func__);
if ([self.parsers containsObject:parser]) {
[self _notifyRfcStatusWithParser:parser];
}
}
- (void)parser:(RFCResponseParser *)parser didFailWithError:(NSError*)error
{
DBGMSG(@"%s", __func__);
if ([self.parsers containsObject:parser]) {
[self _notifyRfcStatusWithParser:parser];
}
}
- (void)parserDidCancel:(RFCResponseParser *)parser
{
DBGMSG(@"%s", __func__);
if ([self.parsers containsObject:parser]) {
[self _notifyRfcStatusWithParser:parser];
}
}
- (void)_notifyRfcStatusWithParser:(RFCResponseParser *)parser
{
DBGMSG(@"%s", __func__);
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:parser forKey:@"parser"];
[[NSNotificationCenter defaultCenter] postNotificationName:ConnectorDidFinishRfc
object:self
userInfo:userInfo];
if (parser.completionHandler) {
parser.completionHandler(parser);
}
[self willChangeValueForKey:@"networkAccessing"];
[self.parsers removeObject:parser];
[self didChangeValueForKey:@"networkAccessing"];
}
@end
第62回のCocoa勉強会(関東)が開催された。内容は、以下のとおり。
前回の勉強会で、NSURLConnectionにQueueを設定して主スレッド以外で動作させる方法を知り、早速、活用して喜んでいたのだが、NSURLSessionによって、もはや、過去のお話になってしまった事を知ってしまった。早速、活用してみよう。
APIの前方互換について、まだ、必要に迫られた事はないが、次のプロジェクトに必要になる可能性があるので復習を。
CloagのModules、ちょうど、フレームワークを作っているので、考慮してみよう。
Cocoaでマルチウィンドウ。最近、OS Xアプリケーションを作っていないので、作りたくなってきました。
古いシミュレータの捨て方、自分の環境を確認してみたところ、キャッシュにゴミが残っていた!これは助かる情報だ。
次回は、12月7日に松戸で開催予定だ。