月度归档:2015年06月

静态lib使用注意事项

最近做的项目用了一个三方包,纯C++的,打包成的静态文件,这种包需要注意的地方在于需要引入libstdc++库,然后还需要在build settings中将Language C++中的C++ Standard Library修改为libstdc++(GNU C++ standard)。
另外的就是基本规则,使用的地方将后缀名修改为mm。

几个常用的命令
lipo -info your.a 查看lib支持的架构.
lipo create i386.a arm64.a -output universal.a 将多个lib合并为一个通用lib,方便调试,当然上架肯定得用最小的lib了。

FMDB小技巧

FMDB,这个库我也用了很多年了,早些的版本在多线程使用时会报错,不能在不同的线程使用db操作。我自己用单例独立线程出处理这些问题。现在就方便多了,使用GCD,之前介绍GCD说过,fmdb使用一个独立的串行队列,我只要专注于对模型和DAO的编写就足够了。

_queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL);
dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL);

在使用的地方(inDatabase)做了断言

FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey);
    assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock");

使用方式举例

- (WWGroupObject *)selectByGroupId:(NSInteger)groupId
{
__block WWGroupObject *obj = nil;
FMDatabaseQueue *dbQueue = [[WWDatabaseHelper sharedInstance] dbQueue];
[dbQueue inDatabase:^(FMDatabase *db) {
    FMResultSet *rs = [db executeQuery:[self setTable:SELECT_GROUPID_ROSTER], [NSNumber numberWithInteger:groupId]];
    while ([rs next]) {
        obj = [[WWGroupObject alloc] init];
        obj.groupId = [rs intForColumn:@"id"];
        obj.groupName = [rs stringForColumn:@"groupName"];
        obj.groupType = [rs intForColumn:@"groupType"];
        }
    }];
  return obj;
}

因为它是用的dispatch_sync,整个是串行操作,就不会出现不返回数据的情况,之前我还用dispatch_semaphore_t
来判断block是否执行结束然后再返回数据,其实没必要的,串行队列一定是执行完block才return的。

早些版本我是写一个DatabaseHelper单例类保存一个FMDatabase实例,现在是单例类保存一个FMDatabaseQueue实例,它是使用路径初始化的,执行一套sql建表文件executeStatements和更新版本做的一些增删字段的sql语句。

写一堆各种模型,对应数据库表的。

在写一些DAO模块,对应增删改查,DAO使用单例helper的queue执行inDatabase方法,去调用executeQuery执行语句。
这种方式就是看起来比较直观,很容易分模块给别人编写,使用时隐藏了sql,如- (void)insertTitle:(WWTitleObject *)obj,只要初始化这个obj就可以。
安卓下有GreenDao,更方便写模型。

iOS之Widget开发总结

上月底,给crm项目加了个widget,在Today这里展示。

首先打开project,创建target,选择Today Extension,选择关联的project和target。扩展只能依托一个某个target才能存在。

创建好这一切后,我是比较偷懒,直接将需要的代码关联这个target,并没有使用framework去弄。

关联完毕后,有些地方代码需要做target判断,注意一点UIApplication无法在这里面使用需要用self.extensionContext。他只有上下文,比如通过extension打开主应用.

[self.extensionContext openURL:[NSURL URLWithString:@"iOSWidgetApp://123"] completionHandler:^(BOOL success) {
    NSLog(@"open url result:%d",success);
}];

extension是在你手指下拉出这个面板才回刷新数据

- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
    completionHandler(NCUpdateResultNewData);
    [self loadData];
    }

还有一个问题就是共享数据,选择主target,标签Capabilities,找到App Groups,打开开关,添加一个group,举例叫group.icrm,保存。
在extension的这个位置打开开关,勾选group.icrm
配置完成后再程序中使用路径的位置,对于crm项目是db索索在位的初始化修改一下:

     NSURL *storeURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.icrm"];
storeURL = [storeURL URLByAppendingPathComponent:@"icrm.db"];

这样在exension中也能访问到db。因为Today Extension是继承自UIViewController,UIKit的用法都差不多。

几个配置地方,我习惯代码开发,所以需要修改info.plist

<dict>
	<key>NSExtensionPointIdentifier</key>
	<string>com.apple.widget-extension</string>
	<key>NSExtensionPrincipalClass</key>
	<string>TodayViewController</string>
</dict>

target general的Main interface也置为空。

UITableView的size问题,因为todo都是列表,动态高度使用下方这种方式,在loadData时设置widget在面板上的size大小。

    self.preferredContentSize = CGSizeMake(self.view.bounds.size.width, _dataArray.count * 50.0f);

URLScheme,在主target中设置好了才能在extension中使用openURL打开主App。

<key>CFBundleURLTypes</key>
<array>
	<dict>
		<key>CFBundleTypeRole</key>
		<string>Todo</string>
		<key>CFBundleURLName</key>
		<string>com.highwe.icrm</string>
		<key>CFBundleURLSchemes</key>
		<array>
			<string>iOSWidgetApp</string>
		</array>
	</dict>
</array>

NSURLConnection NSURLSession

Mapping of NSURLConnection to NSURLSession delegate methods. Created by Mattt Thompson.

https://gist.github.com/floriankugler/6870499

objcio之NSURLSession

新的项目换了NSURLSession,使用Task管理,跟之前相比,我觉得显而易见的好处就是集成block回调加GCD,代码更清晰直观。

使用全局的NSURLSessionConfiguration配置全局session更方面,一般没什么要配置的,使用default就足够,基本就是写个单例类,继承AFHTTPSessionManager,使用默认初始化就可以。

三个不同类型的NSURLSessionTask:NSURLSessionDataTask、NSURLSessionUploadTask、NSURLSessionDownloadTask涵盖了网络请求基本功能,上传,下载,获取Data。

我现在做网络的方式就是使用AFNetworking,写单例继承自AFHTTPSessionManager,每个Task使用sharedInstance来POST或者GET。

[WWFriend loadAllTimelineWithParameters:parameters
                         andHandler:^(NSArray *response, NSError *error) {
                             _timelineArray = response;
                             WWLog(@"%@", _timelineArray);
                             [_tableView reloadData];
                         }];

AFNetworking的AFHTTPSessionManager已经封装好了NSURLSessionDataTask相关参数,并且已经调用[task resume]了。所以使用只需要使用类方法返回一个NSURLSessionDataTask即可。操作简单,代码直观。

注意事项:定义多个不同类型的block用作completionHandler;对于失败处理尽量在网络层处理好;返回的数据尽量先在网络层解析完成再作为handler的参数返回到视图上;注意handler为空的判断。