What I have learned from Cheddar for iOS
- Code Style.
- DRY,整理适合自己的代码库(SSToolkit)。
application:didFinishLaunchingWithOptions:里尽量少操作,减少 launch 时间。只做界面展示工作,数据层用 dispatch_async 异步操作。- 多用
[image stretchableImageWithLeftCapWidth:5 topCapHeight:0]图片拉伸,减小 App size。效果上并没有缺失很多。很多效果都可以用代码实现,不一定非得贴图。 - 数据层封装不同的对象,方便各种调用。直接用 dict 传来传去不够清晰。
- Core Data 和 UIViewController 可以很好的结合,深度封装后的确很方便,参见 SSManagedViewController = UIViewController+SSManagedObject(NSManagedObject),SSDataKit。但这样感觉 ViewController 很沉重,也可能是因为我对 Core Data 不熟悉,以后有机会加深一下 CD 的学习使用。
- KVO 是个好东西。
- 定义一些内部 scheme 来做界面跳转,
x-cheddar-tag. UIColor+CheddariOSAdditions.h-cheddarTextColor,定义整体风格配色,很方便使用。UIFont+CheddariOSAdditions.h同理。cellHeightForText:用dispatch_once_t生成一个单例 label,然后sizeThatFits:计算。prepareForReuse释放数据。CDKHTTPClient学习 AFN 的好例子。单实例,用 block 封装接口。Block is better than delegate, simple, clear and powerful.
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
- Enable QuickLook for all plain text file with QLStephen
- Open Terminal and run following code:
defaults write com.apple.finder QLEnableTextSelection -bool true; killall Finder
- Done.
iPad OS version history
iPad 的系统版本历史 (via iOS version history):
- 3.2/3.2.1/3.2.2,iPad Only.
- 4.2/4.2.1
- 4.3.x
- 5.x
- 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 的数据存储。常见的方式有这么几种:
- NSUserDefaults
- SQLite
- Core Data
- iCloud
- NSKeyedArchiver/NSKeyedUnarchiver
- Keychain
- UIPasteboard
- 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。