<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>iOS on prometheus</title><link>https://new.halfrost.com/zh/categories/ios/</link><description>Recent content in iOS on prometheus</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><copyright>halfrost</copyright><lastBuildDate>Sat, 08 Jul 2017 09:51:00 +0000</lastBuildDate><atom:link href="https://new.halfrost.com/zh/categories/ios/index.xml" rel="self" type="application/rss+xml"/><item><title>大话大前端时代(一) —— Vue 与 iOS 的组件化</title><link>https://new.halfrost.com/zh/vue_ios_modularization/</link><pubDate>Sat, 08 Jul 2017 09:51:00 +0000</pubDate><guid>https://new.halfrost.com/zh/vue_ios_modularization/</guid><description>序 今年大前端的概念一而再再而三的被提及，那么大前端时代究竟是什么呢？大前端这个词最早是因为在阿里内部有很多前端开发人员既写前端又写 Java 的 Velocity 模板而得来，不过现在大前端的范围已经越来越大了，包含前端 + 移动端，前端、CDN、Nginx、Node、Hybrid、Weex、React Native、Native App。笔者是一名普通的全职 iOS 开发者，在接触到了前端开发以后，发现了前端有些值得移动端学习的地方，于是便有了这个大前端时代系列的文章，希望两者能相互借鉴优秀的思想。谈及到大前端，常常被提及的话题有：组件化，路由与解耦，工程化（打包工具，脚手架，包管理工具），MVC 和 MVVM 架构，埋点和性能监控。笔者就先从组件化方面谈起。网上关于前端框架对比的文章也非常多（对比 React，Vue，Angular），不过跨端对比的文章好像不多？笔者就打算以前端和移动端（以 iOS 平台为主）对比为主，看看这两端的不同做法，并讨论讨论有无相互借鉴学习的地方。
本文前端的部分也许前端大神看了会觉得比较基础，如有错误还请各位大神不吝赐教。
Vue 篇 一. 组件化的需求 为了提高代码复用性，减少重复性的开发，我们就把相关的代码按照 template、style、script 拆分，封装成一个个的组件。组件可以扩展 HTML 元素，封装可重用的 HTML 代码，我们可以将组件看作自定义的 HTML 元素。在 Vue 里面，每个封装好的组件可以看成一个个的 ViewModel。
二. 如何封装组件 谈到如何封装的问题，就要先说说怎么去组织组件的问题。
如果在简单的 SPA 项目中，可以直接用 Vue.component 去定义一个全局组件，项目一旦复杂以后，就会出现弊端了：
全局定义(Global definitions) 强制要求每个 component 中的命名不得重复 字符串模板(String templates) 缺乏语法高亮，在 HTML 有多行的时候，需要用到丑陋的 \ 不支持 CSS(No CSS support) 意味着当 HTML 和 JavaScript 组件化时，CSS 明显被遗漏 没有构建步骤(No build step) 限制只能使用 HTML 和 ES5 JavaScript, 而不能使用预处理器，如 Pug (formerly Jade) 和 Babel 而且现在公司级的项目，大多数都会引入工程化的管理，用包管理工具去管理，npm 或者 yarn。所以 Vue 在复杂的项目中用 Vue.</description></item><item><title>iOS 开发者的 Weex 伪最佳实践指北</title><link>https://new.halfrost.com/zh/weex_best_practice_guidelines/</link><pubDate>Wed, 03 May 2017 06:07:00 +0000</pubDate><guid>https://new.halfrost.com/zh/weex_best_practice_guidelines/</guid><description>引子 这篇文章是笔者近期关于Weex在iOS端的一些研究和实践心得，和大家一起分享分享，也算是对学习成果的总结。文章里面提到的做法也许不是最佳实践，也许里面的方法称不算是一份标准的指南手册，所以标题就只好叫“伪最佳实践指北”了。有更好的方法欢迎大家一起留言讨论，一起学习。
由于笔者不太了解Android，所以以下的文章不会涉及到Android。
一. React Native 和 Weex 自从Weex出生的那一天起，就无法摆脱和React Native相互比较的命运。React Native宣称“Learn once, write anywhere”，而Weex宣称“Write Once, Run Everywhere”。Weex从出生那天起，就被给予了一统三端的厚望。React Native可以支持iOS、Android，而Weex可以支持iOS、Android、HTML5。
在Native端，两者的最大的区别可能就是在对JSBundle是否分包。React Native官方只允许将React Native基础JS库和业务JS一起打成一个JS bundle，没有提供分包的功能，所以如果想节约流量就必须制作分包打包工具。而Weex默认打的JS bundle只包含业务JS代码，体积小很多，基础JS库包含在Weex SDK中，这一点Weex与Facebook的React Native和微软的Cordova相比，Weex更加轻量，体积小巧。
在JS端，Weex又被人称为Vue Native，所以 React Native 和 Weex 的区别就在 React 和 Vue 两者上了。
笔者没有写过React Native，所以也没法客观的去比较两者。不过知乎上有一个关于Weex 和 React Native很好的对比文章《weex&amp;amp;React Native对比》，推荐大家阅读。
前两天@Allen 许帅也在Glow 技术团队博客上面发布了一篇《React Native 在 Glow 的实践》这篇文章里面也谈了很多关于React Native实践相关的点，也强烈推荐大家去阅读。
二. 入门基础 关于小白想入门Weex，当然最基础的还是要通读文档，文档是官方最好的学习资料。官方的基础文档有两份：
教程文档
手册文档
在文档手册里面包含了Weex所有目前有的组件，模块，每个组件和模块的用法和属性。遇到问题可以先过来翻翻。很有可能有些组件和模块没有那些属性。
1. Weex全家桶和脚手架 看完官方文档以后，就可以开始上手构建工程项目了。
我司在知乎上面写了4篇关于《Weex入坑指南的》。这四篇文章还是很值得看的。
Weex也和前端项目一样，拥有它自己的脚手架全家桶。weex-toolkit + weexpack + playground + code snippets + weex-devtool。</description></item><item><title>Weex 中别具匠心的 JS Framework</title><link>https://new.halfrost.com/zh/weex_js_framework/</link><pubDate>Sun, 23 Apr 2017 05:15:00 +0000</pubDate><guid>https://new.halfrost.com/zh/weex_js_framework/</guid><description>前言 Weex为了提高Native的极致性能，做了很多优化的工作
为了达到所有页面在用户端达到秒开，也就是网络（JS Bundle下载）和首屏渲染（展现在用户第一屏的渲染时间）时间和小于1s。
手淘团队在对Weex进行性能优化时，遇到了很多问题和挑战：
JS Bundle下载慢，压缩后60k左右大小的JS Bundle，在全网环境下，平均下载速度大于800ms（在2G/3G下甚至是2s以上）。 JS和Native通信效率低，拖慢了首屏加载时间。
最终想到的办法就是把JSFramework内置到SDK中，达到极致优化的作用。
客户端访问Weex页面时，首先会网络请求JS Bundle，JS Bundle被加载到客户端本地后，传入JSFramework中进行解析渲染。JS Framework解析和渲染的过程其实是根据JS Bundle的数据结构创建Virtual DOM 和数据绑定，然后传递给客户端渲染。
由于JSFramework在本地，所以就减少了JS Bundle的体积，每个JS Bundle都可以减少一部分体积，Bundle里面只保留业务代码。每个页面下载Bundle的时间都可以节约10-20ms。如果Weex页面非常多，那么每个页面累计起来节约的时间就很多了。 Weex这种默认就拆包加载的设计，比ReactNative强，也就不需要考虑一直困扰ReactNative头疼的拆包的问题了。
整个过程中，JSFramework将整个页面的渲染分拆成一个个渲染指令，然后通过JS Bridge发送给各个平台的RenderEngine进行Native渲染。因此，尽管在开发时写的是 HTML / CSS / JS，但最后在各个移动端（在iOS上对应的是iOS的Native UI、在Android上对应的是Android的Native UI）渲染后产生的结果是纯Native页面。 由于JSFramework在本地SDK中，只用在初始化的时候初始化一次，之后每个页面都无须再初始化了。也进一步的提高了与Native的通信效率。
JSFramework在客户端的作用在前几篇文章里面也提到了。它的在Native端的职责有3个：
管理每个Weex instance实例的生命周期。 不断的接收Native传过来的JS Bundle，转换成Virtual DOM，再调用Native的方法，构建页面布局。 响应Native传过来的事件，进行响应。 接下来，笔者从源码的角度详细分析一下Weex 中别具匠心的JS Framework是如何实现上述的特性的。
目录 1.Weex JS Framework 初始化 2.Weex JS Framework 管理实例的生命周期 3.Weex JS Framework 构建Virtual DOM 4.Weex JS Framework 处理Native触发的事件 5.Weex JS Framework 未来可能做更多的事情 一.</description></item><item><title>Weex 事件传递的那些事儿</title><link>https://new.halfrost.com/zh/weex_event/</link><pubDate>Fri, 14 Apr 2017 18:18:00 +0000</pubDate><guid>https://new.halfrost.com/zh/weex_event/</guid><description>前言 在前两篇文章里面分别谈了Weex如何在Native端初始化的和Weex是如何高效的渲染Native的原生UI的。Native这边还缺一块，那就是Native产生的一些事件，是怎么传回给JS的。这篇文章就详细分析这一部分。
目录 1.Weex的事件类型 2.Weex的事件传递 一.Weex的事件类型 在Weex中，目前最新版本中事件总共分为4种类型，通用事件，Appear 事件，Disappear 事件，Page 事件。
在Weex的组件里面只包含前三种事件，即通用事件，Appear 事件，Disappear 事件。
当WXComponent添加事件的时候，会调用以下函数：
- (void)_addEventOnMainThread:(NSString *)addEventName { WX_ADD_EVENT(appear, addAppearEvent) WX_ADD_EVENT(disappear, addDisappearEvent) WX_ADD_EVENT(click, addClickEvent) WX_ADD_EVENT(swipe, addSwipeEvent) WX_ADD_EVENT(longpress, addLongPressEvent) WX_ADD_EVENT(panstart, addPanStartEvent) WX_ADD_EVENT(panmove, addPanMoveEvent) WX_ADD_EVENT(panend, addPanEndEvent) WX_ADD_EVENT(horizontalpan, addHorizontalPanEvent) WX_ADD_EVENT(verticalpan, addVerticalPanEvent) WX_ADD_EVENT(touchstart, addTouchStartEvent) WX_ADD_EVENT(touchmove, addTouchMoveEvent) WX_ADD_EVENT(touchend, addTouchEndEvent) WX_ADD_EVENT(touchcancel, addTouchCancelEvent) [self addEvent:addEventName]; } WX_ADD_EVENT是一个宏：
#define WX_ADD_EVENT(eventName, addSelector) \ if ([addEventName isEqualToString:@#eventName]) {\ [self addSelector];\ } 即是判断待添加的事件addEventName的名字和默认支持的事件名字eventName是否一致，如果一致，就执行addSelector方法。
最后会执行一个addEvent:方法，每个组件里面会可以重写这个方法。在这个方法里面做的就是对组件的状态的标识。
比如WXWebComponent组件里面的addEvent:方法：
- (void)addEvent:(NSString *)eventName { if ([eventName isEqualToString:@&amp;#34;pagestart&amp;#34;]) { _startLoadEvent = YES; } else if ([eventName isEqualToString:@&amp;#34;pagefinish&amp;#34;]) { _finishLoadEvent = YES; } else if ([eventName isEqualToString:@&amp;#34;error&amp;#34;]) { _failLoadEvent = YES; } } 在这个方法里面即对Web组件里面的状态进行了标识。</description></item><item><title>由 FlexBox 算法强力驱动的 Weex 布局引擎</title><link>https://new.halfrost.com/zh/weex_flexbox/</link><pubDate>Fri, 31 Mar 2017 08:26:00 +0000</pubDate><guid>https://new.halfrost.com/zh/weex_flexbox/</guid><description>前言 在上篇文章里面谈了Weex在iOS客户端工作的基本流程。这篇文章将会详细的分析Weex是如何高性能的布局原生界面的，之后还会与现有的布局方法进行对比，看看Weex的布局性能究竟如何。
目录 1.Weex布局算法 2.Weex布局算法性能分析 3.Weex是如何布局原生界面的 一. Weex布局算法 打开Weex的源码的Layout文件夹，就会看到两个c的文件，这两个文件就是今天要谈的Weex的布局引擎。
Layout.h和Layout.c最开始是来自于React-Native里面的代码。也就是说Weex和React-Native的布局引擎都是同一套代码。
当前React-Native的代码里面已经没有这两个文件了，而是换成了Yoga。
Yoga本是Facebook在React Native里引入的一种跨平台的基于CSS的布局引擎，它实现了Flexbox规范，完全遵守W3C的规范。随着该系统不断完善，Facebook对其进行重新发布，于是就成了现在的Yoga(Yoga官网)。
那么Flexbox是什么呢？
熟悉前端的同学一定很熟悉这个概念。2009年，W3C提出了一种新的方案——Flex布局，可以简便、完整、响应式地实现各种页面布局。目前，它已经得到了几乎所有浏览器的支持，目前的前端主要是使用Html / CSS / JS实现，其中CSS用于前端的布局。任何一个Html的容器可以通过css指定为Flex布局，一旦一个容器被指定为Flex布局，其子元素就可以按照FlexBox的语法进行布局。
关于FlexBox的基本定义，更加详细的文档说明，感兴趣的同学可以去阅读一下W3C的官方文档，那里会有很详细的说明。官方文档链接
Weex中的Layout文件是Yoga的前身，是Yoga正式发布之前的版本。底层代码使用C语言代码，所以性能也不是问题。接下来就仔细分析Layout文件是如何实现FlexBox的。
故以下源码分析都基于v0.10.0这个版本。
（一）FlexBox中的基本数据结构 Flexbox布局（Flexible Box)设计之初的目的是为了能更加高效的分配子视图的布局情况，包括动态的改变宽度，高度，以及排列顺序。Flexbox可以更加方便的兼容各个大小不同的屏幕，比如拉伸和压缩子视图。
在FlexBox的世界里，存在着主轴和侧轴的概念。
大多数情况，子视图都是沿着主轴（main axis），从主轴起点（main-start）到主轴终点（main-end）排列。但是这里需要注意的一点是，主轴和侧轴虽然永远是垂直的关系，但是谁是水平，谁是竖直，并没有确定，有可能会有如下的情况：
在上图这种水平是侧轴的情况下，子视图是沿着侧轴（cross axis），从侧轴起点（cross-start）到侧轴终点（cross-end）排列的。
**主轴（main axis）：**父视图的主轴，子视图主要沿着这条轴进行排列布局。
**主轴起点（main-start）和主轴终点（main-end）：**子视图在父视图里面布局的方向是从主轴起点（main-start）向主轴终点（main-start）的方向。
**主轴尺寸（main size）：**子视图在主轴方向的宽度或高度就是主轴的尺寸。子视图主要的大小属性要么是宽度，要么是高度属性，由哪一个对着主轴方向决定。
**侧轴（cross axis）：**垂直于主轴称为侧轴。它的方向主要取决于主轴方向。
**侧轴起点（cross-start）和侧轴终点（cross-end）：**子视图行的配置从容器的侧轴起点边开始，往侧轴终点边结束。
**侧轴尺寸（cross size）：**子视图的在侧轴方向的宽度或高度就是项目的侧轴长度，伸缩项目的侧轴长度属性是「width」或「height」属性，由哪一个对着侧轴方向决定。
接下来看看Layout是怎么定义FlexBox里面的元素的。
typedef enum { CSS_DIRECTION_INHERIT = 0, CSS_DIRECTION_LTR, CSS_DIRECTION_RTL } css_direction_t; 这个方向是定义的上下文的整体布局的方向，INHERIT是继承，LTR是Left To Right，从左到右布局。RTL是Right To Left，从右到左布局。下面分析如果不做特殊说明，都是LTR从左向右布局。如果是RTL就是LTR反向。
typedef enum { CSS_FLEX_DIRECTION_COLUMN = 0, CSS_FLEX_DIRECTION_COLUMN_REVERSE, CSS_FLEX_DIRECTION_ROW, CSS_FLEX_DIRECTION_ROW_REVERSE } css_flex_direction_t; 这里定义的是Flex的方向。</description></item><item><title>Weex 是如何在 iOS 客户端上跑起来的</title><link>https://new.halfrost.com/zh/weex_ios/</link><pubDate>Sat, 18 Mar 2017 02:29:00 +0000</pubDate><guid>https://new.halfrost.com/zh/weex_ios/</guid><description>前言 2016年4月21日，阿里巴巴在Qcon大会上宣布跨平台移动开发工具Weex开放内测邀请。Weex能够完美兼顾性能与动态性，让移动开发者通过简捷的前端语法写出Native级别的性能体验，并支持iOS、安卓、YunOS及Web等多端部署。
近一年来，ReactNative 和 Weex 这些跨平台技术对Native开发者来说，冲击是巨大的。Native在开发App的时候存在一些弊端，比如客户端需要频繁更新，iOS更新时间还要受到审核的牵制；iOS、Android和前端同时开发同一个需求，在人员成本上消耗大；Hybrid的性能和Native相比又差了一点。
ReactNative 和 Weex的出现，就是为了解决这些痛点的。
从4月21号宣布内测以后，短短两周就有超过5000名开发者申请。
2016年6月30日阿里巴巴正式宣布Weex开源。号称可以用Web方式，开发Native级性能体验的亿级应用匠心打造跨平台移动开发工具Weex在开源首日就登上Github趋势榜首位，截止目前为止，Weex在GitHub上的Star数已经到达了13393了。成为中国2016年在Github上最热门的开源项目之一。
目录 1.Weex概述 2.Weex工作原理 3.Weex在iOS上是如何跑起来的 4.关于Weex，ReactNative，JSPatch 一. Weex概述 Weex从出生那天起，仿佛就是和ReactNative是“一对”。
ReactNative宣称“Learn once, write anywhere”，而Weex宣称“Write Once, Run Everywhere”。Weex从出生那天起，就被给予了一统三端的厚望。ReactNative可以支持iOS、Android，而Weex可以支持iOS、Android、HTML5。一统三端就解决了前言里面说的第二个痛点，同时开发浪费人员成本的问题。
Native移动开发者只需要在本地导入Weex的SDK，就可以通过HTML/CSS/JavaScript网页的这套编程语言来开发Native级别的Weex界面。这意味着可以直接用现有Web开发的编辑器和IDE的代码补全、提示、检查等功能。从而也给前端人员开发Native端，较低的开发成本和学习成本。
Weex是一种轻量级、可扩展、高性能框架。集成也很方便，可以直接在HTML5页面嵌入，也可嵌在原生UI中。由于和ReactNative一样，都会调用Native端的原生控件，所以在性能上比Hybrid高出一个层次。这就解决了前言里面所说的第三个痛点，性能问题。
Weex非常轻量，体积小巧，语法简单，方便接入和上手。ReactNative官方只允许将ReactNative基础JS库和业务JS一起打成一个JS bundle，没有提供分包的功能，所以如果想节约流量就必须制作分包打包工具。而Weex默认打的JS bundle只包含业务JS代码，体积小很多，基础JS库包含在Weex SDK中，这一点Weex与Facebook的React Native和微软的Cordova相比，Weex更加轻量，体积小巧。把Weex生成的JS bundle轻松部署到服务器端，然后Push到客户端，或者客户端请求新的资源即可完成发布。如此快速的迭代就解决了前言里面说的第一个痛点，发布无法控制时间，
Weex中Native组件和API都可以横向扩展，业务方可去中心化横向灵活化定制组件和功能模块。并且还可以直接复用Web前端的工程化管理和监控性能等工具。
知乎上有一个关于Weex 和 ReactNative很好的对比文章weex&amp;amp;ReactNative对比，推荐大家阅读。
Weex在2017年2月17日正式发布v0.10.0，这个里程碑的版本开始完美的兼容Vue.js开发Weex界面。
Weex又于2017年2月24 迁移至 Apache 基金会，阿里巴巴会基于 Apache 的基础设施继续迭代。并启用了全新的 GitHub 仓库：https://github.com/apache/incubator-weex
故以下源码分析都基于v0.10.0这个版本。
二. Weex工作原理 上图是官方给的一张原理图，Weex是如何把JS打包成JS Bundle的原理本篇文章暂时不涉及。本篇文章会详细分析Weex是如何在Native端工作的。笔者把Native端的原理再次细分，如下图：
Weex可以通过自己设计的DSL，书写.we文件或者.vue文件来开发界面，整个页面书写分成了3段，template、style、script，借鉴了成熟的MVVM的思想。
Weex在性能方面，为了尽可能的提升客户端的性能，DSL的Transformer全部都放在了服务器端实现，Weex会在服务器端将XML + CSS + JavaScript 代码全部都转换成JS Bundle。服务器将JS Bundle部署到Server上和CDN上。
Weex和React Native不同的是，Weex把JS Framework内置在SDK里面，用来解析从服务器上下载的JS Bundle，这样也减少了每个JS Bundle的体积，不再有React Native需要分包的问题。客户端请求完JS Bundle以后，传给JS Framework，JS Framework解析完成以后会输出Json格式的Virtual DOM，客户端Native只需要专心负责 Virtual DOM 的解析和布局、UI 渲染。然而这一套解析，布局，渲染的逻辑SDK基本实现了。</description></item><item><title>BeeHive —— 一个优雅但还在完善中的解耦框架</title><link>https://new.halfrost.com/zh/beehive/</link><pubDate>Sat, 04 Mar 2017 10:44:00 +0000</pubDate><guid>https://new.halfrost.com/zh/beehive/</guid><description>前言 BeeHive是阿里巴巴公司开源的一个iOS框架，这个框架是App模块化编程的框架一种实现方案，吸收了Spring框架Service的理念来实现模块间的API解耦。
BeeHive这个名字灵感来源于蜂窝。蜂窝是世界上高度模块化的工程结构，六边形的设计能带来无限扩张的可能。所以就用了这个名字作为开源项目的名字。
在前一篇文章iOS 组件化 —— 路由设计思路分析中，我们分析了App组件之间可以通过路由来解除耦合。那么这篇文章就来看看利用模块化的思想如何解除耦合的。
(看到这里一定会很多人有疑问，那就看看这篇文章组件和模块的区别)
说明：本文是基于BeeHive v1.2.0版本进行解析的。
目录 1.BeeHive概述 2.BeeHive模块注册 3.BeeHive模块事件 4.BeeHive模块调用 5.其他的一些辅助类 6.可能还在完善中的功能 一. BeeHive概述 由于BeeHive是基于Spring的Service理念，虽然可以使模块间的具体实现与接口解耦，但无法避免模块对接口类的依赖关系。
暂时BeeHive没有采用invoke和performSelector:action withObject: params的方法。主要原因还是考虑学习成本难度以及动态调用实现无法在编译检查阶段检测接口参数变更等问题。
目前BeeHive v1.2.0 全部是利用Protocol的方式，实现了模块间解耦的目的：
1.各个模块以插件的形式存在。每个都可独立，相互解耦。 2.各个模块具体实现与接口调用分离 3.各个模块也有生命周期，也可以进行管理。
官方也给出了一个架构图：
接下来就依次分析模块注册，模块事件，模块调用是如何实现解耦的。
二. BeeHive模块注册 先从模块的注册开始分析，来看看BeeHive是如何给各个模块进行注册的。
在BeeHive中是通过BHModuleManager来管理各个模块的。BHModuleManager中只会管理已经被注册过的模块。
注册Module的方式总共有三种：
1. Annotation方式注册 通过BeeHiveMod宏进行Annotation标记。
BeeHiveMod(ShopModule) BeeHiveMod宏定义如下：
#define BeeHiveMod(name) \ char * k##name##_mod BeeHiveDATA(BeehiveMods) = &amp;#34;&amp;#34;#name&amp;#34;&amp;#34;; BeeHiveDATA又是一个宏：
#define BeeHiveDATA(sectname) __attribute((used, section(&amp;#34;__DATA,&amp;#34;#sectname&amp;#34; &amp;#34;))) 最终BeeHiveMod宏会在预编译结束会完全展开成下面的样子：
char * kShopModule_mod __attribute((used, section(&amp;#34;__DATA,&amp;#34;&amp;#34;BeehiveMods&amp;#34;&amp;#34; &amp;#34;))) = &amp;#34;&amp;#34;&amp;#34;ShopModule&amp;#34;&amp;#34;&amp;#34;; 注意双引号的总对数。</description></item><item><title>iOS 组件化 —— 路由设计思路分析</title><link>https://new.halfrost.com/zh/ios_router/</link><pubDate>Sat, 25 Feb 2017 03:39:00 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_router/</guid><description>前言 随着用户的需求越来越多，对App的用户体验也变的要求越来越高。为了更好的应对各种需求，开发人员从软件工程的角度，将App架构由原来简单的MVC变成MVVM，VIPER等复杂架构。更换适合业务的架构，是为了后期能更好的维护项目。
但是用户依旧不满意，继续对开发人员提出了更多更高的要求，不仅需要高质量的用户体验，还要求快速迭代，最好一天出一个新功能，而且用户还要求不更新就能体验到新功能。为了满足用户需求，于是开发人员就用H5，ReactNative，Weex等技术对已有的项目进行改造。项目架构也变得更加的复杂，纵向的会进行分层，网络层，UI层，数据持久层。每一层横向的也会根据业务进行组件化。尽管这样做了以后会让开发更加有效率，更加好维护，但是如何解耦各层，解耦各个界面和各个组件，降低各个组件之间的耦合度，如何能让整个系统不管多么复杂的情况下都能保持“高内聚，低耦合”的特点？这一系列的问题都摆在开发人员面前，亟待解决。今天就来谈谈解决这个问题的一些思路。
目录 1.引子 2.App路由能解决哪些问题 3.App之间跳转实现 4.App内组件间路由设计 5.各个方案优缺点 6.最好的方案 一. 引子 大前端发展这么多年了，相信也一定会遇到相似的问题。近两年SPA发展极其迅猛，React 和 Vue一直处于风口浪尖，那我们就看看他们是如何处理好这一问题的。
在SPA单页面应用，路由起到了很关键的作用。路由的作用主要是保证视图和 URL 的同步。在前端的眼里看来，视图是被看成是资源的一种表现。当用户在页面中进行操作时，应用会在若干个交互状态中切换，路由则可以记录下某些重要的状态，比如用户查看一个网站，用户是否登录、在访问网站的哪一个页面。而这些变化同样会被记录在浏览器的历史中，用户可以通过浏览器的前进、后退按钮切换状态。总的来说，用户可以通过手动输入或者与页面进行交互来改变 URL，然后通过同步或者异步的方式向服务端发送请求获取资源，成功后重新绘制 UI，原理如下图所示：
react-router通过传入的location到最终渲染新的UI，流程如下：
location的来源有2种，一种是浏览器的回退和前进，另外一种是直接点了一个链接。新的 location 对象后，路由内部的 matchRoutes 方法会匹配出 Route 组件树中与当前 location 对象匹配的一个子集，并且得到了 nextState，在this.setState(nextState) 时就可以实现重新渲染 Router 组件。
大前端的做法大概是这样的，我们可以把这些思想借鉴到iOS这边来。上图中的Back / Forward 在iOS这边很多情况下都可以被UINavgation所管理。所以iOS的Router主要处理绿色的那一块。
二. App路由能解决哪些问题 既然前端能在SPA上解决URL和UI的同步问题，那这种思想可以在App上解决哪些问题呢？
思考如下的问题，平时我们开发中是如何优雅的解决的：
1.3D-Touch功能或者点击推送消息，要求外部跳转到App内部一个很深层次的一个界面。
比如微信的3D-Touch可以直接跳转到“我的二维码”。“我的二维码”界面在我的里面的第三级界面。或者再极端一点，产品需求给了更加变态的需求，要求跳转到App内部第十层的界面，怎么处理？
2.自家的一系列App之间如何相互跳转？
如果自己App有几个，相互之间还想相互跳转，怎么处理？
3.如何解除App组件之间和App页面之间的耦合性？
随着项目越来越复杂，各个组件，各个页面之间的跳转逻辑关联性越来越多，如何能优雅的解除各个组件和页面之间的耦合性？
4.如何能统一iOS和Android两端的页面跳转逻辑？甚至如何能统一三端的请求资源的方式？
项目里面某些模块会混合ReactNative，Weex，H5界面，这些界面还会调用Native的界面，以及Native的组件。那么，如何能统一Web端和Native端请求资源的方式？
5.如果使用了动态下发配置文件来配置App的跳转逻辑，那么如果做到iOS和Android两边只要共用一套配置文件？
6.如果App出现bug了，如何不用JSPatch，就能做到简单的热修复功能？
比如App上线突然遇到了紧急bug，能否把页面动态降级成H5，ReactNative，Weex？或者是直接换成一个本地的错误界面？
7.如何在每个组件间调用和页面跳转时都进行埋点统计？每个跳转的地方都手写代码埋点？利用Runtime AOP ？
8.如何在每个组件间调用的过程中，加入调用的逻辑检查，令牌机制，配合灰度进行风控逻辑？
9.如何在App任何界面都可以调用同一个界面或者同一个组件？只能在AppDelegate里面注册单例来实现？
比如App出现问题了，用户可能在任何界面，如何随时随地的让用户强制登出？或者强制都跳转到同一个本地的error界面？或者跳转到相应的H5，ReactNative，Weex界面？如何让用户在任何界面，随时随地的弹出一个View ？
以上这些问题其实都可以通过在App端设计一个路由来解决。那么我们怎么设计一个路由呢？
三. App之间跳转实现 在谈App内部的路由之前，先来谈谈在iOS系统间，不同App之间是怎么实现跳转的。
1. URL Scheme方式 iOS系统是默认支持URL Scheme的，具体见官方文档。</description></item><item><title>ReactiveCocoa 中 奇妙无比的“宏”魔法</title><link>https://new.halfrost.com/zh/reactivecocoa_macro/</link><pubDate>Sun, 12 Feb 2017 05:46:54 +0000</pubDate><guid>https://new.halfrost.com/zh/reactivecocoa_macro/</guid><description>前言 在ReactiveCocoa 中，开源库作者为我们提供了很多种魔法，“黑”魔法，“红”魔法……今天就让先来看看“红”魔法。
在ReactiveCocoa 中，封装了很多非常实用的“宏”，使用这些“宏”为我们开发带来了很多的便利。
今天就来盘点一下RAC中的宏是如何实现的。
目录 1.关于宏 2.ReactiveCocoa 中的元宏 3.ReactiveCocoa 中常用的宏 一. 关于宏 宏（Macro），是一种批量处理的称谓。
在编程领域里的宏是一种抽象（Abstraction），它根据一系列预定义的规则替换一定的文本模式。解释器或编译器在遇到宏时会自动进行这一模式替换。绝大多数情况下，“宏”这个词的使用暗示着将小命令或动作转化为一系列指令。
宏的用途在于自动化频繁使用的序列或者是获得一种更强大的抽象能力。 计算机语言如C语言或汇编语言有简单的宏系统，由编译器或汇编器的预处理器实现。C语言的宏预处理器的工作只是简单的文本搜索和替换，使用附加的文本处理语言如M4，C程序员可以获得更精巧的宏。
Lisp类语言如Common Lisp和Scheme有更精巧的宏系统：宏的行为如同是函数对自身程序文本的变形，并且可以应用全部语言来表达这种变形。一个C宏可以定义一段语法的替换，然而一个Lisp的宏却可以控制一节代码的计算。
对于编译语言来说，所有的宏都是在预编译的时候被展开的，所以在lex进行词法扫描生成Token，词法分析过程之前，所有的宏都已经被展开完成了。
对于Xcode，预处理或者预编译阶段是可以直接查看的。
随便写一个宏，然后打开Xcode右上方的Assistant，选择“Preprocess”就可以看到该文件预处理之后的样子了。可以看到左边的@weakify(self) 被转换成了右边的两行代码了。
关于这个Xcode的这个功能还有2点补充说明：
1.不同阶段的Preprocessed可能不同，要根据你的目标去选择预处理的条件。
比如这里就有5种预编译的种类可以选择。
2.宏经过预编译之后出来的代码，是可以用来检测宏写的是否正确的，但是无法看到宏被展开的具体过程。这意味着我们可以通过Xcode这个功能来查看宏的作用，但是无法知道宏的具体实现。具体实现还是需要通过查看源码来分析。
ReactiveCocoa中的宏，如果不查看源码分析，会觉得那些宏都像魔法一样奇妙无比，接下来就来解开“宏”魔法的神秘面纱。
二. ReactiveCocoa 中的元宏 在ReactiveCocoa的宏中，作者定义了这么一些基础的宏，作为“元宏”，它们是构成之后复杂宏的基础。在分析常用宏之前，必须要先分析清楚这些元宏的具体实现。
1. metamacro_stringify(VALUE) #define metamacro_stringify(VALUE) \ metamacro_stringify_(VALUE) #define metamacro_stringify_(VALUE) # VALUE metamacro_stringify( )这个宏用到了#的用法。#在宏中代表把宏的参数变为一个字符串。这个宏的目的和它的名字一样明显，把入参VALUE转换成一个字符串返回。
这里可能就有人有疑问，为啥要包装一层，不能直接写成下面这样：
#define metamacro_stringify(VALUE) # VALUE 语意确实也没有变，但是有种特殊情况下就会出现问题。
举个例子：
#define NUMBER 10 #define ADD(a,b) (a+b) NSLog(@&amp;#34;%d+%d=%d&amp;#34;,NUMBER, NUMBER, ADD(NUMBER,NUMBER)); 输出如下：
10+10=20这样子确实是没有问题，但是稍作修改就会有问题。</description></item><item><title>ReactiveCocoa 中 RACCommand 底层实现分析</title><link>https://new.halfrost.com/zh/reactivecocoa_raccommand/</link><pubDate>Sat, 07 Jan 2017 01:59:00 +0000</pubDate><guid>https://new.halfrost.com/zh/reactivecocoa_raccommand/</guid><description>前言 在ReactiveCocoa 过程中，除去RACSignal和RACSubject这些信号类以外，有些时候我们可能还需要封装一些固定的操作集合。这些操作集合都是固定的，每次只要一触发就会执行事先定义好的一个过程。在iOS开发过程中，按钮的点击事件就可能有这种需求。那么RACCommand就可以实现这种需求。
当然除了封装一个操作集合以外，RACCommand还能集中处理错误等等功能。今天就来从底层来看看RACCommand是如何实现的。
目录 1.RACCommand的定义 2.initWithEnabled: signalBlock: 底层实现分析 3.execute:底层实现分析 4.RACCommand的一些Category 一. RACCommand的定义 首先说说RACCommand的作用。 RACCommand 在ReactiveCocoa 中是对一个动作的触发条件以及它产生的触发事件的封装。
触发条件：初始化RACCommand的入参enabledSignal就决定了RACCommand是否能开始执行。入参enabledSignal就是触发条件。举个例子，一个按钮是否能点击，是否能触发点击事情，就由入参enabledSignal决定。
触发事件：初始化RACCommand的另外一个入参(RACSignal * (^)(id input))signalBlock就是对触发事件的封装。RACCommand每次执行都会调用一次signalBlock闭包。
RACCommand最常见的例子就是在注册登录的时候，点击获取验证码的按钮，这个按钮的点击事件和触发条件就可以用RACCommand来封装，触发条件是一个信号，它可以是验证手机号，验证邮箱，验证身份证等一些验证条件产生的enabledSignal。触发事件就是按钮点击之后执行的事件，可以是发送验证码的网络请求。
RACCommand在ReactiveCocoa中算是很特别的一种存在，因为它的实现并不是FRP实现的，是OOP实现的。RACCommand的本质就是一个对象，在这个对象里面封装了4个信号。
关于RACCommand的定义如下：
@interface RACCommand : NSObject @property (nonatomic, strong, readonly) RACSignal *executionSignals; @property (nonatomic, strong, readonly) RACSignal *executing; @property (nonatomic, strong, readonly) RACSignal *enabled; @property (nonatomic, strong, readonly) RACSignal *errors; @property (atomic, assign) BOOL allowsConcurrentExecution; volatile uint32_t _allowsConcurrentExecution; @property (atomic, copy, readonly) NSArray *activeExecutionSignals; NSMutableArray *_activeExecutionSignals; @property (nonatomic, strong, readonly) RACSignal *immediateEnabled; @property (nonatomic, copy, readonly) RACSignal * (^signalBlock)(id input); @end RACCommand中4个最重要的信号就是定义开头的那4个信号，executionSignals，executing，enabled，errors。需要注意的是，这4个信号基本都是（并不是完全是）在主线程上执行的。</description></item><item><title>ReactiveCocoa 中 RACScheduler 是如何封装 GCD 的</title><link>https://new.halfrost.com/zh/reactivecocoa_racscheduler/</link><pubDate>Sat, 31 Dec 2016 01:09:00 +0000</pubDate><guid>https://new.halfrost.com/zh/reactivecocoa_racscheduler/</guid><description>前言 在使用ReactiveCocoa 过程中，Josh Abernathy和Justin Spahr-Summers 两位大神为了能让RAC的使用者更畅快的在沉浸在FRP的世界里，更好的进行并发编程，于是就对GCD进行了一次封装，并与RAC的各大组件进行了完美的整合。
自从有了RACScheduler以后，使整个RAC并发编程的代码里面更加和谐统一，更加顺手，更加“ReactiveCocoa”。
目录 1.RACScheduler是如何封装GCD的 2.RACScheduler的一些子类 3.RACScheduler是如何“取消”并发任务的 4.RACScheduler是如何和RAC其他组件进行完美整合的 一. RACScheduler是如何封装GCD的 RACScheduler在ReactiveCocoa中到底是干嘛的呢？处于什么地位呢？官方给出的定义如下：
Schedulers are used to control when and where work is performedRACScheduler在ReactiveCocoa中是用来控制一个任务，何时何地被执行。它主要是用来解决ReactiveCocoa中并发编程的问题的。
RACScheduler的实质是对GCD的封装，底层就是GCD实现的。
要分析RACScheduler，先来回顾一下GCD。
众所周知，在GCD中，Dispatch Queue主要分为2类，Serial Dispatch Queue 和 Concurrent Dispatch Queue 。其中Serial Dispatch Queue是等待现在执行中处理结束的队列，Concurrent Dispatch Queue是不等待现在执行中处理结束的队列。
生成Dispatch Queue的方法也有2种，第一种方式是通过GCD的API生成Dispatch Queue。
生成Serial Dispatch Queue
dispatch_queue_t serialDispatchQueue = dispatch_queue_create(&amp;#34;com.gcd.SerialDispatchQueue&amp;#34;, DISPATCH_QUEUE_SERIAL); 生成Concurrent Dispatch Queue
dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create(&amp;#34;com.gcd.ConcurrentDispatchQueue&amp;#34;, DISPATCH_QUEUE_CONCURRENT); 第二种方法是直接获取系统提供的Dispatch Queue。系统提供的也分为2类，Main Dispatch Queue 和 Global Dispatch Queue。Main Dispatch Queue 对应着是Serial Dispatch Queue，Global Dispatch Queue 对应着是Concurrent Dispatch Queue。</description></item><item><title>ReactiveCocoa 中 集合类 RACSequence 和 RACTuple 底层实现分析</title><link>https://new.halfrost.com/zh/reactivecocoa_racsequence_ractuple/</link><pubDate>Sun, 25 Dec 2016 07:54:00 +0000</pubDate><guid>https://new.halfrost.com/zh/reactivecocoa_racsequence_ractuple/</guid><description>前言 在OOP的世界里使用FRP的思想来编程，光有函数这种一等公民，还是无法满足我们一些需求的。因此还是需要引用变量来完成各式各样的类的操作行为。
在前几篇文章中详细的分析了RACStream中RACSignal的底层实现。RACStream还有另外一个子类，RACSequence，这个类是RAC专门为集合而设计的。这篇文章就专门分析一下RACSequence的底层实现。
目录 1.RACTuple底层实现分析 2.RACSequence底层实现分析 3.RACSequence操作实现分析 4.RACSequence的一些扩展 一. RACTuple底层实现分析 在分析RACSequence之前，先来看看RACTuple的实现。RACTuple是ReactiveCocoa的元组类。
1. RACTuple @interface RACTuple : NSObject &amp;lt;NSCoding, NSCopying, NSFastEnumeration&amp;gt; @property (nonatomic, readonly) NSUInteger count; @property (nonatomic, readonly) id first; @property (nonatomic, readonly) id second; @property (nonatomic, readonly) id third; @property (nonatomic, readonly) id fourth; @property (nonatomic, readonly) id fifth; @property (nonatomic, readonly) id last; @property (nonatomic, strong) NSArray *backingArray; @property (nonatomic, copy, readonly) RACSequence *rac_sequence; // 这个是专门为sequence提供的一个扩展 @end RACTuple的定义看上去很简单，底层实质就是一个NSArray，只不过封装了一些方法。RACTuple继承了NSCoding, NSCopying, NSFastEnumeration这三个协议。</description></item><item><title>ReactiveCocoa 中 RACSignal 所有变换操作底层实现分析(下)</title><link>https://new.halfrost.com/zh/reactivecocoa_racsignal_operations3/</link><pubDate>Sat, 10 Dec 2016 09:12:00 +0000</pubDate><guid>https://new.halfrost.com/zh/reactivecocoa_racsignal_operations3/</guid><description>前言 紧接着上篇的源码实现分析，继续分析RACSignal的变换操作的底层实现。
目录 1.高阶信号操作 2.同步操作 3.副作用操作 4.多线程操作 5.其他操作 一. 高阶信号操作 高阶操作大部分的操作是针对高阶信号的，也就是说信号里面发送的值还是一个信号或者是一个高阶信号。可以类比数组，这里就是多维数组，数组里面还是套的数组。
1. flattenMap: (在父类RACStream中定义的) flattenMap:在整个RAC中具有很重要的地位，很多信号变换都是可以用flattenMap:来实现的。
map:，flatten，filter，sequenceMany:这4个操作都是用flattenMap:来实现的。然而其他变换操作实现里面用到map:，flatten，filter又有很多。
回顾一下map:的实现：
- (instancetype)map:(id (^)(id value))block { NSCParameterAssert(block != nil); Class class = self.class; return [[self flattenMap:^(id value) { return [class return:block(value)]; }] setNameWithFormat:@&amp;#34;[%@] -map:&amp;#34;, self.name]; } map:的操作其实就是直接原信号进行的 flattenMap:的操作，变换出来的新的信号的值是block(value)。
flatten的实现接下去会具体分析，这里先略过。
filter的实现：
- (instancetype)filter:(BOOL (^)(id value))block { NSCParameterAssert(block != nil); Class class = self.class; return [[self flattenMap:^ id (id value) { block(value) ? return [class return:value] : return class.</description></item><item><title>ReactiveCocoa 中 RACSignal 冷信号和热信号底层实现分析</title><link>https://new.halfrost.com/zh/reactivecocoa_hot_cold_signal/</link><pubDate>Sun, 04 Dec 2016 22:15:00 +0000</pubDate><guid>https://new.halfrost.com/zh/reactivecocoa_hot_cold_signal/</guid><description>前言 关于ReactiveCocoa v2.5中冷信号和热信号的文章中，最著名的就是美团的臧成威老师写的3篇冷热信号的文章：
细说ReactiveCocoa的冷信号与热信号（一）
细说ReactiveCocoa的冷信号与热信号（二）：为什么要区分冷热信号
细说ReactiveCocoa的冷信号与热信号（三）：怎么处理冷信号与热信号
由于最近在写关于RACSignal底层实现分析的文章，当然也逃不了关于冷热信号操作的分析。这篇文章打算分析分析如何从冷信号转成热信号的底层实现。
目录 1.关于冷信号和热信号的概念 2.RACSignal热信号 3.RACSignal冷信号 4.冷信号是如何转换成热信号的 一. 关于冷信号和热信号的概念 冷热信号的概念是源自于源于.NET框架Reactive Extensions(RX)中的Hot Observable和Cold Observable，
Hot Observable是主动的，尽管你并没有订阅事件，但是它会时刻推送，就像鼠标移动；而Cold Observable是被动的，只有当你订阅的时候，它才会发布消息。
Hot Observable可以有多个订阅者，是一对多，集合可以与订阅者共享信息；而Cold Observable只能一对一，当有不同的订阅者，消息是重新完整发送。
在这篇文章细说ReactiveCocoa的冷信号与热信号（一）详细分析了冷热信号的特点：
热信号是主动的，即使你没有订阅事件，它仍然会时刻推送。而冷信号是被动的，只有当你订阅的时候，它才会发送消息。
热信号可以有多个订阅者，是一对多，信号可以与订阅者共享信息。而冷信号只能一对一，当有不同的订阅者，消息会从新完整发送。
二. RACSignal热信号 RACSignal家族中符合热信号的特点的信号有以下几个。
1.RACSubject @interface RACSubject : RACSignal &amp;lt;RACSubscriber&amp;gt; @property (nonatomic, strong, readonly) NSMutableArray *subscribers; @property (nonatomic, strong, readonly) RACCompoundDisposable *disposable; - (void)enumerateSubscribersUsingBlock:(void (^)(id&amp;lt;RACSubscriber&amp;gt; subscriber))block; + (instancetype)subject; @end 首先来看看RACSubject的定义。
RACSubject是继承自RACSignal，并且它还遵守RACSubscriber协议。这就意味着它既能订阅信号，也能发送信号。
在RACSubject里面有一个NSMutableArray数组，里面装着该信号的所有订阅者。其次还有一个RACCompoundDisposable信号，里面装着该信号所有订阅者的RACDisposable。
RACSubject之所以能称之为热信号，那么它肯定是符合上述热信号的定义的。让我们从它的实现来看看它是如何符合的。
- (RACDisposable *)subscribe:(id&amp;lt;RACSubscriber&amp;gt;)subscriber { NSCParameterAssert(subscriber !</description></item><item><title>ReactiveCocoa 中 RACSignal 所有变换操作底层实现分析(中)</title><link>https://new.halfrost.com/zh/reactivecocoa_racsignal_operations2/</link><pubDate>Tue, 29 Nov 2016 20:52:00 +0000</pubDate><guid>https://new.halfrost.com/zh/reactivecocoa_racsignal_operations2/</guid><description>前言 紧接着上篇的源码实现分析，继续分析RACSignal的变换操作的底层实现。
目录 1.过滤操作 2.组合操作 一. 过滤操作 过滤操作也属于一种变换，根据过滤条件，过滤出符合条件的值。变换出来的新的信号是原信号的一个子集。
1. filter: (在父类RACStream中定义的) 这个filter:操作在any:的实现中用到过了。
- (instancetype)filter:(BOOL (^)(id value))block { NSCParameterAssert(block != nil); Class class = self.class; return [[self flattenMap:^ id (id value) { if (block(value)) { return [class return:value]; } else { return class.empty; } }] setNameWithFormat:@&amp;#34;[%@] -filter:&amp;#34;, self.name]; } filter:中传入一个闭包，是用筛选的条件。如果满足筛选条件的即返回原信号的值，否则原信号的值被“吞”掉，返回空的信号。这个变换主要是用flattenMap的。
2. ignoreValues - (RACSignal *)ignoreValues { return [[self filter:^(id _) { return NO; }] setNameWithFormat:@&amp;#34;[%@] -ignoreValues&amp;#34;, self.name]; } 由上面filter的实现，这里把筛选判断条件永远的传入NO，那么原信号的值都会被变换成empty信号，故变换之后的信号为空信号。</description></item><item><title>ReactiveCocoa 中 RACSignal 所有变换操作底层实现分析(上)</title><link>https://new.halfrost.com/zh/reactivecocoa_racsignal_operations1/</link><pubDate>Sat, 26 Nov 2016 00:42:12 +0000</pubDate><guid>https://new.halfrost.com/zh/reactivecocoa_racsignal_operations1/</guid><description>前言 在上篇文章中，详细分析了RACSignal是创建和订阅的详细过程。看到底层源码实现后，就能发现，ReactiveCocoa这个FRP的库，实现响应式（RP）是用Block闭包来实现的，而并不是用KVC / KVO实现的。
在ReactiveCocoa整个库中，RACSignal占据着比较重要的位置，而RACSignal的变换操作更是整个RACStream流操作核心之一。在上篇文章中也详细分析了bind操作的实现。RACsignal很多变换操作都是基于bind操作来实现的。在开始本篇底层实现分析之前，先简单回顾一下上篇文章中分析的bind函数，这是这篇文章分析的基础。
bind函数可以简单的缩写成下面这样子。
- (RACSignal *)bind:(RACStreamBindBlock (^)(void))block; { return [RACSignal createSignal:^RACDisposable *(id&amp;lt;RACSubscriber&amp;gt; subscriber) { RACStreamBindBlock bindBlock = block(); [self subscribeNext:^(id x) { //(1)  BOOL stop = NO; RACSignal *signal = (RACSignal *)bindBlock(x, &amp;amp;stop); //(2)  if (signal == nil || stop) { [subscriber sendCompleted]; } else { [signal subscribeNext:^(id x) { [subscriber sendNext:x]; //(3)  } error:^(NSError *error) { [subscriber sendError:error]; } completed:^{ }]; } } error:^(NSError *error) { [subscriber sendError:error]; } completed:^{ [subscriber sendCompleted]; }]; return nil; }]; } 当bind变换之后的信号被订阅，就开始执行bind函数中return的block闭包。</description></item><item><title>ReactiveCocoa 中 RACSignal 是如何发送信号的</title><link>https://new.halfrost.com/zh/reactivecocoa_racsignal/</link><pubDate>Mon, 14 Nov 2016 09:48:43 +0000</pubDate><guid>https://new.halfrost.com/zh/reactivecocoa_racsignal/</guid><description>前言 ReactiveCocoa是一个(第一个？)将函数响应式编程范例带入Objective-C的开源库。ReactiveCocoa是由Josh Abernathy和Justin Spahr-Summers 两位大神在对GitHub for Mac的开发过程中编写的。Justin Spahr-Summers 大神在2011年11月13号下午12点35分进行的第一次提交，直到2013年2月13日上午3点05分发布了其1.0 release，达到了第一个重要里程碑。ReactiveCocoa社区也非常活跃，目前最新版已经完成了ReactiveCocoa 5.0.0-alpha.3，目前在5.0.0-alpha.4开发中。
ReactiveCocoa v2.5 是公认的Objective-C最稳定的版本，因此被广大的以OC为主要语言的客户端选中使用。ReactiveCocoa v3.x主要是基于Swift 1.2的版本，而ReactiveCocoa v4.x 主要基于Swift 2.x，ReactiveCocoa 5.0就全面支持Swift 3.0，也许还有以后的Swift 4.0。接下来几篇博客先以ReactiveCocoa v2.5版本为例子，分析一下OC版的RAC具体实现（也许分析完了RAC 5.0就到来了）。也算是写在ReactiveCocoa 5.0正式版到来前夕的祝福吧。
目录 1.什么是ReactiveCocoa？ 2.RAC中的核心RACSignal发送与订阅流程 3.RACSignal操作的核心bind实现 4.RACSignal基本操作concat和zipWith实现 5.最后 一. 什么是ReactiveCocoa？ ReactiveCocoa（其简称为RAC）是由Github 开源的一个应用于iOS和OS X开发的新框架。RAC具有函数式编程(FP)和响应式编程(RP)的特性。它主要吸取了.Net的 Reactive Extensions的设计和实现。
ReactiveCocoa 的宗旨是Streams of values over time ，随着时间变化而不断流动的数据流。
ReactiveCocoa 主要解决了以下这些问题：
UI数据绑定 UI控件通常需要绑定一个事件，RAC可以很方便的绑定任何数据流到控件上。
用户交互事件绑定 RAC为可交互的UI控件提供了一系列能发送Signal信号的方法。这些数据流会在用户交互中相互传递。
解决状态以及状态之间依赖过多的问题 有了RAC的绑定之后，可以不用在关心各种复杂的状态，isSelect，isFinish……也解决了这些状态在后期很难维护的问题。
消息传递机制的大统一 OC中编程原来消息传递机制有以下几种：Delegate，Block Callback，Target-Action，Timers，KVO，objc上有一篇关于OC中这5种消息传递方式改如何选择的文章Communication Patterns，推荐大家阅读。现在有了RAC之后，以上这5种方式都可以统一用RAC来处理。
二. RAC中的核心RACSignal ReactiveCocoa 中最核心的概念之一就是信号RACStream。RACStream中有两个子类——RACSignal 和 RACSequence。本文先来分析RACSignal。</description></item><item><title>Objc 对象的今生今世</title><link>https://new.halfrost.com/zh/objc_life/</link><pubDate>Sun, 30 Oct 2016 17:11:38 +0000</pubDate><guid>https://new.halfrost.com/zh/objc_life/</guid><description>前言 在面向对象编程中，我们每天都在创建对象，用对象描述着整个世界，然而对象是如何从孕育到销毁的呢？
目录 1.孕育对象 2.对象的出生 3.对象的成长 4.对象的销毁 5.总结 一.孕育对象 每天开发我们都在alloc对象，而alloc方法做了些什么呢？
+ (id)alloc { return _objc_rootAlloc(self); } 所有对象alloc都会调用这个root的方法
id _objc_rootAlloc(Class cls) { return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/); } 这个方法又会去调用callAlloc方法
static ALWAYS_INLINE id callAlloc(Class cls, bool checkNil, bool allocWithZone=false) { if (checkNil &amp;amp;&amp;amp; !cls) return nil; #if __OBJC2__  if (! cls-&amp;gt;ISA()-&amp;gt;hasCustomAWZ()) { // No alloc/allocWithZone implementation. Go straight to the allocator.  // fixme store hasCustomAWZ in the non-meta class and  // add it to canAllocFast&amp;#39;s summary  if (cls-&amp;gt;canAllocFast()) { // No ctors, raw isa, etc.</description></item><item><title>Realm 数据库 从入门到“放弃”</title><link>https://new.halfrost.com/zh/realm_ios/</link><pubDate>Sat, 22 Oct 2016 10:12:00 +0000</pubDate><guid>https://new.halfrost.com/zh/realm_ios/</guid><description>前言 由于最近项目中在用Realm，所以把自己实践过程中的一些心得总结分享一下。
Realm是由Y Combinator公司孵化出来的一款可以用于iOS(同样适用于Swift&amp;amp;Objective-C)和Android的跨平台移动数据库。目前最新版是Realm 2.0.2，支持的平台包括Java，Objective-C，Swift，React Native，Xamarin。
Realm官网上说了好多优点，我觉得选用Realm的最吸引人的优点就三点：
跨平台：现在很多应用都是要兼顾iOS和Android两个平台同时开发。如果两个平台都能使用相同的数据库，那就不用考虑内部数据的架构不同，使用Realm提供的API，可以使数据持久化层在两个平台上无差异化的转换。
简单易用：Core Data 和 SQLite 冗余、繁杂的知识和代码足以吓退绝大多数刚入门的开发者，而换用 Realm，则可以极大地学习成本，立即学会本地化存储的方法。毫不吹嘘的说，把官方最新文档完整看一遍，就完全可以上手开发了。
可视化：Realm 还提供了一个轻量级的数据库查看工具，在Mac Appstore 可以下载“Realm Browser”这个工具，开发者可以查看数据库当中的内容，执行简单的插入和删除数据的操作。毕竟，很多时候，开发者使用数据库的理由是因为要提供一些所谓的“知识库”。
“Realm Browser”这个工具调试起Realm数据库实在太好用了，强烈推荐。
如果使用模拟器进行调试,可以通过
[RLMRealmConfiguration defaultConfiguration].fileURL 打印出Realm 数据库地址,然后在Finder中⌘⇧G跳转到对应路径下,用Realm Browser打开对应的.realm文件就可以看到数据啦.
如果是使用真机调试的话“Xcode-&amp;gt;Window-&amp;gt;Devices(⌘⇧2 )”,然后找到对应的设备与项目,点击Download Container，导出xcappdata文件后,显示包内容,进到AppData-&amp;gt;Documents,使用Realm Browser打开.realm文件即可.
自2012年起， Realm 就已经开始被用于正式的商业产品中了。经过4年的使用，逐步趋于稳定。
目录 1.Realm 安装 2.Realm 中的相关术语 3.Realm 入门——如何使用 4.Realm 使用中可能需要注意的一些问题 5.Realm “放弃”——优点和缺点 6.Realm 到底是什么？ 7.总结 一. Realm 安装 使用 Realm 构建应用的基本要求：
iOS 7 及其以上版本, macOS 10.9 及其以上版本，此外 Realm 支持 tvOS 和 watchOS 的所有版本。 需要使用 Xcode 7.</description></item><item><title>iOS 如何实现 Aspect Oriented Programming</title><link>https://new.halfrost.com/zh/ios_aspect/</link><pubDate>Sat, 15 Oct 2016 09:54:00 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_aspect/</guid><description>前言 在“Runtime病院”住院的后两天，分析了一下AOP的实现原理。“出院”后，发现Aspect库还没有详细分析，于是就有了这篇文章，今天就来说说iOS 是如何实现Aspect Oriented Programming。
目录 1.Aspect Oriented Programming简介 2.什么是Aspects 3.Aspects 中4个基本类 解析 4.Aspects hook前的准备工作 5.Aspects hook过程详解 6.关于Aspects的一些 “坑” 一.Aspect Oriented Programming简介 面向切面的程序设计（aspect-oriented programming，AOP，又译作面向方面的程序设计、观点导向编程、剖面导向程序设计）是计算机科学中的一个术语，指一种程序设计范型。该范型以一种称为侧面（aspect，又译作方面）的语言构造为基础，侧面是一种新的模块化机制，用来描述分散在对象、类或函数中的横切关注点（crosscutting concern）。
侧面的概念源于对面向对象的程序设计的改进，但并不只限于此，它还可以用来改进传统的函数。与侧面相关的编程概念还包括元对象协议、主题（subject）、混入（mixin）和委托。
AOP通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
OOP（面向对象编程）针对业务处理过程的实体及其属性和行为进行抽象封装，以获得更加清晰高效的逻辑单元划分。
AOP则是针对业务处理过程中的切面进行提取，它所面对的是处理过程中的某个步骤或阶段，以获得逻辑过程中各部分之间低耦合性的隔离效果。
OOP和AOP属于两个不同的“思考方式”。OOP专注于对象的属性和行为的封装，AOP专注于处理某个步骤和阶段的，从中进行切面的提取。
举个例子，如果有一个判断权限的需求，OOP的做法肯定是在每个操作前都加入权限判断。那日志记录怎么办？在每个方法的开始结束的地方都加上日志记录。AOP就是把这些重复的逻辑和操作，提取出来，运用动态代理，实现这些模块的解耦。OOP和AOP不是互斥，而是相互配合。
在iOS里面使用AOP进行编程，可以实现非侵入。不需要更改之前的代码逻辑，就能加入新的功能。主要用来处理一些具有横切性质的系统性服务，如日志记录、权限管理、缓存、对象池管理等。
二. 什么是Aspects Aspects是一个轻量级的面向切面编程的库。它能允许你在每一个类和每一个实例中存在的方法里面加入任何代码。可以在以下切入点插入代码：before(在原始的方法前执行) / instead(替换原始的方法执行) / after(在原始的方法后执行,默认)。通过Runtime消息转发实现Hook。Aspects会自动的调用super方法，使用method swizzling起来会更加方便。
这个库很稳定，目前用在数百款app上了。它也是PSPDFKit的一部分，PSPDFKit是一个iOS 看PDF的framework库。作者最终决定把它开源出来。
三.Aspects 中4个基本类 解析 我们从头文件开始看起。
1.Aspects.h typedef NS_OPTIONS(NSUInteger, AspectOptions) { AspectPositionAfter = 0, /// Called after the original implementation (default)  AspectPositionInstead = 1, /// Will replace the original implementation.</description></item><item><title>神经病院 Objective-C Runtime 出院第三天——如何正确使用 Runtime</title><link>https://new.halfrost.com/zh/how_to_use_runtime/</link><pubDate>Fri, 30 Sep 2016 05:00:00 +0000</pubDate><guid>https://new.halfrost.com/zh/how_to_use_runtime/</guid><description>前言 到了今天终于要&amp;quot;出院&amp;quot;了，要总结一下住院几天的收获，谈谈Runtime到底能为我们开发带来些什么好处。当然它也是把双刃剑，使用不当的话，也会成为开发路上的一个大坑。
####目录
1.Runtime的优点 (1) 实现多继承Multiple Inheritance (2) Method Swizzling (3) Aspect Oriented Programming (4) Isa Swizzling (5) Associated Object关联对象 (6) 动态的增加方法 (7) NSCoding的自动归档和自动解档 (8) 字典和模型互相转换 2.Runtime的缺点 一. 实现多继承Multiple Inheritance 在上一篇文章里面讲到的forwardingTargetForSelector:方法就能知道，一个类可以做到继承多个类的效果，只需要在这一步将消息转发给正确的类对象就可以模拟多继承的效果。
在官方文档上记录了这样一段例子。
在OC程序中可以借用消息转发机制来实现多继承的功能。 在上图中，一个对象对一个消息做出回应，类似于另一个对象中的方法借过来或是“继承”过来一样。 在图中，warrior实例转发了一个negotiate消息到Diplomat实例中，执行Diplomat中的negotiate方法，结果看起来像是warrior实例执行了一个和Diplomat实例一样的negotiate方法，其实执行者还是Diplomat实例。
这使得不同继承体系分支下的两个类可以“继承”对方的方法，这样一个类可以响应自己继承分支里面的方法，同时也能响应其他不相干类发过来的消息。在上图中Warrior和Diplomat没有继承关系，但是Warrior将negotiate消息转发给了Diplomat后，就好似Diplomat是Warrior的超类一样。
消息转发提供了许多类似于多继承的特性，但是他们之间有一个很大的不同：
多继承：合并了不同的行为特征在一个单独的对象中，会得到一个重量级多层面的对象。
消息转发：将各个功能分散到不同的对象中，得到的一些轻量级的对象，这些对象通过消息通过消息转发联合起来。
这里值得说明的一点是，即使我们利用转发消息来实现了“假”继承，但是NSObject类还是会将两者区分开。像respondsToSelector:和 isKindOfClass:这类方法只会考虑继承体系，不会考虑转发链。比如上图中一个Warrior对象如果被问到是否能响应negotiate消息：
if ( [aWarrior respondsToSelector:@selector(negotiate)] ) 结果是NO，虽然它能够响应negotiate消息而不报错，但是它是靠转发消息给Diplomat类来响应消息的。
如果非要制造假象，反应出这种“假”的继承关系，那么需要重新实现 respondsToSelector:和 isKindOfClass:来加入你的转发算法：
- (BOOL)respondsToSelector:(SEL)aSelector { if ( [super respondsToSelector:aSelector] ) return YES; else { /* Here, test whether the aSelector message can * * be forwarded to another object and whether that * * object can respond to it.</description></item><item><title>神经病院 Objective-C Runtime 住院第二天——消息发送与转发</title><link>https://new.halfrost.com/zh/objc_runtime_objc_msgsend/</link><pubDate>Sat, 17 Sep 2016 01:47:58 +0000</pubDate><guid>https://new.halfrost.com/zh/objc_runtime_objc_msgsend/</guid><description>前言 现在越来越多的app都使用了JSPatch实现app热修复，而JSPatch 能做到通过 JS 调用和改写 OC 方法最根本的原因是 Objective-C 是动态语言，OC 上所有方法的调用/类的生成都通过 Objective-C Runtime 在运行时进行，我们可以通过类名/方法名反射得到相应的类和方法，也可以替换某个类的方法为新的实现，理论上你可以在运行时通过类名/方法名调用到任何 OC 方法，替换任何类的实现以及新增任意类。今天就来详细解析一下OC中runtime最为吸引人的地方。
####目录
1.objc_msgSend函数简介 2.消息发送Messaging阶段—objc_msgSend源码解析 3.消息转发Message Forwarding阶段 4.forwardInvocation的例子 5.入院考试 6.Runtime中的优化 一.objc_msgSend函数简介 最初接触到OC Runtime，一定是从[receiver message]这里开始的。[receiver message]会被编译器转化为：
id objc_msgSend ( id self, SEL op, ... ); 这是一个可变参数函数。第二个参数类型是SEL。SEL在OC中是selector方法选择器。
typedef struct objc_selector *SEL; objc_selector是一个映射到方法的C字符串。需要注意的是@selector()选择子只与函数名有关。不同类中相同名字的方法所对应的方法选择器是相同的，即使方法名字相同而变量类型不同也会导致它们具有相同的方法选择器。由于这点特性，也导致了OC不支持函数重载。
在receiver拿到对应的selector之后，如果自己无法执行这个方法，那么该条消息要被转发。或者临时动态的添加方法实现。如果转发到最后依旧没法处理，程序就会崩溃。
所以编译期仅仅是确定了要发送消息，而消息如何处理是要运行期需要解决的事情。
objc_msgSend函数究竟会干什么事情呢？从这篇「objc_msgSend() Tour」文章里面可以得到一个比较详细的结论。
1. Check for ignored selectors (GC) and short-circuit. 2. Check for nil target. If nil &amp;amp; nil receiver handler configured, jump to handler If nil &amp;amp; no handler (default), cleanup and return.</description></item><item><title>神经病院 Objective-C Runtime 入院第一天—— isa 和 Class</title><link>https://new.halfrost.com/zh/objc_runtime_isa_class/</link><pubDate>Sat, 10 Sep 2016 01:25:00 +0000</pubDate><guid>https://new.halfrost.com/zh/objc_runtime_isa_class/</guid><description>前言 我第一次开始重视Objective-C Runtime是从2014年11月1日，@唐巧老师在微博上发的一条微博开始。
这是sunnyxx在线下的一次分享会。会上还给了4道题目。
这4道题以我当时的知识，很多就不确定，拿不准。从这次入院考试开始，就成功入院了。后来这两年对Runtime的理解慢慢增加了，打算今天自己总结总结平时一直躺在我印象笔记里面的笔记。有些人可能有疑惑，学习Runtime到底有啥用，平时好像并不会用到。希望看完我这次的总结，心中能解开一些疑惑。
####目录
1.Runtime简介 2.NSObject起源 (1) isa_t结构体的具体实现 (2) cache_t的具体实现 (3) class_data_bits_t的具体实现 3.入院考试 一. Runtime简介 Runtime 又叫运行时，是一套底层的 C 语言 API，是 iOS 系统的核心之一。开发者在编码过程中，可以给任意一个对象发送消息，在编译阶段只是确定了要向接收者发送这条消息，而接受者将要如何响应和处理这条消息，那就要看运行时来决定了。
C语言中，在编译期，函数的调用就会决定调用哪个函数。 而OC的函数，属于动态调用过程，在编译期并不能决定真正调用哪个函数，只有在真正运行时才会根据函数的名称找到对应的函数来调用。
Objective-C 是一个动态语言，这意味着它不仅需要一个编译器，也需要一个运行时系统来动态得创建类和对象、进行消息传递和转发。
Objc 在三种层面上与 Runtime 系统进行交互：
1. 通过 Objective-C 源代码 一般情况开发者只需要编写 OC 代码即可，Runtime 系统自动在幕后把我们写的源代码在编译阶段转换成运行时代码，在运行时确定对应的数据结构和调用具体哪个方法。
2. 通过 Foundation 框架的 NSObject 类定义的方法 在OC的世界中，除了NSProxy类以外，所有的类都是NSObject的子类。在Foundation框架下，NSObject和NSProxy两个基类，定义了类层次结构中该类下方所有类的公共接口和行为。NSProxy是专门用于实现代理对象的类，这个类暂时本篇文章不提。这两个类都遵循了NSObject协议。在NSObject协议中，声明了所有OC对象的公共方法。
在NSObject协议中，有以下5个方法，是可以从Runtime中获取信息，让对象进行自我检查。
- (Class)class OBJC_SWIFT_UNAVAILABLE(&amp;#34;use &amp;#39;anObject.dynamicType&amp;#39; instead&amp;#34;); - (BOOL)isKindOfClass:(Class)aClass; - (BOOL)isMemberOfClass:(Class)aClass; - (BOOL)conformsToProtocol:(Protocol *)aProtocol; - (BOOL)respondsToSelector:(SEL)aSelector; -class方法返回对象的类； -isKindOfClass: 和 -isMemberOfClass: 方法检查对象是否存在于指定的类的继承体系中； -respondsToSelector: 检查对象能否响应指定的消息； -conformsToProtocol:检查对象是否实现了指定协议类的方法；</description></item><item><title>深入研究 Block 用 weakSelf、strongSelf、@weakify、@strongify 解决循环引用</title><link>https://new.halfrost.com/zh/ios_block_retain_circle/</link><pubDate>Sun, 04 Sep 2016 11:38:04 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_block_retain_circle/</guid><description>前言 在上篇中，仔细分析了一下Block的实现原理以及__block捕获外部变量的原理。然而实际使用Block过程中，还是会遇到一些问题，比如Retain Circle的问题。
####目录
1.Retain Circle的由来 2.__weak、__strong的实现原理 3.weakSelf、strongSelf的用途 4.@weakify、@strongify实现原理 一. Retain Circle的由来 循环引用的问题相信大家都很理解了，这里还是简单的提一下。
当A对象里面强引用了B对象，B对象又强引用了A对象，这样两者的retainCount值一直都无法为0，于是内存始终无法释放，导致内存泄露。所谓的内存泄露就是本应该释放的对象，在其生命周期结束之后依旧存在。
这是2个对象之间的，相应的，这种循环还能存在于3，4……个对象之间，只要相互形成环，就会导致Retain Cicle的问题。
当然也存在自身引用自身的，当一个对象内部的一个obj，强引用的自身，也会导致循环引用的问题出现。常见的就是block里面引用的问题。
二.__weak、__strong的实现原理 在ARC环境下，id类型和对象类型和C语言其他类型不同，类型前必须加上所有权的修饰符。
所有权修饰符总共有4种：
1.__strong修饰符 2.__weak修饰符 3.__unsafe_unretained修饰符 4.__autoreleasing修饰符
一般我们如果不写，默认的修饰符是__strong。
要想弄清楚__strong，__weak的实现原理，我们就需要研究研究clang(LLVM编译器)和objc4 Objective-C runtime库了。
关于clang有一份关于ARC详细的文档，有兴趣的可以仔细研究一下文档里面的说明和例子，很有帮助。
以下的讲解，也会来自于上述文档中的函数说明。
1.__strong的实现原理 (1)对象持有自己 首先我们先来看看生成的对象持有自己的情况，利用alloc/new/copy/mutableCopy生成对象。
当我们声明了一个__strong对象
{ id __strong obj = [[NSObject alloc] init]; } LLVM编译器会把上述代码转换成下面的样子
id __attribute__((objc_ownership(strong))) obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass(&amp;#34;NSObject&amp;#34;), sel_registerName(&amp;#34;alloc&amp;#34;)), sel_registerName(&amp;#34;init&amp;#34;)); 相应的会调用
id obj = objc_msgSend(NSObject, @selector(alloc)); objc_msgSend(obj,selector(init)); objc_release(obj); 上述这些方法都好理解。在ARC有效的时候就会自动插入release代码，在作用域结束的时候自动释放。
(2)对象不持有自己 生成对象的时候不用alloc/new/copy/mutableCopy等方法。</description></item><item><title>深入研究 Block 捕获外部变量和 __block 实现原理</title><link>https://new.halfrost.com/zh/ios_block/</link><pubDate>Sat, 27 Aug 2016 06:54:28 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_block/</guid><description>前言 Blocks是C语言的扩充功能，而Apple 在OS X Snow Leopard 和 iOS 4中引入了这个新功能“Blocks”。从那开始，Block就出现在iOS和Mac系统各个API中，并被大家广泛使用。一句话来形容Blocks，带有自动变量（局部变量）的匿名函数。
Block在OC中的实现如下：
struct Block_layout { void *isa; int flags; int reserved; void (*invoke)(void *, ...); struct Block_descriptor *descriptor; /* Imported variables. */ }; struct Block_descriptor { unsigned long int reserved; unsigned long int size; void (*copy)(void *dst, void *src); void (*dispose)(void *); }; 从结构图中很容易看到isa，所以OC处理Block是按照对象来处理的。在iOS中，isa常见的就是_NSConcreteStackBlock，_NSConcreteMallocBlock，_NSConcreteGlobalBlock这3种(另外只在GC环境下还有3种使用的_NSConcreteFinalizingBlock，_NSConcreteAutoBlock，_NSConcreteWeakBlockVariable，本文暂不谈论这3种，有兴趣的看看官方文档)
以上介绍是Block的简要实现，接下来我们来仔细研究一下Block的捕获外部变量的特性以及__block的实现原理。
研究工具：clang 为了研究编译器的实现原理，我们需要使用 clang 命令。clang 命令可以将 Objetive-C 的源码改写成 C / C++ 语言的，借此可以研究 block 中各个特性的源码实现方式。该命令是
clang -rewrite-objc block.c####目录
1.</description></item><item><title>手把手教你给一个 iOS app 配置多个环境变量</title><link>https://new.halfrost.com/zh/ios_multienvironments/</link><pubDate>Wed, 10 Aug 2016 16:11:00 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_multienvironments/</guid><description>前言 谈到多环境，相信现在大多公司都至少有2-3个app环境了，比如Test环境，UAT(User Acceptance Test)用户验收测试环境，Release环境等等。当需要开发打多个包的时候，一般常见做法就是直接代码里面修改环境变量，改完之后Archive一下就打包了。当然这种做法很正确，只不过不是很优雅很高效。如果搭建好了Jenkins(搭建教程)，我们利用它来优雅的打包。如果利用Jenkins来打包，我们就需要来给app来配置一下多个环境变量了。之后Jenkins分别再不同环境下自动集成即可。接下来，我们来谈谈常见的2种做法。
####目录
1.利用Build Configuration来配置多环境 2.利用xcconfig文件来配置多环境 3.利用Targets来配置多环境 一.利用Build Configuration来配置多环境 前言里面我们先谈到了需求，由于需要配置多个环境，并且多个环境都需要安装到手机上，那么可以配置Build Configuration来完成这个任务。如果Build Configuration还不熟悉的，可以先温习一下官方文档，新版文档链接在这里Build settings reference。
1. 新建Build Configuration 先点击Project里面找到Configuration，然后选择添加，这里新加一个Configuration。系统默认是2个，一个Debug，一个Release。这里我们需要选择是复制一个Debug还是Release。Release和Debug的区别是，Release是不能调试程序，因为默认是屏蔽了可调试的一些参数，具体可以看BuildSetting里面的区别，而且Release编译时有做编译优化，会比用Debug打包出来的体积更小一点。
这里我们选择一个Duplicate “Debug” Configuration，因为我们新的环境需要debug，添加完了之后就会多了一套Configuration了，这一套其实是包含了一些编译参数的配置集合。如果此时项目里面有cocopods的话，打开Configuration Set就会发现是如下的样子：
在我们自己的项目里面用了Pod，打开配置是会看到如下信息
注意：刚刚新建完Build Configuration之后，这时如果有pod，请立即执行一下
pod install pod安装完成之后会自动生成xcconfig文件，如果你手动新建这个xcconfig，然后把原来的debug和release对应的pod xcconfig文件内容复制进来，这样做是无效的，需要pod自己去生成xcconfig文件才能被识别到。
新建完Build Configuration，这个时候需要新建pod里面对应的Build Configuration，要不然一会编译会报错。如果没用pod，可以忽略一下这一段。
如下图新建一个对应之前Porject里面新建的Build Configuration
2. 新建Scheme 接下来我们要为新的Configuration新建一个编译Scheme。
新建完成之后，我们就可以编辑刚刚新建的Scheme，这里可以把Run模式和Archive都改成新建Scheme。如下图：
注意：如果是使用了Git这些协同工具的同学这里还需要把刚刚新建的Scheme共享出去，否则其他人看不到这个Scheme。选择“Manage Schemes”
3. 新建User-defined Build Settings 再次回到Project的Build Settings里面来，Add User-Defined Setting。
我们这里新加入2个参数，CustomAppBundleld是为了之后打包可以分开打成多个包，这里需要3个不同的Id，建议是直接在原来的Bundleld加上Scheme的名字即可。
CustomProductName是为了app安装到手机上之后，手机上显示的名字，这里可以按照对应的环境给予描述，比如测试服，UAT，等等。如下图。
这里值得提到的一点是，下面Pods的Build_DIR这些目录其实是Pods自己生成好的，之前执行过Pod install 之后，这里默认都是配置好的，不需要再改动了。
4. 修改info.plist文件 和 Images.xcassets 先来修改一下info.plist文件。
由于我们新添加了2个CustomAppBundleld 和 CustomProductName，这里我们需要把info.plist里面的Bundle display name修改成我们自定义的这个字典。编译过程中，编译器会根据我们设置好的Scheme去自己选择Debug，Release，TestRelease分别对应的ProductName。
我们还需要在Images.xcassets里面新添加2个New iOS App Icon，名字最好和scheme的名字相同，这样好区分。</description></item><item><title>手把手教你利用 Jenkins 持续集成 iOS 项目</title><link>https://new.halfrost.com/zh/ios_jenkins/</link><pubDate>Sat, 30 Jul 2016 23:04:00 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_jenkins/</guid><description>前言 众所周知，现在App的竞争已经到了用户体验为王，质量为上的白热化阶段。用户们都是很挑剔的。如果一个公司的推广团队好不容易砸了重金推广了一个APP，好不容易有了一些用户，由于一次线上的bug导致一批的用户在使用中纷纷出现闪退bug，轻则，很可能前期推广砸的钱都白费了，重则，口碑不好，未来也提升不起用户量来了。静下心来分析一下问题的原因，无外乎就是质量没有过关就上线了。除去主观的一些因素，很大部分的客观因素我觉得可以被我们防范的。根据大神们提出的一套开发规范建议，CI + FDD，就可以帮助我们极大程度的解决客观因素。本文接下来主要讨论 Continuous Integration 持续集成（简称CI）
####目录
1.为什么我们需要持续集成 2.持续化集成工具——Jenkins 3.iOS自动化打包命令——xcodebuild + xcrun 和 fastlane - gym 命令 4.打包完成自动化上传 fir / 蒲公英 第三方平台 5.完整的持续集成流程 6.Jenkins + Docker 一. 为什么我们需要持续集成 谈到为什么需要的问题，我们就需要从什么是来说起。那什么是持续集成呢。
持续集成是一种软件开发实践：许多团队频繁地集成他们的工作，每位成员通常进行日常集成，进而每天会有多种集成。每个集成会由自动的构建（包括测试）来尽可能快地检测错误。许多团队发现这种方法可以显著的减少集成问题并且可以使团队开发更加快捷。
CI是一种开发实践。实践应该包含3个基本模块，一个可以自动构建的过程，自动编译代码，可以自动分发，部署和测试。一个代码仓库，SVN或者Git。最后一个是一个持续集成的服务器。通过持续集成，可以让我们通过自动化等手段高频率地去获取产品反馈并响应反馈的过程。
那么持续集成能给我们带来些什么好处呢？这里推荐一篇文章，文章中把Continuous integration (CI) and test-driven development (TDD)分成了12个步骤。然而带来的好处成倍增加，有24点好处。
我来说说用了CI以后带来的一些深有体会的优点。
1. 缩减开发周期，快速迭代版本 每个版本开始都会估算好开发周期，但是总会因为各种事情而延期。这其中包括了一些客观因素。由于产品线增多，迭代速度越来越快，给测试带来的压力也越来越大。如果测试都在开发完全开发完成之后再来测试，那就会影响很长一段时间。这时候由于集成晚就会严重拖慢项目节奏。如果能尽早的持续集成，尽快进入上图的12步骤的迭代环中，就可以尽早的暴露出问题，提早解决，尽量在规定时间内完成任务。
2. 自动化流水线操作带来的高效 其实打包对于开发人员来说是一件很耗时，而且没有很大技术含量的工作。如果开发人员一多，相互改的代码冲突的几率就越大，加上没有产线管理机制，代码仓库的代码质量很难保证。团队里面会花一些时间来解决冲突，解决完了冲突还需要自己手动打包。这个时候如果证书又不对，又要耽误好长时间。这些时间其实可以用持续集成来节约起来的。一天两天看着不多，但是按照年的单位来计算，可以节约很多时间！
3. 随时可部署 有了持续集成以后，我们可以以天为单位来打包，这种高频率的集成带来的最大的优点就是可以随时部署上线。这样就不会导致快要上线，到处是漏洞，到处是bug，手忙脚乱弄完以后还不能部署，严重影响上线时间。
4. 极大程度避免低级错误 我们可以犯错误，但是犯低级错误就很不应该。这里指的低级错误包括以下几点：编译错误，安装问题，接口问题，性能问题。 以天为单位的持续集成，可以很快发现编译问题，自动打包直接无法通过。打完包以后，测试扫码无法安装，这种问题也会立即被暴露出来。接口问题和性能问题就有自动化测试脚本来发现。这些低级问题由持续集成来暴露展现出来，提醒我们避免低级错误。
二. 持续化集成工具——Jenkins Jenkins 是一个开源项目，提供了一种易于使用的持续集成系统，使开发者从繁杂的集成中解脱出来，专注于更为重要的业务逻辑实现上。同时 Jenkins 能实施监控集成中存在的错误，提供详细的日志文件和提醒功能，还能用图表的形式形象地展示项目构建的趋势和稳定性。
根据官方定义，Jenkins有以下的用途：
构建项目 跑测试用例检测bug 静态代码检测 部署 关于这4点，实际使用中还是比较方便的： 1.</description></item><item><title>函数响应式编程(FRP)从入门到"放弃"——图解RACSignal篇</title><link>https://new.halfrost.com/zh/ios_rac_racsignal/</link><pubDate>Sun, 24 Jul 2016 10:54:00 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_rac_racsignal/</guid><description>目录 1.RACSignal的创建 2.RACSignal的订阅 3.RACSignal各类操作 ####一.RACSignal的创建
1.创建单元信号
NSError *errorObject = [NSError errorWithDomain:@&amp;quot;Something wrong&amp;quot; code:500 userInfo:nil]; //基本的4种创建方法 RACSignal *signal1 = [RACSignal return:@&amp;quot;Some Value&amp;quot;]; RACSignal *signal2 = [RACSignal error:errorObject]; RACSignal *signal3 = [RACSignal empty]; RACSignal *signal4 = [RACSignal never]; 2.创建动态信号
RACSignal *signal5 = [RACSignal createSignal: ^RACDisposable *(id&amp;lt;RACSubscriber&amp;gt; subscriber) { [subscriber sendNext:@1]; [subscriber sendNext:@2]; [subscriber sendError:errorObject]; [subscriber sendCompleted]; return [RACDisposable disposableWithBlock:^{ }]; }]; 3.通过Cocoa桥接方式获得一个信号
RACSignal *signal6 = [view rac_signalForSelector:@selector(setFrame:)]; RACSignal *signal7 = [view rac_signalForControlEvents:UIControlEventTouchUpInside]; RACSignal *signal8 = [view rac_willDeallocSignal]; //KVO的原理实现 RACSignal *signal9 = RACObserve(view, backgroundColor); 4.</description></item><item><title>关于 IB_DESIGNABLE / IBInspectable 的那些需要注意的事</title><link>https://new.halfrost.com/zh/ios_ib_designable_ibinspectable/</link><pubDate>Fri, 22 Jul 2016 05:13:00 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_ib_designable_ibinspectable/</guid><description>前言 IB_DESIGNABLE / IBInspectable 这两个关键字是在WWDC 2014年&amp;quot;What&amp;rsquo;s New in Interface Builder&amp;quot;这个Session里面，用Swift讲过一个例子。也是随着Xcode 6 新加入的关键字。
这两个关键字是用在我们自定义View上的，目前暂时只能用在UIView的子类中所以系统自带的原生的那些控件使用这个关键字都没有效果。
Live RenderingYou can use two different attributes—@IBDesignable and @IBInspectable—to enable live, interactive custom view design in Interface Builder. When you create a custom view that inherits from the UIView class or the NSView class, you can add the @IBDesignable attribute just before the class declaration. After you add the custom view to Interface Builder (by setting the custom class of the view in the inspector pane), Interface Builder renders your view in the canvas.</description></item><item><title>WWDC 2016 Session 笔记 - Xcode 8 Auto Layout 新特性</title><link>https://new.halfrost.com/zh/wwdc2016_xcode8autolayout_features/</link><pubDate>Sun, 17 Jul 2016 03:36:00 +0000</pubDate><guid>https://new.halfrost.com/zh/wwdc2016_xcode8autolayout_features/</guid><description>####目录
1.Incrementally Adopting Auto Layout 2.Design and Runtime Constraints 3.NSGridView 4.Layout Feedback Loop Debugging ####一.Incrementally Adopting Auto Layout Incrementally Adopting Auto Layout是什么意思呢？在我们IB里面布局我们的View的时候，我们并不需要一次性就添加好所有的constraints。我们可以一步步的增加constraints，简化我们的步骤，而且能让我们的设置起来更加灵活。
再谈新特性之前，先介绍一下这个特性对应的背景来源。
有这样一种场景，试想，我们把一个view放在父view上，这个时候并没有设置constraints，当我们运行完程序，就会出现下图的样子。
看上去一切都还正常。但是一旦当我们把设备旋转90°以后，就会出现下图的样子。
这个时候可以发现，这个View的长，宽，以及top和left的边距都没有发生变化。这时我们并没有设置constraints，这是怎么做到的呢？
在程序的编译期，Auto Layout的引擎会自动隐式的给View加上一些constraints约束，以保证View的大小不会发生变化。这个例子中，View被加上了top，left，width，height这4个约束。
如果我们需要更加动态的resize的行为，就需要我们在IB里面自定义约束了。现在问题就来了，有没有更好的方式来做这件事情？最好是能有一种不用约束的方法，也能达到简单的resize的效果。
现在这个问题有了解决办法。在Xcode8中，我们可以给View指定autoresizing masks，而不用去设置constraints。这就意味着我们可以不用约束，我们也能做到简单的resize的效果。
在Autolayout时代之前，可能会有人认出这种UI方式。这是一种Springs &amp;amp; Struts的UI。我们可以设定边缘约束(注：这里的约束并不是指的是Autolayout里面的constraints，是autoresizing masks里面的规则)，无论View的长宽如何变化，这些View都会跟随着设置了约束的view一起变化。
上述的例子中，Xcode 8 中在没有加如何constraint就可以做到旋转屏幕之后，View的边距并没有发生变化。这是怎么做到的呢？事实上，Xcode 8的做法是先取出autoresizing masks，然后把它转换成对应的constraints，这个转换的时机发生在Runtime期间。生成对应的constraints是发生在运行时，而不是编译时的原因是可以给我们开发者更加便利的方式为View添加更加细致的约束。
在View上，我们可以设置translatesAutoresizingMaskIntoConstraints属性。
translatesAutoresizingMaskIntoConstraints == true 假设如果View已经在Interface Builder里面加过constraints，“Show the Size inspector”面板依旧会和以前一样。点击View，查看给它加的所有的constraints，这个时候Autoresizing masks就被忽略了，而且translatesAutoresizingMask的属性也会变成false。如下图，我们这个时候在“Show the Size inspector”面板上面就已经看不到AutoresizingMask的设置面板了。
上图就是在Autolayout时代之前，我们一直使用的是autoresizing masks，但是Autolayout时代来临之后，一旦勾选上了这个Autolayout，之前的AutoresizingMask也就失效了。
回到我们最原始的问题上来，Xcode 8 现在针对View可以支持增量的适用Autolayout。这就意味着我们可以从AutoresizingMask开始，先做简单的resize的工作，然后如果有更加复杂的需求，我们再加上适当的约束constraints来进行适配。简而概之，Xcode 8 Autolayout ≈ AutoresizingMask + Autolayout 。
接下来用一个demo的例子来说明一下Xcode 8 Autolayout新特性。</description></item><item><title>函数响应式编程 ( FRP ) 从入门到"放弃"——基础概念篇</title><link>https://new.halfrost.com/zh/functional_reactive_programming_concept/</link><pubDate>Mon, 11 Jul 2016 01:24:00 +0000</pubDate><guid>https://new.halfrost.com/zh/functional_reactive_programming_concept/</guid><description>前言 研究ReactiveCocoa一段时间了，是时候总结一下学到的一些知识了。
一.函数响应式编程 说道函数响应式编程，就不得不提到函数式编程，它们俩到底有什么关系呢？今天我们就详细的解析一下他们的关系。
现在有下面4个概念，需要我们理清一下它们之间的关系：
面向对象编程 Object Oriented Programming
响应式编程 Reactive Programming
函数式编程 Functional Programming
函数响应式编程 Functional Reactive Programming
我们先来说说什么是函数式编程Functional Programming，我们先来看看wikipedia上的相关定义：
Functional Programming is a programming paradigm
treats computation as the evaluation of mathematical functions. avoids changing-state and mutable data 总结一下函数式编程具有以下几个特点：
函数是&amp;quot;第一等公民&amp;rdquo; 闭包和高阶函数 不改变状态(由此延伸出”引用透明”的概念) 递归 只用&amp;quot;表达式&amp;rdquo;，不用&amp;quot;语句&amp;rdquo;，没有副作用 接下来我们依次说明一下这些特点。
一. 函数是&amp;quot;第一等公民&amp;rdquo; 所谓&amp;quot;第一等公民&amp;rdquo;（first class），指的是函数与其他数据类型一样，处于平等地位，可以赋值给其他变量，也可以作为参数，传入另一个函数，或者作为别的函数的返回值。
一等函数的理念可以追溯到 Church 的 lambda 演算 (Church 1941; Barendregt 1984)。此后，包括 Haskell，OCaml，Standard ML，Scala 和 F# 在内的大量 (函数式) 编程语言都不同程度地借鉴了这个概念。</description></item><item><title>WWDC 2016 Session 笔记 - iOS 10 UICollectionView 新特性</title><link>https://new.halfrost.com/zh/wwdc2016_ios10_uicollectionview_new_features/</link><pubDate>Sun, 03 Jul 2016 09:02:00 +0000</pubDate><guid>https://new.halfrost.com/zh/wwdc2016_ios10_uicollectionview_new_features/</guid><description>前言 关于 iOS 10 UICollectionView的新特性，主要还是体现在如下3个方面
顺滑的滑动体验 现在基本上人人都离不开手机，手机的app也每天都有人在用。一个app的好坏由它的用户体验决定。在可以滑动的视图里面，必须要更加丝滑柔顺才能获得用户的青睐。这些UICollectionView的新特性可以让你们的app比原来更加顺滑，而且这些特性只需要你加入少量的代码即可达到目的。 针对self-sizing的改进 self-sizing的API在iOS8的时候被引进，iOS10中加入更多特性使cell更加容易去适配。 Interactive reordering重排 这个功能在iOS9的时候介绍过了，苹果在iOS 10的API里面大大增强了这一功能。 ####目录
1.UICollectionViewCell顺滑的滑动体验 2.UICollectionViewCell的Pre-Fetching预加载 3.UITableViewCell的Pre-Fetching预加载 4.针对self-sizing的改进 5.Interactive Reordering 6.UIRefreshControl 一. UICollectionViewCell顺滑的滑动体验 众所周知，iOS设备已良好的用户体验赢得了广大的用户群。iOS系统在用户点击屏幕会立即做出响应。而且很大一部分的操作是来自于用户的滑动操作。所以滑动的顺滑是使用户沉浸在app中享受的必要条件。接下来我们就谈谈iOS 10 中增加了那些新特性。
我们先来看一下之前 UICollectionView 的体验，假设我们每个cell都是简单的蓝色，实际开发app中，cell会比这复杂很多。 我们先生成100个cell。当用户滑动不是很快的时候，还感觉不出来卡顿，当用户大幅度滑动，整个UICollectionView的卡顿就很明显了。如果整个cell的DataSource又是从网络加载的，那就更加卡顿了。效果如下图。
如果这种app上架，用户使用过后，很可能就直接给1星评价了。但是为什么会造成这种问题呢？我们来分析一下，我们模拟一下系统如何处理重用机制的，效果如下图
在上图中，我们可以看出，当cell准备加载进屏幕的时候，整个cell都已经加载完成，等待在屏幕外面了。而且更重要的是，在屏幕外面等待加载的cell是整整一行！这一行的cell都已经加载完数据。这是UICollectionView在用户大幅度滑动时卡顿的根本原因。用专业的术语来说，掉帧。
接下来我们就来详细的说说掉帧的问题。
当今的用户是很挑剔的，用户需要一个很顺滑的体验，只要有一点卡顿，很可能一言不合就卸载app了。要想用户感觉不到卡顿，那么我们的app必须帧率达到60帧/秒。用数学换算一下就是每帧16毫秒就必须刷新一次。
我们用图标来分析一下掉帧的问题。下面会出现2种不同的帧。
第一种情况，下图是当用户轻微的上下小幅度滑动。这个时候每个cell的加载压力都不大，iOS针对这种情况，已经做了很好的优化了，所以用户感觉不到任何卡顿。这种情况是不会掉帧，用户也希望能使用如此顺滑的app。
第二种情况，当用户大幅度滑动，每个cell加载的压力很大，也许需要网络请求，也许需要读取数据库，而且每次都加载一行cell出来，这样每个cell的加载时间都增加了，加载一行的总时间也就大大增加了，如下图所示。这样，不仅仅当前帧在加载cell，总的时间还会挤压到下一帧的时间里面去。这种情况下，用户就感觉到了卡顿了。
我们换种方式在说明一下2种情况下掉帧的情况。我们用下图的标准来衡量一下上面2种情况。下图分为2部分，上面红色的区域，就是表示掉帧的区域，因为高于16ms。红色和绿色区域的分界线就在16ms处。y轴我们表示的是CPU在主线程中花费的时间。x轴表示的是在用户滑动中发生的刷新事件。
针对上述掉帧的情况，绘制出实验数据，如下图。值得我们关注的是，曲线是很曲折的，非常的不平滑。当用户大幅度滑动的时候，峰值超过了16ms，当用户慢速滑动的时候，帧率又能保持在比较顺滑的区域。处于绿色区域内的cell加载压力都是很小的。这就是时而掉帧时而顺滑的场景。这种场景下，用户体验是很糟糕的。
那怎么解决这么问题的呢？我们来看下图：
上图中的曲线我们看着就很平缓了，而且这种情况也不会出现掉帧的情况了，每个滑动中的时间都能达到60帧了。这是怎样做到的呢？因为把每个cell的加载事件都平分了，每个cell不会再出现很忙和很闲的两个极端。这样我们就取消了之前的波峰和波谷。从而让该曲线达到近乎水平的直线。
如何让每个cell都分摊加载任务的压力？这就要谈到新的cell的生命周期了。
先来看看老的 UICollectionViewCell的声明周期。当用户滑动屏幕，屏幕外有一个cell准备加载显示进来。
这个时候我们把这个cell从reuse队列里面拿出来，然后调用prepareForReuse方法。这个方法就给了cell时间，用来重置cell，重置状态，刷新cell，加载新的数据。
再滑动，我们就会调用cellForItemAtIndexPath方法了。这个方法里面就是我们开发者自定义的填充cell的方式了。这里会填充data model，然后赋值给cell，再把cell返回给iOS系统。
当cell马上要进入屏幕的时候，就会调用willDisplayCell的方法。这个方法给了我们app最后一次机会，为cell进入屏幕做最后的准备工作。执行完willDisplayCell之后，cell就进入屏幕了。
当cell完全离开屏幕之后，就会调用didEndDisplayingCell方法。以上就是在iOS10之前的整个UICollectionViewCell的生命周期。
接下来我们就来看看iOS 10的UICollectionViewCell生命周期是怎么样的。
这里还是和iOS9一样的，当用户滑动UICollectionView的时候，需要一个cell，我们就从reuse队列里面拿出一个cell，并调用prepareForReuse方法。注意调用这个方法的时间，当cell还没有进入屏幕的时候，就已经提前调用这个方法了。注意对比和iOS 9的区别，iOS 9 是在cell上边缘马上进入屏幕的时候才调用方法，而这里，cell整个生命周期都被提前了，提前到cell还在设备外面的时候。
这里还是和之前一样，在cellForItemAtIndexPath中创建cell，填充数据，刷新状态等等操作。注意，这里生命周期也比iOS 9提前了。
用户继续滑动，这个时候就有不同了！
这个时候我们并不去调用willDisplayCell方法了！这里遵循的原则是，何时去显示，何时再去调用willDisplayCell。
当cell要马上就需要显示的时候，我们再调用willDisplayCell方法。
当整个cell要从UICollectionView的可见区域消失的时候，这个时候会调用didEndDisplayingCell方法。接下来发生的事情和iOS9一样，cell会进入重用队列中。
如果用户想要显示某个cell，在iOS 9 当中，cell只能从重用队列里面取出，再次走一遍生命周期。并调用cellForItemAtIndexPath去创建或者生成一个cell。
在iOS 10 当中，系统会把cell保持一段时间。在iOS中，如果用户把cell滑出屏幕后，如果突然又想回来，这个时候cell并不需要再走一段的生命周期了。只需要直接调用willDisplayCell就可以了。cell就又会重新出现在屏幕中。这就是iOS 10 的整个UICollectionView的生命周期。</description></item><item><title>WWDC 2016 Session 笔记 - iOS 10 推送 Notification 新特性</title><link>https://new.halfrost.com/zh/wwdc2016_ios10_notification_new_features/</link><pubDate>Sun, 26 Jun 2016 06:22:00 +0000</pubDate><guid>https://new.halfrost.com/zh/wwdc2016_ios10_notification_new_features/</guid><description>前言 在今年6月14号苹果WWDC开发者大会上，苹果带来了新的iOS系统——iOS 10。苹果为iOS 10带来了十大项更新。苹果高级副总裁Craig Federighi称此次对iOS的更新是“苹果史上最大的iOS更新”。
新的屏幕通知查看方式：苹果为iOS 10带来了全新的通知查看功能，即抬起iPhone的屏幕，用户就能看到目前的通知和更新情况。 苹果将Siri开放给第三方开发者: 现在用户可以让Siri实现更多的功能，例如让Siri向自己的联系人发送微信信息等。目前Siri可以直接支持的应用有微信、WhatsApp以及Uber、滴滴、Skype等。 Siri将会更加智能：Siri将拥有更多对语境的意识。基于用户的地点、日历、联系人、联系地址等，Siri会做出智能建议。Siri将越来越成为一个人工智能机器人，具备深度学习功能。 照片应用更新：基于深度学习技术，iOS 10对照片应用有比较大的更新。iOS 10对照片的搜索能力进一步增强，可以检测到新的人物和景色。未来的iPhone能够将相关的照片组织在一起，比如某次旅行的照片、某个周末的照片，并且能够进行自动编辑。iOS 10照片还新增了一个“记忆”标签。 苹果地图：有点类似Siri和照片的更新，苹果地图也增加了很多预测功能，例如苹果地图能够将提供附近的餐厅建议。苹果地图的界面也得到了重新设计，更加的简洁，并增加了交通实时信息。新的苹果地图还将整合在苹果CarPlay中，将为用户提供turn-by-turn导航功能。和Siri一样，地图也将开放给开发者。 苹果音乐：苹果音乐的界面得到了更新，界面会更加简洁、支持多任务，增加最近播放列表。苹果音乐现在已经有1500万付费用户。 HomeKit：iOS 10新增智能家庭应用，支持一键场景模式，HomeKit可以与Siri相连接。 苹果电话：苹果更新了电话功能，来电时可以区别出骚扰电话。
iMesseage：在iMessage方面，用户可以直接在文本框内发送视频、链接，分享实时照片。另外，苹果还增添了表情预测功能，打出的文字若和表情相符，将会直接推荐相关表情。 以下是我关于关于iOS 10中变化比较大的推送通知的学习笔记。
####目录
1.Notification User Interface 2.Media Attachments 3.Customize user interface 4.Customize Actions 一. Notification User Interface 让我们先来看看用户推送在iOS X中的样子，如下图
上图这是在锁屏界面下的推送。支持抬起手机唤醒功能。
上图是Banner，可以看到这个推送更加的易读，并且包含更多的内容。
上图是通知中心。从上面三种图可以看到，它们都长一个样。
在iOS 8 中，我们可以给推送增加用户操作，这样使推送更加具有交互性，并且允许用户去处理用户推送更加的迅速。到了iOS 9 中，苹果又再次增加了快速回复功能，进一步的提高了通知的响应性。开发者可以允许用户通过点击推送，并用文字进行回复。再就到了iOS 10 中，推送变得更加给力。因为在iOS X中，推送对iOS系统来说，是很重要的一部分。在日常使用中，我们会经常和推送打交道。推送是我们和设备进行互动非常重要的方式。
在iOS X 中，你可以按压推送，推送就会被展开，展示出更加详细的用户界面。展示出来的详细界面对用户来说，提供了更加有用的信息。用户可以通过点击下面的按钮，来处理一些事件，并且推送的详细界面也会跟着用户的操作进行更新UI界面。
iOS 8 中iMessage支持了快速回复功能，但是你只能看见一条信息，并且你也只能回复一条信息。但是在iOS X中，你可以展开推送，这个时候你就可以看到整个对话的内容了。你可以等待你的朋友回复，你再回复他，并且可以回复很多条。
以上就是iOS X的强大功能。以上的所有功能都能通过iOS X的新API来实现。所有的新特性都能在我们开发者开发的app里面有所体现。
二. Media Attachments 如果经常使用iMessage的朋友们，就会经常收到一些信息，附带了一些照片或者视频，所以推送中能附带这些多媒体是非常重要的。如果推送中包含了这些多媒体信息，可以使用户不用打开app，不用下载就可以快速浏览到内容。众所周知，推送通知中带了push payload，及时去年苹果已经把payload的size提升到了4k bites，但是这么小的容量也无法使用户能发送一张高清的图片，甚至把这张图的缩略图包含在推送通知里面，也不一定放的下去。在iOS X中，我们可以使用新特性来解决这个问题。我们可以通过新的service extensions来解决这个问题。</description></item><item><title>iOS 如何优雅的处理“回调地狱 Callback hell ”(二) —— 使用 Swift</title><link>https://new.halfrost.com/zh/ios_callback_hell_swift/</link><pubDate>Wed, 22 Jun 2016 06:45:00 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_callback_hell_swift/</guid><description>####前言 在上篇中，我谈到了可以用promise来解决Callback hell的问题，这篇我们换一种方式一样可以解决这个问题。
我们先分析一下为何promise能解决多层回调嵌套的问题，经过上篇的分析，我总结也一下几点：
1.promise封装了所有异步操作，把异步操作封装成了一个“盒子”。 2.promise提供了Monad，then相当于flatMap。 3.promise的函数返回对象本身，于是就可形成链式调用
好了，既然这些能优雅的解决callback hell，那么我们只要能做到这些，也一样可以完成任务。到这里大家可能就已经恍然大悟了，Swift就是完成这个任务的最佳语言！Swift支持函数式编程，分分钟就可以完成promise的基本功能。
####一.利用Swift特性处理回调Callback hell
我们还是以上篇的例子来举例，先来描述一下场景： 假设有这样一个提交按钮，当你点击之后，就会提交一次任务。当你点下按钮的那一刻，首先要先判断是否有权限提交，没有权限就弹出错误。有权限提交之后，还要请求一次，判断当前任务是否已经存在，如果存在，弹出错误。如果不存在，这个时候就可以安心提交任务了。
那么代码如下：
func requestAsyncOperation(request : String , success : String -&amp;gt; Void , failure : NSError -&amp;gt; Void) { WebRequestAPI.fetchDataAPI(request, success : { result in WebOtherRequestAPI.fetchOtherDataAPI ( result , success : {OtherResult in [self fulfillData:OtherResult]; let finallyTheParams = self.transformResult(OtherResult) TaskAPI.fetchOtherDataAPI ( finallyTheParams , success : { TaskResult in let finallyTaskResult = self.transformTaskResult(TaskResult) success(finallyTaskResult) }, failure:{ TaskError in failure(TaskError) } ) },failure : { ExistError in failure(ExistError) } ) } , failure : { AuthorityError in failure(AuthorityError) } ) } 接下来我们就来优雅的解决上述看上去不好维护的Callback hell。</description></item><item><title>iOS 如何优雅的处理“回调地狱 Callback hell ”(一) —— 使用 PromiseKit</title><link>https://new.halfrost.com/zh/ios_callback_hell_promisekit/</link><pubDate>Fri, 10 Jun 2016 03:51:00 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_callback_hell_promisekit/</guid><description>####前言 最近看了一些Swift关于封装异步操作过程的文章，比如RxSwift，RAC等等，因为回调地狱我自己也写过，很有感触，于是就翻出了Promise来研究学习一下。现将自己的一些收获分享一下，有错误欢迎大家多多指教。
####目录
1.PromiseKit简介 2.PromiseKit安装和使用 3.PromiseKit主要函数的使用方法 4.PromiseKit的源码解析 5.使用PromiseKit优雅的处理回调地狱 ####一.PromiseKit简介 PromiseKit是iOS/OS X 中一个用来处理异步编程框架。这个框架是由Max Howell(Mac下Homebrew的作者，传说中因为&amp;quot;不会&amp;quot;写反转二叉树而没有拿到Google offer)大神级人物开发出来的。
在PromiseKit中，最重要的一个概念就是Promise的概念，Promise是异步操作后的future的一个值。
A promise represents the future value of an asynchronous task. A promise is an object that wraps an asynchronous task
Promise也是一个包装着异步操作的一个对象。使用PromiseKit，能够编写出整洁，有序的代码，逻辑简单的，将Promise作为参数，模块化的从一个异步任务到下一个异步任务中去。用PromiseKit写出的代码就是这样：
[self login].then(^{ // our login method wrapped an async task in a promise  return [API fetchData]; }).then(^(NSArray *fetchedData){ // our API class wraps our API and returns promises  // fetchedData returned a promise that resolves with an array of data  self.</description></item><item><title>手把手教你从 Core Data 迁移到 Realm</title><link>https://new.halfrost.com/zh/ios_coredata_to_realm/</link><pubDate>Thu, 02 Jun 2016 08:03:00 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_coredata_to_realm/</guid><description>前言 看了这篇文章的标题，也许有些人还不知道Realm是什么，那么我先简单介绍一下这个新生的数据库。号称是用来替代SQLite 和 Core Data的。Realm有以下优点：
使用方便 Realm并不是基于SQLite的对象关系映射数据库。它是使用自己的持久化引擎，为简单和速度而生。用户们说，他们在数分钟之内就上手了Realm，构建一个app只需要数小时，每个app开发时间至少节约数周的时间。
快 Realm比其他的对象关系映射型数据库(Object Relational Mapping)，甚至比原生的SQLite更加快，这都得益于它零拷贝的设计。看看iOS用户和Android用户都是怎么评价它的快的Twitter
跨平台 Realm 支持 iOS 和 OS X (Objective‑C &amp;amp; Swift) 和Android。你可以通过使用相同的model，共享Realm文件到各个平台，Java，Swift，Objective-C。并且在全平台可以使用相同的业务逻辑 优秀的特性 Realm支持先进的特性，如加密，图形查询，轻松的迁移。Realm的API是一个非常适合打造高响应的应用程​​序，并且Realm为我们提供方便的组件，以轻松构建复杂的用户界面
值得信任 Realm已经获得了银行，医疗保健提供商，复杂的企业app，星巴克这些产品的青睐。
社区驱动 Realm是Github上星标最多的数据库里面排名第四，仅次于Java 和 Cocoa 的repos。除了核心工程之外，Realm的社区已经编译了上百个app插件和组件
支持 可以从Realm公司快速获得官方的答案，去编译和支持你的数据库。Realm的团队会在Github, StackOverflow, &amp;amp; Twitter回答大家的各种问题
下面再发3张令人惊喜的性能对比图
上图是每秒能在20万条数据中进行查询后count的次数。realm每秒可以进行30.9次查询后count。SQLite仅仅只有每秒13.6次查询后的count，相对于Core Data只有可怜的1。
在20万条中进行一次遍历查询，数据和前面的count相似：Realm一秒可以遍历20万条数据31次，而RCore Data只能进行两次查询。 SQLite也只有14次而已。
这是在一次事务每秒插入数据的对比，Realm每秒可以插入9.4万条记录，在这个比较里纯SQLite的性能最好，每秒可以插入17.8万条记录。然而封装了SQLite的FMDB的成绩大概是Realm的一半，4.7万，Core Data就更低了，只有可怜的1.8万。
从以上3张图可以看出Realm优秀的特性。那么我们开始使用Realm吧。第一步就是把本地的数据库换成Realm。
下面是我翻译的一篇手把手教程，那么让我们赶紧通过教程，来把Core Data迁移到Realm吧。
原文 译文 把一个使用core data框架作为数据库存储方式的app，迁移到Realm的确是一件很容易的事情。如果你现在有一个已经用了Core Data的app，并且考虑换成Realm，这个手把手教程正适合你！
很多开发者在用户界面，高度集成了Core Data(有时可能有上千行代码),这时很多人会告诉你转换Core Data到Realm可能会花数小时。Core Data和Realm两者都是把你的数据当成Object看待，所以迁移通常是很直接的过程:把你已经存在的Core Data的代码重构成使用Realm API的过程是很简单的。</description></item><item><title>iOS app 旧貌换新颜(一) — Launch Page 让 Logo "飞"出屏幕</title><link>https://new.halfrost.com/zh/ios_launchpage_logo_fly/</link><pubDate>Tue, 24 May 2016 14:56:00 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_launchpage_logo_fly/</guid><description>####前言 当今是个看脸的世界，一个app的颜值可能会决定用户的使用次数，icon的颜值更可能决定了用户是否回去下载，如果太丑，放在手机桌面都难看，那用户很可能就直接卸载了。所以漂亮的界面UI + 合理的用户体验UX/UE也会很大程度决定用户的黏性。最近由于公司的app准备美化一下界面，提升性能，所以我就想把美化过程中可以和大家分享的东西都整理整理，拿出来也和大家一起分享学习。这个“旧貌换新颜”我就写成一个系列吧，相信美化的道路是永无止境的！(场外音:自己又给自己开了一个巨坑)
####一.灵感的来源 也许有些人看了文章的标题并不一定完全懂是啥意思，其实设计这个的来源源自于我在微博上看到的一个动图，很生动，形象。
一个呆萌的大叔点开Twitter客户端，启动界面有一个动效，就是他们的logo直接“飞”出屏幕，打在了他的脸上。这个效果我当时看了就觉得很有趣。很多应用每次启动之后都是直接进去，或者先展示一个几秒的广告页。其实要是加一个这种启动特性，感觉也挺不错。
####二.动画原理 接下来说一下上面那个启动特效的原理，其实原理很简单:app在启动之后，先加载一个View，上面放了我们的logo，然后在做一个放大的UIView的动画就好了。接下来看看我的做法吧。
####三.准备工具 PS + AI 或者 Sketch + PaintCode 这个可能有人问了，怎么突然还需要这些作图的工具。其实大家也可以加载一个logo图片放在view上，一样可以实现。不过老板觉得加载一张图片如果太高清会占app大小，能尽量程序画出来的，就让程序画出来。对于不规则复杂的图形，就只好用上面这一套组合工具了。
PS主要是把logo抠出来
AI和Sketch是为了把抠出来的logo用钢笔工具，进行描点，导出路径。
最后PaintCode就是把路径转换成UIBezierPath(PaintCode这个软件很厉害，可以直接把SVG里面的路径直接转换成对应的Swift或者Objective-C代码)(后来我发现其实只要用PaintCode一个软件就可以完成上面所有功能了，它也可以直接用钢笔工具画路径)
####四.开始制作 1.首先用PS把Logo图抠出来，保存成图片。 2.然后打开Sketch，导入刚刚的Logo图片。
3.选择左上角的“Insert”-“Vector”钢笔工具，依次连接Logo图标的各个顶点
4.然后在每段顶点之间，加新的锚点，钢笔工具会出现+号。在软件的右侧，会出现下面这个面板
通过拖拉这些你加出来的点，可以使路径完全吻合Logo复杂的外形。拖过一番拖拽之后，就应该成下面这个图的样子了。
5.接下来我们就选择左边面板上面有一个Page面板
选一下刚刚描出来的Path，右下角会出现一个Export面板
这个时候我们选择导出SVG文件
SVG可缩放矢量图形（Scalable Vector Graphics）是基于 可扩展标记语言（XML），用于描述二维矢量图形的一种图形格式。SVG是W3C(&amp;ldquo;World Wide Web ConSortium&amp;rdquo; 即 &amp;quot; 国际互联网标准组织&amp;rdquo;)在2000年8月制定的一种新的二维矢量图形格式，也是规范中的网络矢量图形标准。SVG严格遵从XML语法，并用文本格式的描述性语言来描述图像内容，因此是一种和图像分辨率无关的矢量图形格式
其实这里有一个小插曲，绘制路径的时候，其实我用的是AI描点的，之后导出SVG给PaintCode，居然不识别我的路径。后来网上问了问，大神要我换Sketch试试，然后就行了。后来我比较了一下Sketch和AI导出的SVG有什么不同，才发现，我之前AI导出的，加了几个图层，把路径盖住了。用AI绘制路径的方法和Sketch的差不多，如下图。
6.把之前导出的SVG文件导入到PaintCode中，下面会自动生成Objective-C代码
把生成的这些代码复制出来。
//// Color Declarations UIColor* color1 = [UIColor colorWithRed: 0.521 green: 0.521 blue: 0.521 alpha: 1]; //// Bezier Drawing //// Page-1 { //// Bezier 2 Drawing UIBezierPath* bezier2Path = UIBezierPath.</description></item><item><title>微信,QQ 这类 IM app 怎么做——谈谈 Websocket</title><link>https://new.halfrost.com/zh/ios_weixin_qq_websocket/</link><pubDate>Sun, 15 May 2016 02:55:00 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_weixin_qq_websocket/</guid><description>####前言 关于我和WebSocket的缘：我从大二在计算机网络课上听老师讲过之后，第一次使用就到了毕业之后的第一份工作。直到最近换了工作，到了一家是含有IM社交聊天功能的app的时候，我觉得我现在可以谈谈我对WebSocket/Socket的一些看法了。要想做IM聊天app，就不得不理解WebSocket和Socket的原理了，听我一一道来。
####目录
1.WebSocket使用场景 2.WebSocket诞生由来 3.谈谈WebSocket协议原理 4.WebSocket 和 Socket的区别与联系 5.iOS平台有哪些WebSocket和Socket的开源框架 6.iOS平台如何实现WebSocket协议 #####一.WebSocket的使用场景 1.社交聊天
最著名的就是微信，QQ，这一类社交聊天的app。这一类聊天app的特点是低延迟，高即时。即时是这里面要求最高的，如果有一个紧急的事情，通过IM软件通知你，假设网络环境良好的情况下，这条message还无法立即送达到你的客户端上，紧急的事情都结束了，你才收到消息，那么这个软件肯定是失败的。
2.弹幕
说到这里，大家一定里面想到了A站和B站了。确实，他们的弹幕一直是一种特色。而且弹幕对于一个视频来说，很可能弹幕才是精华。发弹幕需要实时显示，也需要和聊天一样，需要即时。
3.多玩家游戏
4.协同编辑
现在很多开源项目都是分散在世界各地的开发者一起协同开发，此时就会用到版本控制系统，比如Git，SVN去合并冲突。但是如果有一份文档，支持多人实时在线协同编辑，那么此时就会用到比如WebSocket了，它可以保证各个编辑者都在编辑同一个文档，此时不需要用到Git，SVN这些版本控制，因为在协同编辑界面就会实时看到对方编辑了什么，谁在修改哪些段落和文字。
5.股票基金实时报价
金融界瞬息万变——几乎是每毫秒都在变化。如果采用的网络架构无法满足实时性，那么就会给客户带来巨大的损失。几毫秒钱股票开始大跌，几秒以后才刷新数据，一秒钟的时间内，很可能用户就已经损失巨大财产了。
6.体育实况更新
全世界的球迷，体育爱好者特别多，当然大家在关心自己喜欢的体育活动的时候，比赛实时的赛况是他们最最关心的事情。这类新闻中最好的体验就是利用Websocket达到实时的更新！
7.视频会议/聊天
视频会议并不能代替和真人相见，但是他能让分布在全球天涯海角的人聚在电脑前一起开会。既能节省大家聚在一起路上花费的时间，讨论聚会地点的纠结，还能随时随地，只要有网络就可以开会。
8.基于位置的应用
越来越多的开发者借用移动设备的GPS功能来实现他们基于位置的网络应用。如果你一直记录用户的位置(比如运行应用来记录运动轨迹)，你可以收集到更加细致化的数据。
9.在线教育
在线教育近几年也发展迅速。优点很多，免去了场地的限制，能让名师的资源合理的分配给全国各地想要学习知识的同学手上，Websocket是个不错的选择，可以视频聊天、即时聊天以及其与别人合作一起在网上讨论问题&amp;hellip;
10.智能家居
这也是我一毕业加入的一个伟大的物联网智能家居的公司。考虑到家里的智能设备的状态必须需要实时的展现在手机app客户端上，毫无疑问选择了Websocket。
11.总结
从上面我列举的这些场景来看，一个共同点就是，高实时性！
#####二.WebSocket诞生由来 1.最开始的轮询Polling阶段
这种方式下，是不适合获取实时信息的，客户端和服务器之间会一直进行连接，每隔一段时间就询问一次。客户端会轮询，有没有新消息。这种方式连接数会很多，一个接受，一个发送。而且每次发送请求都会有Http的Header，会很耗流量，也会消耗CPU的利用率。
2.改进版的长轮询Long polling阶段
长轮询是对轮询的改进版，客户端发送HTTP给服务器之后，有没有新消息，如果没有新消息，就一直等待。当有新消息的时候，才会返回给客户端。在某种程度上减小了网络带宽和CPU利用率等问题。但是这种方式还是有一种弊端：例如假设服务器端的数据更新速度很快，服务器在传送一个数据包给客户端后必须等待客户端的下一个Get请求到来，才能传递第二个更新的数据包给客户端，那么这样的话，客户端显示实时数据最快的时间为2×RTT（往返时间），而且如果在网络拥塞的情况下，这个时间用户是不能接受的，比如在股市的的报价上。另外，由于http数据包的头部数据量往往很大（通常有400多个字节），但是真正被服务器需要的数据却很少（有时只有10个字节左右），这样的数据包在网络上周期性的传输，难免对网络带宽是一种浪费。
3.WebSocket诞生
现在急需的需求是能支持客户端和服务器端的双向通信，而且协议的头部又没有HTTP的Header那么大，于是，Websocket就诞生了！
上图就是Websocket和Polling的区别，从图中可以看到Polling里面客户端发送了好多Request，而下图，只有一个Upgrade，非常简洁高效。至于消耗方面的比较就要看下图了
上图中，我们先看蓝色的柱状图，是Polling轮询消耗的流量，这次测试，HTTP请求和响应头信息开销总共包括871字节。当然每次测试不同的请求，头的开销不同。这次测试都以871字节的请求来测试。
**Use case A:**1,000 clients polling every second: Network throughput is (871 x 1,000) = 871,000 bytes = 6,968,000 bits per second (6.6 Mbps)
Use case B: 10,000 clients polling every second: Network throughput is (871 x 10,000) = 8,710,000 bytes = 69,680,000 bits per second (66 Mbps)</description></item><item><title>iOS Core Data 数据迁移 指南</title><link>https://new.halfrost.com/zh/ios_coredata_migration/</link><pubDate>Sun, 08 May 2016 08:06:00 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_coredata_migration/</guid><description>####前言 Core Data是iOS上一个效率比较高的数据库框架，(但是Core Data并不是一种数据库，它底层还是利用Sqlite3来存储数据的)，它可以把数据当成对象来操作，而且开发者并不需要在乎数据在磁盘上面的存储方式。它会把位于NSManagedObject Context里面的托管对象NSManagedObject类的实例或者某个NSManagedObject子类的实例，通过NSManagedObjectModel托管对象模型，把托管对象保存到持久化存储协调器NSPersistentStoreCoordinator持有的一个或者多个持久化存储区中NSPersistentStore中。使用Core Data进行查询的语句都是经过Apple特别优化过的，所以都是效率很高的查询。
当你进行简单的设定，比如说设定某个实体的默认值，设定级联删除的操作，设定数据的验证规则，使用数据的请求模板，这些修改Core Data都会自己完成，不用自己进行数据迁移。那那些操作需要我们进行数据迁移呢？凡是会引起NSManagedObjectModel托管对象模型变化的，都最好进行数据迁移，防止用户升级应用之后就闪退。会引起NSManagedObjectModel托管对象模型变化的有以下几个操作，新增了一张表，新增了一张表里面的一个实体，新增一个实体的一个属性，把一个实体的某个属性迁移到另外一个实体的某个属性里面…………大家应该现在都知道哪些操作需要进行数据迁移了吧。
####小技巧： 进入正题之前，我先说3个调试Core Data里面调试可能你会需要的操作。
1.一般打开app沙盒里面的会有三种类型的文件，sqlite，sqlite-shm,sqlite-wal,后面2者是iOS7之后系统会默认开启一个新的“数据库日志记录模式”(database journaling mode)生成的，sqlite-shm是共享内存(Shared Memory)文件，该文件里面会包含一份sqlite-wal文件的索引，系统会自动生成shm文件，所以删除它，下次运行还会生成。sqlite-wal是预写式日志(Write-Ahead Log)文件，这个文件里面会包含尚未提交的数据库事务，所以看见有这个文件了，就代表数据库里面还有还没有处理完的事务需要提交，所以说如果有sqlite-wal文件，再去打开sqlite文件，很可能最近一次数据库操作还没有执行。
所以在调试的时候，我们需要即时的观察数据库的变化，我们就可以先禁用这个日志记录模式，只需要在建立持久化存储区的时候存入一个参数即可。具体代码如下
NSDictionary *options = @{ NSSQLitePragmasOption: @{@&amp;#34;journal_mode&amp;#34;: @&amp;#34;DELETE&amp;#34;} }; NSError *error = nil; _store = [_coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self storeURL] options:options error:&amp;amp;error]; 2.Mac上打开数据库的方式很多，我推荐3个，一个是Firefox里面直接有sqlite的插件，免费的，可以直接安装，也很方便。当然也有不用Firefox的朋友，就像我是Chrome重度使用者，那就推荐2个免费的小的app，一个是sqlitebrowser，一个是sqlite manager，这2个都比较轻量级，都比较好用。
3.如果你想看看Core Data到底底层是如何优化你的查询语句的，这里有一个方法可以看到。
先点击Product -&amp;gt;Scheme -&amp;gt;Edit Scheme
然后再切换到Arguments分页中,在Arguments Passed On Launch里面加入 “- com.apple.CoreData.SQLDebug 3”,重新运行app，下面就会显示Core Data优化过的Sql语句了。
好了，调试信息应该都可以完美显示了，可以开始愉快的进入正文了！
####一.Core Data自带的轻量级的数据迁移 这种迁移可别小看它，在你新建一张表的时候还必须加上它才行，否则会出现如下的错误：
**Failed to add store. Error: Error Domain=NSCocoaErrorDomain Code=134100 &amp;#34;(null)&amp;#34; UserInfo={metadata={** ** NSPersistenceFrameworkVersion = 641;** ** NSStoreModelVersionHashes = {** ** Item = &amp;lt;64288772 72e62096 a8a4914f 83db23c9 13718f81 4417e297 293d0267 79b04acb&amp;gt;;** ** Measurement = &amp;lt;35717f0e 32cae0d4 57325758 58ed0d11 c16563f2 567dac35 de63d5d8 47849cf7&amp;gt;;** ** };** ** NSStoreModelVersionHashesVersion = 3;** ** NSStoreModelVersionIdentifiers = (** ** &amp;#34;&amp;#34;** ** );** ** NSStoreType = SQLite;** ** NSStoreUUID = &amp;#34;9A16746E-0C61-421B-B936-412F0C904FDF&amp;#34;;** ** &amp;#34;_NSAutoVacuumLevel&amp;#34; = 2;** **}, reason=The model used to open the store is incompatible with the one used to create the store}** 错误原因写的比较清楚了，reason=The model used to open the store is incompatible with the one used to create the store，这个是因为我新建了一张表，但是我没有打开轻量级的迁移Option。这里会有人会问了，我新建表从来没有出现这个错误啊？那是因为你们用的第三方框架就已经写好了改Option了。(场外人:这年头谁还自己从0开始写Core Data啊，肯定都用第三方框架啊)那这里我就当讲解原理了哈。如果是自己从0开始写的Core Data的话，这里是应该会报错了，解决办法当然是加上代码，利用Core Data的轻量级迁移，来防止这种找不到存储区的闪退问题</description></item><item><title>Remote debugging on iOS with Safari Web Inspector</title><link>https://new.halfrost.com/zh/remote_debugging_on_ios_with_safari_web_inspector/</link><pubDate>Mon, 02 May 2016 22:04:00 +0000</pubDate><guid>https://new.halfrost.com/zh/remote_debugging_on_ios_with_safari_web_inspector/</guid><description>之前在公司调试Hybrid其实很蛋疼。。都是本地打开zip包，运行js，然后调试，每次都要找到zip，比较麻烦，后来发现了这个远程调试的方法，直接插上手机就可以调试了，不用那么麻烦了，而且可以直接在手机上看到实时的效果。
后来发现有一些Js前端开发还不会这个方法，今天就分享出来，大家都看看，有啥问题请多指点。
1.首先iPhone连接上Mac，点击信任，确保itunes连接成功，然后打开iPhone的“设置” - “Safari” - &amp;ldquo;高级&amp;rdquo; - 打开“JavaScript” 和 “Web检查器”
2.打开Mac上的Safari，选择“偏好设置” - “高级” - &amp;ldquo;在菜单栏中显示“开发”菜单&amp;rdquo;
3.打开iPhone上的Safari或者运行PhoneGap程序，到某一个界面，回到Mac上的Safari上，选择“开发”，然后选择你的iPhone，就可以查看到那个一个Web界面了</description></item><item><title>iOS Hybrid 框架 —— PhoneGap</title><link>https://new.halfrost.com/zh/ios_hybrid_phonegap/</link><pubDate>Sun, 01 May 2016 21:59:00 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_hybrid_phonegap/</guid><description>####前言
Hybrid App（混合模式移动应用）是指介于web-app、native-app这两者之间的app，兼具“Native App良好用户交互体验的优势”和“Web App跨平台开发的优势”。
Hybrid App按网页语言与程序语言的混合，通常分为三种类型：多View混合型，单View混合型，Web主体型，3种类型比较如下：
今天我来谈谈Web主体型中Hybrid框架里面比较有名的PhoneGap
####一.Cordova 说到PhoneGap，就不得不说到Cordova
Cordova 是一个可以让 JS 与原生代码(包括 Android 的 java，iOS 的 Objective-C 等)互相通信的一个库，并且提供了一系列的插件类，比如 JS 直接操作本地数据库的插件类。
Cordova的设计概念，是在APP上透过Web控件来呈现Web页面，让Web开发人员可以操作熟悉的语言、工具来开发APP.
为了让Web页面能够满足更多的APP功能需求，Cordova提供了Plugin机制，让Web页面能够挂载并调用Native开发技术所开发的功能模块
Cordova在系统中的层级应该是这样子的:
Cordova的层级结构是下图这样的:
Cordova又可以和Angular相结合，变成ngCordova
Cordova + Angular = ngCordova ####二.JS 与 Objective-C 通信 JS 使用了两种方式来与 Objective-C 通信，一种是使用 XMLHttpRequest 发起请求的方式，另一种则是通过设置透明的 iframe 的 src 属性。
我接下来说的主要是第二种方式，iframe bridge。
通过在 JS 端创建一个透明的 iframe，设置这个 ifame 的 src 为自定义的协议，而 ifame 的 src 更改时，UIWebView 会先回调其 delegate 的 webView:shouldStartLoadWithRequest:navigationType: 方法
说的还是很抽象的，来实际看一段代码
在cordova.js 里面，是这样子实现的
function iOSExec() { .</description></item><item><title>2016 年 3 月 iOS 面试总结</title><link>https://new.halfrost.com/zh/ios_interview/</link><pubDate>Thu, 28 Apr 2016 07:56:00 +0000</pubDate><guid>https://new.halfrost.com/zh/ios_interview/</guid><description>今年3月中下旬因为个人原因，换了一份工作，期间面试了有4，5家，基本都是D轮或者上市公司，也从他们的面试笔试中看到了自己的一些不足，于是就想写出来和大家分享一下，如果能帮到正在面试的同学更好。从面试题中，其实可以看到一些行业的发展，以及总体人才需求是怎样的了。
####一.笔试题 笔试基本都有一两道基础题，比如说UITableView的重用机制，ARC的基本原理，如何避免retain cycle，谈谈对MVC的理解，iOS内存管理机制。这些大家应该都很清楚了。笔试的内容有几种有选择题，问答题，难一点的就是多选题了。我面试了一家就是给了10道多选题，多选，少选，错选都不行，当时做完以后就感觉不是很好，有些题目题干就是一下哪些是对的，然后ABCD依次给4个不同的概念，这种一道题相当于考了4个点。总之遇到这种“恶心”的多选题也不要太慌，静下心来一一甄别应该能拿到不错的成绩。
接下来我说几个我当时答的不怎么好的题目，我当时记了一下，和大家分享一下。
#####1.进程和线程的区别和联系 这个其实是操作系统的问题，当时一下子把我问的懵了，后来仔细回想了一下，加上自己的理解就答了，下面说说稍微完整的答案，大家可以准备准备，再问这种问题就可以完美作答了。
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.  一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
#####2.并行和并发的区别
并行是指两个或者多个事件在同一时刻发生； 并发是指两个或多个事件在同一时间间隔内发生。
#####3.谈谈你对Block和delegate的理解 我当时是这么答的，delegate的回调更多的面向过程，而block则是面向结果的。如果你需要得到一条多步进程的通知，你应该使用delegation。而当你只是希望得到你请求的信息（或者获取信息时的错误提示），你应该使用block。（如果你结合之前的3个结论，你会发现delegate可以在所有事件中维持state，而多个独立的block却不能）
#####4.谈谈instancetype和id的异同 1、相同点：都可以作为方法的返回类型
2、不同点： ①instancetype可以返回和方法所在类相同类型的对象，id只能返回未知类型的对象；
②instancetype只能作为返回值，不能像id那样作为参数
#####5.category中能不能使用声明属性？为什么？如果能，怎么实现？ 这种问题一问，我当时就感觉肯定能实现的，但是实在不知道怎么做，后来回来查了一下，才知道是用到了Runtime的知识了。贴一下答案
给分类（Category）添加属性
利用Runtime实现getter/setter 方法
@interface ClassName (CategoryName) @property (nonatomic, strong) NSString *str; @end //实现文件 #import &amp;#34;ClassName + CategoryName.h&amp;#34; #import &amp;lt;objc/runtime.h&amp;gt; static void *strKey = &amp;amp;strKey; @implementation ClassName (CategoryName) -(void)setStr:(NSString *)str { objc_setAssociatedObject(self, &amp;amp; strKey, str, OBJC_ASSOCIATION_COPY); } -(NSString *)str { return objc_getAssociatedObject(self, &amp;amp;strKey); } @end #####6.</description></item></channel></rss>