NSURLConnection 在页面滑动时继续执行

这篇笔记只是为了清掉 BlogTodos,实际开发中用了 AFN 等是不会遇到这个问题。当然也可以作为原理理解。

首先是 NSRunLoop,我的理解 runloop 就是 iOS 的消息循环处理机制,响应处理各种消息事件。runloop 有不同的执行模式,不同模式下只会响应处理该模式类型的事件。App 运行时会有一个主线程 mainRunLoop,在程序中可以用 [NSRunloop currentRunLoop] 简单获取当前的 runloop。

NSURLConnection 在网络请求的时候(无论主线程还是子线程),如果有点击或滑动界面,网络请求会被暂停执行,直到滑动等操作结束。这是因为 NSURLConnection 默认是 NSDefaultRunLoopMode,也就是说只会在该模式下执行,当有滑动、点击界面等操作的时候,runloop 会切换到 NSEventTrackingRunLoopMode 来处理界面操作,这时候网络请求就会被暂停执行,直到界面操作结束,runloop 模式切换回去。

解决方法就是设置 NSURLConnection 的执行模式为 NSRunLoopCommonModes,这样就会在所有模式下持续执行:

[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

NSRunLoop Class Reference:

NSDefaultRunLoopMode - The mode to deal with input sources other than NSConnection objects.

NSRunLoopCommonModes - Objects added to a run loop using this value as the mode are monitored by all run loop modes that have been declared as a member of the set of “common” modes.

参考 NSURLConnection: How to avoid blocked file loading during scrolling of UIScrollView or UITableView

NSInvocation Notes

iOS 中一般用 performSelector 系列方法调用某个对象的方法消息,但是参数过多就不太方便,这时候就可以用 NSInvocation。一个简单的例子:

NSMethodSignature *sig = [self methodSignatureForSelector:@selector(addAlbum:atIndex:)];
NSInvocation *action = [NSInvocation invocationWithMethodSignature:sig];
[action setTarget:self];                            //0
[action setSelector:@selector(addAlbum:atIndex:)];  //1
[action setArgument:&deletedAlbum atIndex:2];       //2
[action setArgument:&currentAlbumIndex atIndex:3];  //3
[action retainArguments];
[action invoke];

NSMethodSignature 直译就是方法签名,保存了方法的参数类型和返回值信息 (type information for the arguments and return value of a method)。 通过方法签名信息就可以完整构建一个 invocation,对各个参数进行赋值后激活执行,也就完成了对象方法调用。

再看 NSMethodSignature 一段文档:

Indices begin with 0. The hidden arguments self (of type id) and _cmd (of type SEL) are at indices 0 and 1; method-specific arguments begin at index 2.

NSInvocation 第一步设定 Target(0),第二步设定 Selector(1),然后从 index 2 开始依次对参数赋值,因为 0/1 已被 Target/Selector 占用。要注意赋值的时候传的都是 指针 ,如果赋值参数可能会被释放,要记得 retainArguments。如果需要 NSInvocation 执行后的返回值:

NSString *returnString = nil; //假定返回值类型为 NSString
[action getReturnValue:&returnString];

最后附上 Three20 里用 NSInvocation 实现多参数 performSelector:

https://github.com/facebook/three20/blob/1.0.12/src/Three20Core/Sources/NSObjectAdditions.m#L89

- (id)performSelector:(SEL)selector
           withObject:(id)p1
           withObject:(id)p2
           withObject:(id)p3
           withObject:(id)p4
           withObject:(id)p5
{
    NSMethodSignature *sig = [self methodSignatureForSelector:selector];
    if (sig) {
        NSInvocation* invo = [NSInvocation invocationWithMethodSignature:sig];
        [invo setTarget:self];
        [invo setSelector:selector];
        [invo setArgument:&p1 atIndex:2];
        [invo setArgument:&p2 atIndex:3];
        [invo setArgument:&p3 atIndex:4];
        [invo setArgument:&p4 atIndex:5];
        [invo setArgument:&p5 atIndex:6];
        [invo invoke];

        if (sig.methodReturnLength) {
            id anObject;
            [invo getReturnValue:&anObject];
            return anObject;
        } else {
            return nil;
        }
    } else {
        return nil;
    }
}

MBP 不能充电解决

MBP 电源适配器不能充电,各种插拔都无效,真以为要悲剧了,搜到一个办法:

苹果的电源适配器在电流过大或电压不稳的情况下会自动启用保护机制,切断电流以保护电脑,所以才会充不进电。而解决方法非常简单,只需将 MagSafe 电源适配器拔出电源,静置 60秒以上,就可以重置电源适配器。

简单重置后果然有效,留记。

Dev 放权

最近的一点反思。

着重技术往 Dev Leader 转型的过程中,老是做不到技术放权。新需求和问题过来第一反应往往是自己怎么去做,原因有二:

  1. 不自信。自己技术不懂不熟,所以想借实战强化;刚学到一个新技术点想实用一次,毕竟技术性调研和实际项目应用差别还是很大。这种情况多出现在新项目。
  2. 不信任。对事不对人的说,项目紧,其他人对业务不是特别熟悉,所以很不放心把东西交出去做。

结果就是自己排了很多 tickets,很容易拖累进度,而其他人对业务继续不熟悉。

以后要加多 Code Reivew,前期系统设计多参与,后期实现时多跟进把控,具体实现大家一起完成,强化整体架构能力。

StatusBar in iOS 7

iOS 7 下状态栏默认是白底黑字,如果应用是黑色背景整个状态栏就啥也看不见。解决办法:

  1. plist 设置 UIViewControllerBasedStatusBarAppearance NO.
  2. [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

多 UIScrolllView 下点击状态栏回到顶部

iOS 下 UITableView/UIScrollView 有个特性:点击状态栏回到顶部。如果当前 view 下有多个 scrollView,或者多个 tableView 嵌套,点击回到顶部就无效,因为系统不知道该响应哪个,索性就全部禁用。文档:

On iPhone, we execute this gesture only if there’s one on-screen scroll view with scrollsToTop == YES. If more than one is found, none will be scrolled.

找到了原因解决也就很简单:只保留需要点击回到顶部 scrollView.scrollsToTop = YES,其他全部禁用。

就这么简单的 tip 我也是最近才知道,而解决办法就在官方文档里,so RTFM first.

终端下 Vim 粘贴缩进错乱

终端下 Vim 粘贴代码时会有缩进错乱,原因是终端下的 Vim 是通过模拟用户输入来完成粘贴操作,所以缩进就错乱了。解决方法是每次粘贴前 set paste,完成后 set nopaste,嫌麻烦的话可以设置一个快捷键来切换 paste 状态 set pastetoggle=<F2>.

另外 Vim 下 Ctrl-C 和 ESC 根本不是一码事,Ctrl-C 不会响应 InsertLeave,所以 autocmd InsertLeave * setlocal nopaste 在 Ctrl-C 时是不会执行的。

Git 新建无历史记录分支

git checkout --orphan NEW_BRANCH_NAME

在代码开源分发等时候往往需要去掉不必要的历史记录,这种新分支方式会很方便。

Mosh - Better SSH

Mosh 相比 SSH 的优点:

  1. 网络中断、切换后自动重连。
  2. 屏幕输入及时回显。

服务器需要先安装 mosh-server,开启 60000-61000 端口,本地通过 SSH 登录服务器,然后 UDP 连接服务器 mosh-server。

The most effective debugging tool

The most effective debugging tool is still careful thought, coupled with judiciously placed print statements. by Brian Kernighan