iOS Background Task Notes

iOS 4+ 支持 audio、location、voip 后台常驻任务,除此以外 App 还可以向系统申请额外一段时间(十分钟)在后台执行某些任务,比如进入后台后发送操作日志等。

注册消息通知,或者直接实现 - (applicationDidEnterBackground:(UIApplication *)application delegate。

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(appDidEnterBackground)
                                             name:UIApplicationDidEnterBackgroundNotification
                                           object:nil];

向系统申请 background task 并执行:

- (void)appDidEnterBackground
{
    if (![UIDevice currentDevice].multitaskingSupported) {
        return;
    }

    UIApplication *app = [UIApplication sharedApplication];
    __block UIBackgroundTaskIdentifier bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            if (bgTask != UIBackgroundTaskInvalid) {
                [app endBackgroundTask:bgTask];
                bgTask = UIBackgroundTaskInvalid;
            }
        });
    }];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //Do tasks you want.

        dispatch_async(dispatch_get_main_queue(), ^{
            if (bgTask != UIBackgroundTaskInvalid) {
                [app endBackgroundTask:bgTask];
                bgTask = UIBackgroundTaskInvalid;
            }
        });
    });
}

注意:beginBackgroundTaskWithExpirationHandler: 生成的 task 在执行完以后必须要用 endBackgroundTask: 告诉系统任务已结束,不然在申请时间用完以后 App 会被系统直接终止,而不是挂起(suspended)。

20120730

公司最早的一批人,就剩我了。

相信她们的选择,祝福她们的明天,可心里还是难受。

你们一定要过的比今天好。

Symbol not found: _objc_storeStrong

Crash log:

dyld: lazy symbol binding failed: Symbol not found: _objc_storeStrong
  Referenced from: /var/mobile/Applications/6E4A4771-B39A-48B9-A7B7-0EA0108DCAF4/X.app/X
  Expected in: /usr/lib/libobjc.A.dylib

dyld: Symbol not found: _objc_storeStrong
  Referenced from: /var/mobile/Applications/6E4A4771-B39A-48B9-A7B7-0EA0108DCAF4/X.app/X
  Expected in: /usr/lib/libobjc.A.dylib

在 Non-ARC 项目中使用 ARC-enabled 库的时候,需要对库文件在 Build Phases->Compile Sources 添加 -fobjc-arc Compiler Flags,在 Build Settings->Other Linker Flags 添加 -fobjc-arc.

via libobjc.A.dylib compile error on iOS 4.3, Static library with ARC support linked to non-ARC project causing linker errors.

UISegmentedControl error on iOS 4.x

Code 1:

self.segmentedControl = [[[UISegmentedControl alloc] init] autorelease];
[_segmentedControl setSelectedSegmentIndex:0];
[_segmentedControl addTarget:self
                      action:@selector(segmentedControlSwitch)
            forControlEvents:UIControlEventValueChanged];

Code 2:

self.segmentedControl = [[[UISegmentedControl alloc] init] autorelease];
[_segmentedControl addTarget:self
                      action:@selector(segmentedControlSwitch)
            forControlEvents:UIControlEventValueChanged];
[_segmentedControl setSelectedSegmentIndex:0];

注意 [_segmentedControl setSelectedSegmentIndex:0]; 的位置,在 iOS 4.x 下,Code 2 代码在设置 selectedSegmentIndex 的时候会执行一次 segmentedControlSwitch 方法。在 iOS 5+ 没有这个问题。

随想

当你坚持着自己的想法在做一件自己想做事情的时候,当你坚持着自己的原则拒绝去做一些事情的时候,总会有一些声音有意无意地告诉你:你这个傻逼,这个社会就是这样。

这个社会太浮躁了现在,巨大的通胀让每个人都想着去赚快钱,大家都很难静下心来做一件让自己开心的事情。嗯,让自己开心的事情。

Tim O’Reilly:

Money is like gasoline during a road trip. You don’t want to run out of gas on your trip, but you’re not doing a tour of gas stations. You have to pay attention to money, but it shouldn’t be about the money.

UITaleView 多选

效果就是 cell.contentView 右移,左侧留一空圆,点击选中,再点取消选中。

[_rootTable setEditing:YES animated:YES]; 进入多选,然后实现 delegate:

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
	// 关键所在
    return UITableViewCellEditingStyleDelete | UITableViewCellEditingStyleInsert;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (tableView.editing) {
        if (_dataArray && [_dataArray count]>indexPath.row) {
            NSDictionary *dict = [_dataArray objectAtIndex:indexPath.row];
            [_pickedArray addObject:dict];
        }
    }
}

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (tableView.editing) {
        if (_dataArray && [_dataArray count]>indexPath.row) {
            NSDictionary *dict = [_dataArray objectAtIndex:indexPath.row];
            [_pickedArray removeObject:dict];
        }
    }
}

Preview and Copy text from QuickLook

  1. Enable QuickLook for all plain text file with QLStephen
  2. Open Terminal and run following code:

    defaults write com.apple.finder QLEnableTextSelection -bool true; killall Finder

  3. Done.

via 1, 2

iPad OS version history

iPad 的系统版本历史 (via iOS version history):

  1. 3.2/3.2.1/3.2.2,iPad Only.
  2. 4.2/4.2.1
  3. 4.3.x
  4. 5.x
  5. 6.x

iPad 的系统版本并不是连续的,3.2 是上市时的版本,iPad 独享。iOS 里程碑 4.0 刚开始并不支持 iPad,直到 4.2 才支持。4.3 随 iPad 2 发布,之后的 iOS 都是全设备支持。

所以做 iPad App 时系统要求可以从 4.2 起步,为了兼容 3.2 的用户需要做很多处理,总体来说不值当。

iPhone 数据存储

稍微总结一下 iPhone 的数据存储。常见的方式有这么几种:

  1. NSUserDefaults
  2. SQLite
  3. Core Data
  4. iCloud
  5. NSKeyedArchiver/NSKeyedUnarchiver
  6. Keychain
  7. UIPasteboard
  8. writeToFile:

NSUserdefaults

NSUserdefaults 是最为常见的方式,通常用来保存简单的数据,比如 App 设置等。数据保存在 $App/Library/Preferences/$BundleID.plist

// 存
[[NSUserDefaults standardUserDefaults] setInteger:100 forKey:@"MaxCount"];
[[NSUserDefaults standardUserDefaults] synchronize];

// 取
[[NSUserDefaults standardUserDefaults] stringForKey:@"StringKey"];

存数据时候最好 [[NSUserDefaults standardUserDefaults] synchronize]; 来及时保存。

SQLite

iPhone 自带了 SQLite 数据库,可以用来存储大数据量的持久化数据,比如 Google Reader 类阅读器缓存的文章内容。SQLite 直接操作 Api 很复杂,一般都会选用一些开源的 wrapper,比如 FMDB.

Core Data

Core Data 是苹果官方推荐的数据存储方式,底层也是拿 SQLite 做持久化。目前还没有在项目中实践过 Core Data,不多说,官方文档 Introduction to Core Data Programming Guide.

iCloud

iCloud 是 iOS 5 带来的新特性,云端同步是最大的优点,iOS+OS X 通用,可以拿来做一些很神奇的事情,比如 Tweetbot 通过 iCloud 同步 iPhone 和 iPad 上时间线的阅读位置。目前也没有在项目中实践过。

iCloud 可以和 NSUserDefaults 配合使用,比如 MKiCloudSync,sync your NSUserDefaults to iCloud automatically。

NSKeyedArchiver

NSKeyedArchiver 可以将数据 encode 后保存成文件,或者通过 NSUserDefaults 保存;对应 NSKeyedUnarchiver 用来读取数据。适合数据结构复杂(NSArray/NSDictionary)数据量较大但又不需要用 SQLite 做持久化存储的中间缓存,比如 MKNetworkKit 的 freezeOperations 操作,很方便。

// 保存 dict 到 Library 下 Dict 文件
[NSKeyedArchiver archiveRootObject:dict
                            toFile:NIPathForLibraryResource(@"Dict")];
// 读取
NSDictionary *dict = [NSKeyedUnarchiver unarchiveObjectWithFile:NIPathForLibraryResource(@"Dict")];

Keychain

Keychain 就是钥匙串,加密保存用户帐号、密码等重要信息。推荐两个 wrapper:SSKeychain, PDKeychainBindingsController

UIPasteboard

UIPasteboard 系统剪贴板是一个非常巧妙的数据存储方式,最大的好处就是可以夸应用数据访问,比如词典应用自动读取翻译剪贴板内容,非常方便。OpenUDID 就是通过一个特殊的剪贴板来保存唯一设备字符串,这样其他 App 就可以用这个唯一标识做设备区分。

writeToFile:

writeToFile: 可以直接将数据写入到指定路径的文件中,NSArray、NSDictionary、NSData 都支持。做数据缓存用的比较多,比如 EGOCache

2012 年中总结

2012 上半年小结。

工作

去年年终总结时候说今年要加强产品锻炼,今年团队也给了这样的机会。第一次作为产品负责人带产品开发,自己却做的很不好,很不及格。产品这一块还有很多很多要学习的地方,分工、协作、沟通、整体、进度等等,在接下来的工作中还要多多学习。

上半年在技术上有了一些新的学习和积累,做了第一个真正意义上的 iPad 应用。大屏幕的体验、使用场景、使用习惯都和 iPhone 有着很大的不同,信息结构有着不一样的处理,对应交互、UI 上有很大的差异,这些差异需要更多的学习理解。开发上由于 iPad 项目是一个全新的项目,尝试了一些新的技术和框架,包括 ARC,新的网络请求方式等。新技术和框架的学习需要一定时间和成本,甚至要交学费,但是从长远来看,这些投入都是值得的,对自己的技术成长很有好处,也有利于团队技术积累。作为开发,如果现在用着和一年前一样的代码做产品,虽然看起来效果一样,但自身的成长非常有限。

作为小团队,机动灵活是自己的特点,也是和大公司竞争的优势,因此在产品开发中要尽快拍板,速度迭代,先简单快速满足 80% 用户的需求,再去精益求精「像素级」地满足剩下 20% 用户。

生活

今年生活中的重头戏就是完婚。结婚是一项庞大的工程,我和妞妞周末忙乎着婚纱照、礼服,爸妈在家忙婚礼安排、婚房装修,有时候还要远程遥控订化妆、摄影。婚礼就是两家人花费忙活,两个人受累上演给别人看得一场秀,表现着家族的威望、实力。如果你能躲过这场秀,那么恭喜你省力很多;如果不能避免,那么何不把这场秀演的更好呢?

上半年陆续有三个兄弟离职回老家工作,称不上「衣锦还乡」,但至少离家近点,毕竟我们都是念家的孩子。对于兄弟们回家工作,我都是双手赞成,虽然我自己还在坚持北漂。有时候我也会问自己到底在坚持什么,那些个梦?那些个理想?那些个自己不愿被这世界改变?我不想等我老了的时候后悔没有拼搏坚持过。

向阳说「成长就是让你从诗人变成世人」。其实,从诗人变成世人,我们都输了。在坚持做自己喜欢的事,还要记住不做让自己讨厌自己的事。最近很多事情让我越来越感受到拒绝比坚持更难。

不知道跟谁学的,现在酒喝多了就满世界打电话,还哭,太差劲。酒量也严重退步,估计结婚的时候该被灌成傻子了。

嗯,以上。