Don't use accessor methods in init and dealloc

苹果在 WWDC 2012 Session 413 - Migrating to Modern Objective-C 里强调不要在 init 和 dealloc 里使用 accessor methods:

Always use accessor methods. Except in initializer methods and dealloc.

之前没有注意过这种情况,稍微搜索学习了一下。文档 Memory Management 里确实有这说法:

The only places you shouldn’t use accessor methods to set an instance variable are in initializer methods and dealloc.

提倡下面这种写法:

- (id)init {
    self = [super init];
    if (self) {
        _count = [[NSNumber alloc] initWithInteger:0];
    }
    return self;
}

- (id)initWithCount:(NSNumber *)startingCount {
    self = [super init];
    if (self) {
        _count = [startingCount copy];
    }
    return self;
}

dealloc 不能用比较好理解,self.property 是向 property 发了一个消息,有可能该对象的生命周期已经结束,不能再接受消息。init 不能用比较靠谱的说法是如果有 subClass 并重载了 accessor,那么 init 里 self.property 就无效;另外也可能会有其他影响,比如 KVC notifications 等。

SO 参考帖子:

  1. Should I refer to self.property in the init method with ARC?
  2. Using properties to access iVars in init?
  3. Initializing a property, dot notation
  4. Objective-C Dot Syntax and Init

What I have learned from Cheddar for iOS

  1. Code Style.
  2. DRY,整理适合自己的代码库(SSToolkit)。
  3. application:didFinishLaunchingWithOptions: 里尽量少操作,减少 launch 时间。只做界面展示工作,数据层用 dispatch_async 异步操作。
  4. 多用 [image stretchableImageWithLeftCapWidth:5 topCapHeight:0] 图片拉伸,减小 App size。效果上并没有缺失很多。很多效果都可以用代码实现,不一定非得贴图。
  5. 数据层封装不同的对象,方便各种调用。直接用 dict 传来传去不够清晰。
  6. Core Data 和 UIViewController 可以很好的结合,深度封装后的确很方便,参见 SSManagedViewController = UIViewController+SSManagedObject(NSManagedObject),SSDataKit。但这样感觉 ViewController 很沉重,也可能是因为我对 Core Data 不熟悉,以后有机会加深一下 CD 的学习使用。
  7. KVO 是个好东西。
  8. 定义一些内部 scheme 来做界面跳转,x-cheddar-tag.
  9. UIColor+CheddariOSAdditions.h-cheddarTextColor,定义整体风格配色,很方便使用。UIFont+CheddariOSAdditions.h 同理。
  10. cellHeightForText:dispatch_once_t 生成一个单例 label,然后 sizeThatFits: 计算。
  11. prepareForReuse 释放数据。
  12. 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

  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 的用户需要做很多处理,总体来说不值当。