Messages Framework 开发文档(Sticker Packs 与 iMessage Apps)

概述

开发者可以为 iOS 10 的 Messages app 创建 extension。用户在其中可以发送文字、表情、媒体文件、交互式消息(交互式消息即 interactive messages,是一种允许被对话的参与者更新状态的消息)。

第三方软件可以使用 Messages framework 创建两种 app extension: 表情包(Sticker packs)和 iMessage apps。这两种 app extension 都可以作为现有主 app 的 extension 来发布,也可以单独发布。

为了方便理解后文内容,强烈建议先玩一下:
1. Sticker pack 比如知乎刘看山
2. GamePigeon中的台球
3. 大众点评的订电影票

Read on →

Dispatch_once造成的死锁—-分析、解决与自动检测

现象

最近遇到了一个死锁crash,主线程在dispatch_once时卡住了:

1
2
3
4
5
6
7
8
9
10
Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   __ulock_wait + 8
1   _dispatch_unfair_lock_wait + 48
2   _dispatch_gate_wait_slow + 56
3   dispatch_once_f + 124
4   +[OTPolicyCenter sharedInstance] (once.h:68)
7   +[OTWebViewUtil completeUrlScheme:] (OTWebViewUtil.m:26)
...
30  start + 4

卡死的代码很简单,世界上的单例基本上都是这么开的:

1
2
3
4
5
6
7
8
+ (OTPolicyCenter *)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        policyCenterInstance = [[OTPolicyCenter alloc] init];
    });
    return policyCenterInstance;
}
Read on →

检查iOS App是否支持IPv6-only Network

从2016年6月1号开始,苹果强制要求上架AppStore的应用支持IPv6-only network。
本文分为两部分:通过代码检查是否兼容IPv6-only网络,以及如何搭建IPv6-only网络的测试环境。旨在帮助快速检查app是否支持IPv6-only Network。可以当做Supporting IPv6 DNS64/NAT64 NetworksEnsuring IPv6 DNS64/NAT64 Compatibility一节的速成版本。

通过搭建IPv6-only网络的测试环境测试

最便捷的检查方式是:找一台Mac,共享Internet到IPv6 DNS64/NAT64网络,然后让待测试设备连接到此网络,并测试app功能是否正常。
需注意,OS X 10.11后才支持创建IPv6 DNS64/NAT64网络。如果待测试设备不是iOS或OS X或macOS设备(比如要测试Andr**d上的app是否兼容IPv6-only网络),请确保设备支持RFC6106

Read on →

Objective-c使用运行时dump Class的方法列表

最近需要对UIWebView内请求的静态文件做缓存,还要把Gif替换成静态图以节约CPU资源等,于是查了一下私有API可不可以抓取UIWebView内的请求打个标记,以便NSURLProtocol中可以准确判断是不是UIWebView发出的请求。
经调试发现hookwebThreadWebView:resource:willSendRequest:redirectResponse:fromDataSource:可以做这件事。调试过程就不细说了。

首先是dump类方法列表的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <objc/runtime.h>
void DumpObjcMethods(Class clz)
{
    unsigned int methodCount = 0;
    Method *methods = class_copyMethodList(clz, &methodCount);
    printf("Found %d methods on '%s'\n", methodCount, class_getName(clz));

    for (unsigned int i = 0; i < methodCount; i++)
    {
        Method method = methods[i];

        printf("\t'%s' has method named '%s' of encoding '%s'\n",
               class_getName(clz),
               sel_getName(method_getName(method)),
               method_getTypeEncoding(method));
        /**
         *  Or do whatever you need here...
         */
    }
    free(methods);
}
Read on →

iOS抓包(使用BurpSuite和tcpdump)

Introduce

开发过程中我们经常会需要对网络请求抓包,本次介绍的是使用BurpSuite抓取HTTP/HTTPS包,以及不越狱使用tcpdump抓取iPhone的网络包。

使用BurpSuite对HTTP/HTTPS抓包

开发中我们经常会需要对HTTP/HTTPS请求进行抓包。
抓包实际上是在中间机器开了一个代理服务,让需要抓包的请求经过代理,我们就可以看到这些请求了。本质上是中间人攻击。
BurpSuite是一个常用的调试工具。

1. 下载BurpSuite

BurpSuite官网下载jar包,右键点击,运行:

Read on →

在lldb中一键打开模拟器sandbox路径

打开~/.lldbinit,在里面加入一行:

1
command alias sb script from subprocess import call; call(["open", '{0:s}'.format(lldb.frame.EvaluateExpression("NSHomeDirectory()")).split("\"")[1]]);

然后中断时,在lldb里打sb回车,就能打开模拟器当前运行的app的沙箱路径了。

Read on →

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 →