心辰·Dev

iOS 开发小记


  • 首页

  • 关于

  • 归档

  • 标签
心辰·Dev

iOS 开发中的持续集成(CI)实践

发表于 2017-04-06

安装配置 CI 系列工具

得益于 mac os 上的神器homebrew,我们可以很快的安装配置以下工具:

  • jenkins:持续集成界的明星项目,提供了工程自动化的服务端支持。
  • fastlane:ruby 编写的一套用于 iOS 自动化编译、打包、测试、发布的工具集合。
  • oclint:c / c++ / objc 代码质量静态检查工具,可自定义检查规范。
  • clang-format:批量化地对代码格式进行规范化。
  • xcpretty:格式化 xcodebuild 输出的工具。

以上工具大多数可以用brew install XXX进行安装,具体步骤可参看各工具官方网站。

jenkins 实用插件

在安装好 jenkins 并运行后,可以对 jenkins 进行一系列配置。因为公司的项目代码集中管理在 SVN 服务上,下面会主要介绍针对 SVN 来进行 jenkins 的工程配置。在 jenkins 进行一次构建之前,需要将 SVN 上的最新代码拉到 jenkins 本地的 workspace 文件夹,所以需要让 jenkins 加载 SVN 的支持插件Subversion Plug-in。针对 iOS 工程的签名和打包,安装Xcode integration和Keychains and Provisioning Profiles Management两个插件来使 jenkins 支持 iOS APP 的打包过程和证书管理。在 jenkins 运行一次构建之后,一般还希望运行一些脚本来进行更多的操作,那么还需要加载一个插件Post-Build Script Plug-in。插件AnsiColor会使 jenkins 的控制台输出可读性更高,插件HTML Publisher plugin可以帮助快速生成 fastlane / oclint 运行结果报告到 workspace 的首页。以上插件都提高 iOS 工程持续集成的效率,还有更多插件有待探索。

安装好插件后,通常在 jenkins 的系统设置页面中会添加相应的待设置的栏目。例如,安装了Keychains and Provisioning Profiles Management插件后,要在系统设置页面对证书和许可文件信息进行完善。
系统设置页面
要注意这里需要导入的是含有证书的钥匙串文件,一般位于 /Users/Username/Library/keychains/login.keychain,也可以重新在钥匙串.app添加一个只用于 jenkins 的钥匙串并上传。项目的Provisioning Profiles可以从 XCode 的 Preferences —> Accounts —> Apple IDs 找到。保存文件的路径最好为下图所示:
证书管理

阅读全文 »
心辰·Dev

iOS 应用 UI 测试开发概览

发表于 2017-03-07

UI 测试是 iOS 开发中很常见的环节,一般可以分为两类:行为测试和外观测试。本文主要根据几个测试框架来介绍 iOS 平台上行为测试的方案。

行为驱动开发

UI 测试在整个测试开发体系中处于最上层的位置。而依次向下有针对软件 Service 层的服务测试和更细粒度的单元测试(Unit Test)。在单元测试中,测试某个对象的行为方式来确保其按预期运行,引申出行为驱动开发(BDD)的概念。

DSL 语法

BDD 有一套 DSL(Domain-specific Language) 来描述需求。针对一个Car类应该这样写测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
SpecBegin(Car)
// 声明测试类
describe(@"Car", ^{
// 声明一组用例
__block Car *car;

// 运行于所有同级块和嵌套块之前的块
beforeEach(^{
car = [Car new];
});

// 运行于所有同级块和嵌套块之后的块
afterEach(^{
car = nil;
});

// it 声明单一实际测试
it(@"should be red", ^{
expect(car.color).to.equal([UIColor redColor]);
});

describe(@"when it is started", ^{

beforeEach(^{
[car start];
});

it(@"should have engine running", ^{
expect(car.engine.running).to.beTruthy();
});
});

describe(@"move to", ^{
// context 提供基于不同状态的期望
context(@"when the engine is running", ^{

beforeEach(^{
car.engine.running = YES;
[car moveTo:CGPointMake(42,0)];
});

it(@"should move to given position", ^{
expect(car.position).to.equal(CGPointMake(42, 0));
});
});

context(@"when the engine is not running", ^{

beforeEach(^{
car.engine.running = NO;
[car moveTo:CGPointMake(42,0)];
});

it(@"should not move to given position", ^{
expect(car.engine.running).to.beTruthy();
});
});
});
});
SpecEnd
// 测试类结束

阅读全文 »
心辰·Dev

iOS 开发常见设计模式解析 | 结构型模式

发表于 2016-11-07

适配器

定义

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

类图

对象适配器
类适配器
目标(Target)定义了客户可以使用的与特定领域相关的接口。被适配者(Adaptee)定义着已经存在的待适配的接口,适配器(Adapter)对被适配者的接口和目标接口进行适配。适配器使用多重继承进行适配的是类适配器,而依赖于对象组合进行适配的是对象适配器。

实践

Objc 语言本身不支持多重继承,但它支持灵活地定义协议(protocol),类似于 Java 语言中的接口(interface),使得一系列方法声明可以独立于类继承之外,通过遵从协议同样可以形成多重继承,实现对象适配器。虽然不符合适配器模式的结构描述,但其思想仍然是使原本接口不匹配的类可以协同工作。
在 iOS 编程中一个对象适配器的实现需要借助于协议(protocol)。客户想要与接口不兼容的类进行消息传递,可以先定义一个协议(protocol),使适配器类(Adapter)遵从协议,实现一个或多个协议中的方法,这样客户就可以使用协议中的接口,通过适配器类向其中引用的不兼容类(Adaptee)转发消息。协议(protocol)常用于iOS 开发中的委托(delegate)模式。 Block 同样可以实现适配器模式,用 Block 替代协议进行信息的传递。

效果

  • 使用一个可复用的类,该类与其他不相关或不可预见的类协同工作。
  • 适配的目标(Target)与被适配者(Adaptee)接口越不相似,需要适配器做的工作越多。
  • 继承两个目标(Target)可以实现双向适配器,提供透明操作。
    阅读全文 »
心辰·Dev

iOS 开发常见设计模式解析 | 创建型模式

发表于 2016-11-02

单例模式

定义

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

类图

单例
单例类(Singleton)定义一个实例化类操作,允许客户通过该操作访问唯一实例。

实践

Cocoa 框架中,UIApplication / UIAccelerometer / NSFileManager 等类均实现为单例类。通过 GCD 多线程框架可以方便的创建一个单例:

1
2
3
4
5
6
7
8
9
10
+ (instancetype)sharedinstance {
static dispatch_once_t oncetoken;
static SingletonClass *sharedinstance;
dispatch_once(&oncetoken, ^{
if (!sharedinstance){
sharedinstance = [[SingletonClass alloc] init];
}
});
return sharedinstance;
}

阅读全文 »
心辰·Dev

Block 底层探秘

发表于 2016-10-09

Block底层实现

运用clang -rewrite-objc命令可以把 Objective-C 代码转换为 C/C++ 代码,从而展示 Block 的底层实现。以下是一个最普通的 Block 代码示例:

1
2
3
4
5
int main(int argc, const char *argv[]) {
void (^blk)(void) = ^{printf("Block\n");};
blk();
return 0;
}

经过clang -rewrite-objc的转换,可以得到 Block 结构体的声明:

1
2
3
4
5
6
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
阅读全文 »
心辰·Dev

OpenEar 语音识别框架使用指南

发表于 2016-09-22

OpenEar 是基于 CMU PocketSphinx 的开源语音库,提供了丰富的语音合成、语音识别相关的功能,用 Cocoa 风格重新封装了 PocketSphinx 的接口,方便 iOS 开发者快速在 PocketSphinx 基础上进行语音方面的研究和开发。

基础原理

CMU PocketSphinx 主要有四部分组成:信号处理和特征提取、声学模型、语言模型和解码器。工作流程如图所示:
语音识别系统组成图
信号处理和特征提取部分以音频信号为输入,通过消除噪音、信道失真等对语音进行增强,将语音信号从时域转化到频域,并为后面的声学模型提取合适的特征。声学模型将声学和发音学的知识进行整合,以特征提取模块提取的特征为输入,生成声学模型得分。语言模型估计通过重训练语料学习词之间的相互概率,来估计假设词序列的可能性,也即语言模型得分。如果了解领域或者任务相关的先验知识,语言模型得分通常可以估计的更准确。解码器对给定的特征向量序列和若干假设词序列计算声学模型得分和语言模型得分,将总体输出分数最高的词序列作为识别结果。PocketSphinx 使用的是梅尔倒谱系数特征和混合高斯-隐马尔科夫(GMM-HMM)声学模型。

阅读全文 »
心辰·Dev

iOS 非越狱环境应用插件开发流程

发表于 2016-07-03

工具

  • class-dump (获取已脱壳应用的头文件)
  • ida (反汇编得到方法的具体实现)
  • Cycript (动态调试获得更多信息)
  • CaptainHook (替换已有方法的实现)
  • yololib (在可执行文件中注入动态库)
  • iOSOpenDev (在 XCode 中生成动态库)

定位目标函数

在掌握上文所述工具后,就可以着手进行插件的开发。首先应该思考插件要实现的需求,找到相关的视图和控制器。第一步可以先使用 class-dump 分析已砸壳的 APP,class-dump 可以根据 Mach-O 文件中的符号表( symbol table )分析出所有的类名和方法声明。

1
~ » ./class-dump -H -o /header/path WeChat
阅读全文 »
心辰·Dev

WKWebView 兼容 iOS7 实践

发表于 2016-03-08

iOS8 新特性 WKWebView

WKWebView 相对于 UIWebView 有以下几点显著的提升:

  1. 使用 Nitro JavaScript 引擎,加载处理 JS 代码的速度明显提升四倍左右。
  2. 对 Web APP 加载的速度提升百分之20左右。
  3. 优化了 WebView 的内存使用。
  4. 开放了更多的 API.

这已经足以让我们考虑在支持 iOS8 设备的应用中使用 WKWebView.

阅读全文 »
心辰·Dev

UIAppearance 详解(译)

发表于 2016-02-15

声明:本文译自《NSHipster》

风格 vs. 实质,信息 vs. 媒介,言辞 vs. 逻辑。
美丽仅是肤浅的表面,还是更深层的事实?某个事物有好的设计意味着什么?审美的判断是绝对的,还是因人而异的?
这些是哲学家、艺术家、创作者千年来反复思索的深刻问题。
当我们都继续着对宇宙中美和真理的追求,APP 销售市场清晰印证了以下的话:
用户会为好看的软件付出更多的钱。

当一个人买下一台 iPhone,他其实买的是苹果公司的哲学:好用的东西同时可以很好看。当我们选择为 iOS 平台开发应用时,我们也是抱着同样的信念。一套稀烂的 UI 反映了其下糟糕的代码。
曾经在 iOS 平台上,使用像方法调剂(Method Swizzling)这样的技术来实现 UI 个性化,绕过 AppStore 的上架审核。幸运的是,随着 iOS 5 的改变,开发者有更简单的方法来实现:UIAppearance.

阅读全文 »
心辰·Dev

Objective-C 和 JavaScript 双向交互详解

发表于 2016-01-30

本文分两部分,分别讲解使用 UIWebView 和 iOS8 引入的 WKWebView 进行 Web <==> Native 双向交互的方式。

UIWebView

Objective-C 调用 JavaScript 方法

UIWebView 的 API stringByEvaluatingJavaScriptFromString:是在 WebView 中运行 JS 代码的实例方法。在 Web 端用 JS 提供具体的方法实现,在 native 端就可以方便的调用。
JS 文件中:

1
2
3
function myJavascriptFunction(){
// 实现
}

OC 文件中:

1
[webview stringByEvaluatingJavaScriptFromString: @“myJavascriptFunction()”];
阅读全文 »
123
BingXin Xue

BingXin Xue

薛冰心的博客

26 日志
13 标签
github weibo douban zhihu
© 2014 - 2017 BingXin Xue
由 Hexo 强力驱动
主题 - NexT.Muse