OTHTTPRequest v2.0 Release

这几天重写了OTHTTPRequest的大部分代码,发布了2.0版。
改动主要是上传文件功能,以前做的比较粗,是整个文件读到内存再上传的,不能支撑大文件上传,现在multipart/form请求全部改为流上传。此外增加了上传速度计算的功能。

最早做这个的原因是13年5月份做网易云音乐的下载性能优化,ASIHTTPRequest下载文件占用CPU时间太长,而当时iPhone4还有一定占有量,做了这个库来提升下载性能。
这次发布2.0,修改上传功能,是因为用ASIHTTPRequest做大文件上传时,进度回调有bug,尝试修结果没修成,于是花了点时间做了这个版本。

和1.0一样,整个框架基于NSURLRequest和NSURLConnection。

Github链接

Read on →

Xcode/lldb自动导入UIKit

lldb导入UIKit

使用lldb调试app时,lldb经常提示view.bounds无法打印:

实际上是lldb启动时没有默认导入UIKit,所以UIKit中的方法无法识别。在lldb中使用@import手动导入

1
expr @import UIKit

即可解决:

在开发Mac App时也是一样的,比如不导入AppKit则无法打印frame属性,手动导入AppKit即可:

1
expr @import AppKit

工程自动导入

但是大部分人都换有懒癌,对于每次调试都要手打一行命令是难以接受的。我们可以在main上加个断点:

在main中断时执行expr @import UIKit,并自动continue:

然后在工程启动时就会自动导入UIKit到lldb了。

全局自动导入

对于懒癌晚期的患者,每个工程单独配置还是挺麻烦的,可以在lldbinit中设置全局自动导入UIKit。在命令行中执行:

1
2
echo display @import UIKit >> ~/.lldbinit
echo display @import AppKit >> ~/.lldbinit

再重新启动调试即可(Xcode7测试时无需重启Xcode)。

Over

GCD异步同步互转

同步转异步

这个是GCD最常见的用法之一,耗时长的操作放在global queue中做,完成之后将结果交给主线程处理:

1
2
3
4
5
6
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
  id result = [self longTimeTask];
  dispatch_async(dispatch_get_main_queue(), ^{
      [self handleResultOnMainThread:result];
  });
});

异步转同步

这个本人并不常用,偶尔有不得不用的场景。
在异步操作开始前create信号量,然后使用dispatch_semaphore_wait等待此信号量。在异步操作完成后对此信号量发送signal:

1
2
3
4
5
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[self someAsyncTaskWithCallback:^(BOOL successed) {
      dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

Over

App/Framework/Static Library开启BitCode,既踩坑笔记

苹果对BitCode的介绍看上去很美,今天尝试了一下,掉入深坑,克服种种困难终于爬上来了。成功开启后的效果还是挺好的。

下面介绍一下如何开启BitCode,以及我掉的坑有哪些。

Read on →

Self在执行方法的过程中dealloc引起的崩溃处理

场景和产生原因

在我的一个view controller里(为方便我们成为controller A),有一个doSomething方法,每次调用时,会延迟5秒调用delayAction,如果在5秒之内再次调用,会取消上次的延迟调用delayAction,再在5秒之后调用delayAction。

这在UI操作中是一个稀松平常的需求,代码如下:

1
2
3
4
5
- (void)doSomething
{
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(delayAction) object:nil];
    [self performSelector:@selector(delayAction) withObject:nil afterDelay:5];
}

然而就在某种情况下,这个正常工作的代码崩溃了,原因是第4行代码self已经释放,成为了僵尸指针,调用performSelector时造成了崩溃。

Read on →

使用nomad cli测试iOS的Push Notification

安装和使用

Nomad cli是一个ruby工具,涵盖了多项打包,测试功能。
首先,安装nomad cli:

1
gem install nomad cli

然后即可使用apn push命令发送push了:

1
apn push devicetoken8fd63xxxxxxxxxxxxxxxxxxe27bc82a3b313475bb488ee92092d3c -c push_cert.pem -n -m "A message from openthread"
Read on →

Xcode 中手动 Symbolicate Crash Log

从 Xcode7 开始 Organizer 集成了查看崩溃日志的功能,若 Xcode 可以定位到 crash 的 build,则可以直接在 Organizer 中查看崩溃是在具体哪一行。
但是不能自动定位到 build 的情况也非常常见,此时地址不能自动转换成符号:

此时即需要手动转换。

Read on →

不带导航条的UIViewController推出带导航条的UIViewController

左侧controller A隐藏导航条,推出右侧controller B显示导航条,类似这个效果:

Read on →

[UIWindow setRootViewController:]后view无限叠加的问题修复

工程中有时会直接修改window.rootViewController,来导航到新的页面。
按理说对window的rootViewController属性设好了新值,老的rootViewController被释放了,UIKit应当自动把老的rootViewController的view一并remove掉,然而实测并非如此。
在iOS8/9中(更老版本没有测试),当window有rootViewController时,把新的controller赋值给window.rootViewController,老的rootViewController的view还是会留在window上。这些被遗留的view虽然看不见,但是浪费了内存,造成了view泄露;而且view的controller已经dealloc,此时view如果回调或通知controller的话,有造成崩溃的隐患。

多次设置rootViewController后,view结构如图,window上加了多个UILayoutContainerView:

Read on →

iOS8的UITextView输入光标显示不全的hack

在iOS8及以下版本的系统上,在定高的UITextView中,输入内容超过Text View高度后,输入光标有时会在Text View的底部显示不全,如how-to-make-a-uitextview-scroll-while-typing-editing中截图所描述。

尝试了各种方案,挑选了一种体验较好的。在textViewDidChanged:中,检测到正在编辑的区域在文字最下行,无动画滚动到结尾:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (void)textViewDidChange:(UITextView *)textView
{
    //hack for iOS8
    if (isLessThanIOS9)//in iOS9 Apple has already fixed this bug
    {
        CGRect line = [textView caretRectForPosition:
                       textView.selectedTextRange.start];
        CGFloat overflow = line.origin.y + line.size.height
        - (textView.contentOffset.y + textView.bounds.size.height
           - textView.contentInset.bottom - textView.contentInset.top);
        if (overflow > 0)//If at the bottom of text view
        {
            //disable animation. Otherwise, when a input confirm scroll animation is doing, input new text, animation will re-do from animation beginning, which looks strange.
            [UIView setAnimationsEnabled:NO];

            //scroll to text end
            [textView scrollRangeToVisible:NSMakeRange([textView.text length], 0)];
            [UIView setAnimationsEnabled:YES];
        }
    }
}

Over