割と簡単にできてびっくりしている。
やりたかったこと
Delegateメソッドを書かずに、CLLocationManagerを使いたかった。
通常位置情報を扱う場合は、CLLocationManagerのdelegateを登録し、ViewController等にDelegateメソッドを追加して実装します。ですがこの方法は王道ですが、メソッドが増えたりソースコード的に書きにくい読みにくい等あってあまり好きではありません。なので、Delegateを使わずにやってみたいと思ってました。
同じようなニーズを、UIAlertView等のiOS標準のクラスでやっているのがBlocksKitです。
iOSコーディングスタイルを変えてしまうBlocksKitの紹介
BlocksKitがやってること
BlocksKitはUIAlertViewのDelegateをblockで置き換えてます。どうやっているのかソースコードを見ると、A2DynamicDelegateというライブラリを使っていました。
UIAlertView+BlocksKit.m
A2DynamicDelegate←BlocksKitに同梱
仕組みが分からないけどパクってみる
A2DynamicDelegateの仕組みは、今の脳みそでは難しくて理解できません。ですがBlocksKitでの使い方をパクりながら、blockを使ったCLLocationManagerを実装してみました。
# import <Foundation/Foundation.h>
# import <CoreLocation/CoreLocation.h>
@interface CLLocationManager(block)
# pragma mark - Responding to Location Events
//– locationManager:didUpdateLocations:
@property (nonatomic, copy) void (^didUpdteLocations)(CLLocationManager *,NSArray *);
@end
# import "CLLocationManager+block.h"
# import "A2BlockDelegate.h"
@interface A2DynamicCLLocationManagerDelegate : A2DynamicDelegate
@end
@implementation A2DynamicCLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
id realDelegate = self.realDelegate;
if (realDelegate && [realDelegate respondsToSelector:@selector(locationManager:didUpdateLocations:)])
[realDelegate locationManager:manager didUpdateLocations:locations];
void (^block)(CLLocationManager *,NSArray *) = [self blockImplementationForMethod:_cmd];
if (block)
block(manager, locations);
return;
}
@end
@implementation CLLocationManager(block)
@dynamic didUpdteLocations;
+ (void)load {
@autoreleasepool {
[self registerDynamicDelegate];
[self linkDelegateMethods: @{
// self property : Delegate methods
@"didUpdteLocations": @"locationManager:didUpdateLocations:",
}];
}
}
@end
呼び出し元のViewControllerを書きます。
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.manager = [[CLLocationManager alloc] init];
self.manager.didUpdteLocations = ^(CLLocationManager *m,NSArray *a){
NSLog(@"%@", [a objectAtIndex:0]);
};
[self.manager startUpdatingLocation];
}
エミュレータを起動して、NSLogがはき出される事を確認しました。
拍子抜けするぐらいあっさり動きました。
ソースコードがすごいシンプルになる可能性
位置情報を扱うアプリのControllerは、Delegateメソッドが多数生えて「下に長く」なっていきます。ですがDelegateの代わりにblockを使うこの方法ならば、シンプルに書いていけそうです。
おまけ:BlocksKit入れるならCocoaPodが楽
platform :ios, '5.0'
pod 'BlocksKit'
を用意してpod install