猫·仁波切

会研发的PM才是好OP.

Fix Ansible Tower: Stdout Capture Is Missing

Ansible Tower will report stdout capture is missing when restoring from previous backup.

Or run from docker?

(得,不装 B 英语了)

长话短说,之前要把 Ansible Tower 拆到 Docker 里,结果发现总不能正常执行。任务界面会提示:

stdout capture is missing

检查发现是 celery 进程出错,用 root 启动 celery 倒是正常的。

最后发现是 docker 中的 supervisord 启动时缺乏部分环境变量,解决方法:

1
2
3
4
5
6
change supervisor/conf.d/tower.conf
ADD:
[program:awx-celeryd]
......
environment=HOME="/var/lib/awx",USER="awx"
......

是的,为找到原因,逆向了整个 Ansible Tower。

Ref: GitHub Issue

在 CircleCI 上使用 Rust(CircleCI Meets Rust)

最近由于频频遇到 travis-ci 的问题,主要是 Linux 资源排队、macOS 资源更需要排队,导致自动测试时间被拉长, 影响开发效率。

了解到 CircleCI 是不错的替代品,所以打算迁移 Rust 项目过去。当然说起来, CircleCI 的野心更大,是要来替代 jenkins 的。

目前官方支持语言其实都比较落后,包括 go 也只是 1.6 版本,但似乎不是问题,而且据介绍, CircleCI 2.0 支持自定义 build image,支持语言的版本当然不在话下。

每天面对各种 IaaS, PaaS,免不了写配置是,这也是 yaml 程序员的日常。

1
2
3
4
5
6
7
8
dependencies:
  pre:
    - curl https://sh.rustup.rs -sSf | sh

test:
  override:
    - cargo build
    - cargo test

如上。然而不 work。报错:

1
2
3
4
5
6
7
8
9
10
11
cargo build
    Updating registry `https://github.com/rust-lang/crates.io-index`
warning: spurious network error (2 tries remaining): [12/-12] Malformed URL 'ssh://git@github.com:/rust-lang/crates.io-index'
warning: spurious network error (1 tries remaining): [12/-12] Malformed URL 'ssh://git@github.com:/rust-lang/crates.io-index'
error: failed to fetch `https://github.com/rust-lang/crates.io-index`

To learn more, run the command again with --verbose.

cargo build returned exit code 101

Action failed: cargo build

神了。原来, CircleCI 自作聪明在 .gitconfig 里修改了映射配置,强制用它自己的 ssh key 去访问 github,rewrite 了 https://github.com 的所有仓库。 这恰恰和 cargo 的 registry 机制冲突。所以报错。

CircleCI has rewrite https:://github.com to ssh://git@github.com: in .gitconfig. And this made cargo fail with above error message.

找到了原因,就可以搞了:

1
2
3
4
5
6
7
8
9
10
11
12
machine:
  pre:
    - sed -i 's/github/git-non-exist-hub/g' ~/.gitconfig

dependencies:
  pre:
    - curl https://sh.rustup.rs -sSf | sh

test:
  override:
    - cargo build
    - cargo test

嗯, Ugly but works.

折腾 Raspberry Pi + HomeKit 手记

9月14日凌晨苹果终于推送了 iOS 10 的更新。从之前发布会来看,并没有多少亮点,除了几天的新鲜感之外, 尤其是对于目前还在用上两代机型的我来说,2333。

两年前苹果发布 Swift 语言的同时,新增了 HomeKit,当时用工具 dump 过最老版本的 Swift 声明。传送门:HomeKit.swift。目前所有官方相关的资料位于 HomeKit - Apple

好消息是期待很久的 HomeKit 应用终于上线,屏幕上多了“家庭(Home)”应用,控制中心(从屏幕下方滑动)、 Siri 均对此有支持。 iOS 10 终于强化了推出已有两年智能家居平台,提供了官方 App,有不少硬件厂商支持。

简单说,HomeKit 就是苹果官方的智能家居平台解决方案,包括移动设备 SDK,智能家居硬件通信协议(HAP: HomeKit Accessory Protocol)、以及 MFi(Made for iPhone/iPod/iPad) 认证等等。通过 WiFi 或蓝牙连接智能家居设备(或 bridge 设备),也可以利用 Apple TV(4代) 或闲家中的置 iPad 实现设备的远程控制(HAP over iCloud)。

Home App 的维度划分:

  • Home: 家,和地理位置绑定,支持共享给好友控制。
  • Room: 房间,用于对设备进行分组。
  • Scene: 场景,一组对设备的配置,例如“起床”,那么可能的配置是打开卧室灯、窗帘、放段舒缓music等等。

众所周知苹果是卖数据线等硬件的公司(嗯,假设你数据线也坏过不少),HAP 协议部分是需要加入 MFi Program 才能获取文档,而且 MFi Program 无法以个人开发者身份加入。

好在有好心人逆向了 HAP 的服务端协议(对于智能硬件来说,硬件是服务端,手机App是客户端)。

对于折腾党来说,机会来了,自己动手改造家居!本文不涉及 App 开发,只涉及如何自制支持 HomeKit 的设备。

准备工作

设备列表:

  • iPhone 6P (iOS 10)
  • Raspberry Pi 3 (Debian jessie)

考察了两个比较靠谱的 HAP 实现:

最终选择使用 golang 的 brutella/hc,准备环境。

需要保证树莓派和手机位于统一子网,因为 HAP 底层是基于 Apple mDNS(RFC 6762)。

brutella/hc 要求 golang >= 1.4,而 Debian jessie 版本较低, 需要配置 jessie-backports 源:

1
deb ftp://ftp.cn.debian.org/debian jessie-backports main contrib non-free

同时导入源的 GPG Key。方法参考 这里

安装好 golang 1.6.2,建立开发目录。

1
2
3
# 似乎直接 install golang 会出点小问题,所以折衷用了如下方法:
> sudo apt-get install -t jessie-backports golang-1.6 golang-1.6-go golang-1.6-src golang-1.6-doc
> sudo apt-get install -t jessie-backports golang

示例

跑通官方示例代码:

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
package main

import (
  "github.com/brutella/hc"
  "github.com/brutella/hc/accessory"
  "log"
)

func main() {
  info := accessory.Info{
      Name:         "Lamp",
      SerialNumber: "051AC-23AAM1",
      Manufacturer: "Apple",
      Model:        "AB",
  }
  acc := accessory.NewSwitch(info)

  acc.Switch.On.OnValueRemoteUpdate(func(on bool) {
      if on == true {
          log.Println("Client changed switch to on")
      } else {
          log.Println("Client changed switch to off")
      }
  })

  config := hc.Config{Pin: "00102003"}
  t, err := hc.NewIPTransport(config, acc.Accessory)
  if err != nil {
      log.Fatal(err)
  }

  hc.OnTermination(func() {
      t.Stop()
  })

  t.Start()
}

编译执行.

1
2
3
4
5
6
7
8
9
10
$ AppleHome> # current dir

$ AppleHome> go get
...

$ AppleHome> go build
...

$ AppleHome> ./AppleHome
...

随后打开手机的 Home App,添加设备,选择 Lamp,输入 PIN 00102003,完成配对,即可使用。

自定义设备

树莓派外接小音箱一只,用来放电台,尝试用 HomeKit 控制树莓派的禁音。命令:

1
2
amixer set PCM on
amixer set PCM off

代码:

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
package main

import (
        "os/exec"
        "github.com/brutella/hc"
        "github.com/brutella/hc/accessory"
        "log"
)

func main() {
        info := accessory.Info{
                Name:            "Radio",
                SerialNumber: "051AC-23AAM2",
                Manufacturer: "Apple",
                Model:          "RPI3",
        }
        acc := accessory.NewSwitch(info)

        acc.Switch.On.OnValueRemoteUpdate(func(on bool) {
                log.Println("Toggled PCM!")

                if on == true {
                        exec.Command("amixer", "set", "PCM", "on").Run()
                        log.Println("Client changed switch to on")
                } else {
                        exec.Command("amixer", "set", "PCM", "off").Run()
                        log.Println("Client changed switch to off")
                }
        })

        config := hc.Config{Pin: "00102004"}
        t, err := hc.NewIPTransport(config, acc.Accessory)
        if err != nil {
                log.Fatal(err)
        }

        hc.OnTermination(func() {
                t.Stop()
        })

        t.Start()
}

其他

HAP 将智能家居分为以下维度:

  • Accessory: 单个设备,例如开关,温度计,调节器
  • Service: 一组值,合起来提供服务,例如中央空调(调节温度,风速等等)

TODO

Swift 3.0 尝试——从入门到再学一门(a Glimpse of Swift 3.0)

安装工具

https://github.com/kylef/swiftenv

Swift 3.0 新变化

以下内容来自 Swift 语言提案1

Swift 3.0 发布计划

Swift Package System

https://github.com/donald-pinckney/swift-packages

AST 结构

代码位于 include/swift/ASTlib/AST

ModuleDecl 模块(单个库或是可执行文件)。编译的最小单元,由多个文件组成。

FileUnit(抽象类) 文件作用域,是代码组织的最小单元。

  • DerivedFileUnit: A container for a module-level definition derived as part of an implicit protocol conformance.
  • SourceFile: A file containing Swift source code. .swift 或 .sil 也可以是虚拟 REPL
    • Imports: Vec<(ImportedModule, ImportOptions)>
    • Identifier
    • Decls: Vec
    • LocalTypeDecl: Vec
    • ObjCMethods: Map<ObjCSelector, AbstractFunctionDecl>
    • infix, postfix, prefix operators: OperatorMap
  • BuiltinUnit

swift 命令入口

入口函数 tools/driver/driver.cppmain 函数。

集成多个子工具。同时若 PATH 下有名为 swift-foobar 的可执行文件,则可通过 swift foobar 调用。

编译器前端 swift -frontend

编译。同时支持打印出各种编译时中间结果。

API Notes 功能 swift -apinotes

参考信息位于 https://github.com/apple/swift/tree/master/apinotes .

简单说,API Notes 机制就是通过 .apinotes 文件(YAML格式)描述 Objective-C Framework 和对应 Swift API 的关系。最终生成 .apinotesc 文件,与 .swiftmodule 文件一起作为 Swift 的模块。

主要功能包括且不限于:

  • SwiftBridge:设置对应的 Bridge 类型,例如 NSArray 对应与 Swift.Array
  • Nullability/NullabilityOfRet: 类的属性、方法的参数、返回值对应类型是否可以为 null,即对应与 Swift 的 T 还是 T?
  • Availability:方法是否在 Swift 中暴露,并给出 availability message
  • SwiftName:方法 Selector 在 Swift 中的重命名,例如 filteredArrayUsingPredicate: 替换为 filtered(using:)

Dump 为YAML文件:

$> swift -apinotes -binary-to-yaml /path/to/lib/swift/macosx/x86_64/Dispatch.apinotesc -o=-

Module Wrap 工具 swift -modulewrap

1
2
3
4
// Wraps .swiftmodule files inside an object file container so they
// can be passed to the linker directly. Mostly useful for platforms
// where the debug info typically stays in the executable.
// (ie. ELF-based platforms).

用法:

swift -modulewrap ObjectiveC.swiftmodule -o objc.o

实际发现是在 .o 里定义了 ___Swift_AST 符号。

REPL

Swift 提供了两个 REPL(Read-Evaluate-Print Loop),一个是 Swift 本身内置,另一个集成到了 lldb 命令行下。前者只有基本功能,即将废弃,后者功能更强大。

子命令分别是:

  • swift -deprecated-integrated-repl
  • swift -lldb-repl

swift -repl 子命令选择可用的 REPL 进入,一般是 lldb-repl,除非找不到 lldb 时。这也是 Swift 命令不带任何参数的默认行为。


  1. (apple/swift-evolution)[https://github.com/apple/swift-evolution]

Rust Pattern Match(Rust中的模式匹配)

模式匹配

汉语字典中对“模式”的解释是:事物的标准样式。在计算机科学中,它指特定类型的数据(往往是序列或是树形结构)满足某一特定结构或格式。“匹配”本身是指一个判断寻找过程。最早的模式匹配用于文本编辑器中的正则字符串搜索,之后才作为编程语言特性。

模式匹配基础

模式匹配在计算机科学领域有两层意思。其一,可以特指字符串匹配算法,例如为人熟知的 KMP 字符串匹配算法、命令行工具 grep 等。 其二,特指在一些语言中作为一种以结构的方式处理数据的工具,此时的匹配过程往往是树形匹配,与此相伴的往往还有一个特性叫 guard(守卫)。

Rust 中模式匹配随处可见,例如在let变量绑定语句、match匹配语句中等。利用好模式匹配这一特性可以使代码更简洁易懂。Rust支持模式匹配中的变量绑定、结构体/元组解构、守卫条件判断、数值范围匹配等特性。

原始匹配

match 语句中可以直接匹配字面常量,下划线_匹配任意情形。

1
2
3
4
5
6
7
8
let x = 1;

match x {
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    _ => println!("anything"),
}

以上代码会打印出one

结构匹配

match 用于匹配一个表达式的值,寻找满足条件的子分支(arm)并执行。每个子分支包含三部分:一系列模式、可选的守卫条件以及主体代码块。

多个模式

每个子分支可以是多个模式,通过 | 符号分割:

1
2
3
4
5
6
7
let x = 1;

match x {
    1 | 2 => println!("one or two"),
    3 => println!("three"),
    _ => println!("anything"),
}

以上代码打印出one or two

守卫条件

通过if引入子分支的守卫条件:

1
2
3
4
5
6
7
8
9
10
11
12
enum OptionalInt {
    Value(i32),
    Missing,
}

let x = OptionalInt::Value(5);

match x {
    OptionalInt::Value(i) if i > 5 => println!("Got an int bigger than five!"),
    OptionalInt::Value(..) => println!("Got an int!"),
    OptionalInt::Missing => println!("No such luck."),
}

模式匹配进阶

其实进阶,不如直接从libsyntax源码看看到底模式匹配是如何实现。syntax::ast::Pat

从AST源码中寻找语法要素屋外户两个要点,其一,语法要素是如何表达为对应AST的;其二,对应AST在哪些父AST中出现。

Rust中使用syntax::ast::Pat枚举来表示一个模式匹配。

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
pub struct Pat {
    pub id: NodeId,
    pub node: PatKind,
    pub span: Span,
}

pub enum PatKind {
    /// Represents a wildcard pattern (`_`)
    /// 表示通配,下划线
    Wild,

    /// A `PatKind::Ident` may either be a new bound variable,
    /// or a unit struct/variant pattern, or a const pattern (in the last two cases
    /// the third field must be `None`).
    ///
    /// In the unit or const pattern case, the parser can't determine
    /// which it is. The resolver determines this, and
    /// records this pattern's `NodeId` in an auxiliary
    /// set (of "PatIdents that refer to unit patterns or constants").
    Ident(BindingMode, SpannedIdent, Option<P<Pat>>),

    /// A struct or struct variant pattern, e.g. `Variant {x, y, ..}`.
    /// The `bool` is `true` in the presence of a `..`.
    Struct(Path, Vec<Spanned<FieldPat>>, bool),

    /// A tuple struct/variant pattern `Variant(x, y, z)`.
    /// "None" means a `Variant(..)` pattern where we don't bind the fields to names.
    TupleStruct(Path, Option<Vec<P<Pat>>>),

    /// A path pattern.
    /// Such pattern can be resolved to a unit struct/variant or a constant.
    Path(Path),

    /// An associated const named using the qualified path `<T>::CONST` or
    /// `<T as Trait>::CONST`. Associated consts from inherent impls can be
    /// referred to as simply `T::CONST`, in which case they will end up as
    /// PatKind::Path, and the resolver will have to sort that out.
    QPath(QSelf, Path),

    /// A tuple pattern `(a, b)`
    Tup(Vec<P<Pat>>),
    /// A `box` pattern
    Box(P<Pat>),
    /// A reference pattern, e.g. `&mut (a, b)`
    Ref(P<Pat>, Mutability),
    /// A literal
    Lit(P<Expr>),
    /// A range pattern, e.g. `1...2`
    Range(P<Expr>, P<Expr>),
    /// `[a, b, ..i, y, z]` is represented as:
    ///     `PatKind::Vec(box [a, b], Some(i), box [y, z])`
    Vec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
    /// A macro pattern; pre-expansion
    Mac(Mac),
}

以上AST定义,即说明,到底什么被认为是一个“模式”。

以下介绍Pat在哪些AST中出现。

全局 Item

全局 Item 中,使用模式匹配的均为函数参数。

ItemKind::Fn

Fn 全局函数 -> FnDecl 函数声明 -> [Arg] 函数头参数声明。

ItemKind::Trait

Trait -> [TraitItem] -> TraitItemKind::Method -> MethodSig -> FnDecl 方法声明,同上。

ItemKind::Impl

Impl -> [ImplItem] -> ImplItemKind::Method -> MethodSig -> FnDecl

ast::Stmt 语句

StmtKind::Decl

Decl -> DeclKind::Local

let 语句 let <pat>:<ty> = <expr>;

StmtKind::Expr 表达式

见下。

ast::Expr

match外,if letwhile letfor控制语句支持同时进行模式匹配。具体实现是一种desugared过程,即,去语法糖化。

同时类似于函数定义,闭包参数也支持模式匹配。

if let

IfLet(P<Pat>, P<Expr>, P<Block>, Option<P<Expr>>)

if let pat = expr { block } else { expr }

This is desugared to a match expression.

while let

WhileLet(P<Pat>, P<Expr>, P<Block>, Option<Ident>)

'label: while let pat = expr { block }

for

ForLoop(P<Pat>, P<Expr>, P<Block>, Option<Ident>)

'label: for pat in expr { block }

match

Match(P<Expr>, Vec<Arm>)

match 语句,在 Arm 中出现,其中 Arm 定义为

1
2
3
4
5
6
pub struct Arm {
    pub attrs: Vec<Attribute>,
    pub pats: Vec<P<Pat>>,
    pub guard: Option<P<Expr>>,
    pub body: P<Expr>,
}

闭包

Closure(CaptureBy, P<FnDecl>, P<Block>)

闭包,例如 move |a, b, c| {a + b + c}

相关 feature gate

advanced_slice_patterns - See the match expressions section for discussion; the exact semantics of slice patterns are subject to change, so some types are still unstable.

slice_patterns - OK, actually, slice patterns are just scary and completely unstable.

box_patterns - Allows box patterns, the exact semantics of which is subject to change.

参考

https://doc.rust-lang.org/book/patterns.html

广州实时工具App逆向

简记。用了 IDA Pro,安卓手机的 Remote 客户端。以及 apktool 等。

Github: guangzhou-realtime-bus

  • 生成 e=3 的 1024 位 RSA 密钥对
  • 公钥串用查表加密(byte 映射),然后 base64 封装发送给服务器
  • 服务器返回一串用公钥加密过的数据
  • 用本地私钥解密后,该数据包含未知96字节的一段数据和 DES Key
  • 从此通信用 DES 加密

base64封装过程:先打包字符串长度,然后是原始字符串(JSON),然后是0x10(md5字符串长度), 然后是 md5 校验值。整个二进制字符串用 base64 转码,POST 给服务器。

具体的登录注册过程还需要进一步抓包分析,不过暂时兴趣不在这里了。

Swift 2.0 的错误处理(Swift 2.0 Error Handling)

1
2
3
4
5
6
7
8
9
10
protocol ErrorType {
  var _domain: String { get }
  var _code: Int { get }
}

@asmname("swift_bridgeErrorTypeToNSError") func _bridgeErrorTypeToNSError(e: ErrorType) -> AnyObject

@asmname("swift_stdlib_getErrorCode") func _stdlib_getErrorCode<T : ErrorType>(x: UnsafePointer<T>) -> Int

@asmname("swift_stdlib_getErrorDomainNSString") func _stdlib_getErrorDomainNSString<T : ErrorType>(x: UnsafePointer<T>) -> AnyObject

Foundation

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
protocol _ObjectiveCBridgeableErrorType : ErrorType {
  init?(_bridgedNSError: NSError)
}

struct NSCocoaError : RawRepresentable, _BridgedNSError, _ObjectiveCBridgeableErrorType, ErrorType, __BridgedNSError, Hashable, Equatable {
  let rawValue: Int
  init(rawValue: Int)
  static var _NSErrorDomain: String {
    get {}
  }
  typealias RawValue = Int
}


infix func ==(a: _GenericObjCError, b: _GenericObjCError) -> Bool
infix func ==(a: _GenericObjCError, b: _GenericObjCError) -> Bool
func ==<T : __BridgedNSError where T.RawValue : SignedIntegerType>(lhs: T, rhs: T) -> Bool

@available(OSX 10.11, iOS 9.0, *)
func resolveError(error: NSError?) throws

enum _GenericObjCError : ErrorType {
  case NilError
  var hashValue: Int {
    get {}
  }
  var _domain: String {
    get {}
  }
  var _code: Int {
    get {}
  }
}

@asmname("swift_stdlib_bridgeNSErrorToErrorType")
func _stdlib_bridgeNSErrorToErrorType<T : _ObjectiveCBridgeableErrorType>(error: NSError, out: UnsafeMutablePointer<T>) -> Bool

@asmname("swift_convertNSErrorToErrorType") func _convertNSErrorToErrorType(error: NSError?) -> ErrorType

@objc enum NSURLError : Int, _BridgedNSError, _ObjectiveCBridgeableErrorType, ErrorType, __BridgedNSError { ... }


protocol __BridgedNSError : RawRepresentable {
  static var _NSErrorDomain: String { get }
}
@asmname("swift_convertErrorTypeToNSError") func _convertErrorTypeToNSError(error: ErrorType) -> NSError
func ~=(match: NSCocoaError, error: ErrorType) -> Bool
protocol _BridgedNSError : __BridgedNSError, _ObjectiveCBridgeableErrorType, Hashable {
  static var _NSErrorDomain: String { get }
}

ErrorType 在 Swift 中表示。

1
2
3
4
5
6
7
8
extension NSError : ErrorType {
  @objc dynamic var _domain: String {
    @objc dynamic get {}
  }
  @objc dynamic var _code: Int {
    @objc dynamic get {}
  }
}

北京实时公交分析

361 条线路,705条单向线路。 aibang 负责数据服务。

每辆车,每15秒更新一次 GPS,

为第三方扩展创建 Swift 模块

本文提出了一种将第三方扩展引入到 Swift 标准库的方法。

以 Alamofire 为例,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cd Path-To-Alamofire-Src-Dir
mkdir -p 32 64

# 创建动态链接库,及对应 Swift 模块,32/64版本
xcrun swiftc -sdk $(xcrun --show-sdk-path --sdk iphoneos) Alamofire.swift -target arm64-apple-ios7.1 -target-cpu cyclone -emit-library -emit-module -module-name Alamofire -v -o libswiftAlamofire.dylib -module-link-name swiftAlamofire -Xlinker -install_name -Xlinker @rpath/libswiftAlamofire.dylib

mv Alamofire.swiftdoc Alamofire.swiftmodule libswiftAlamofire.dylib ./64

xcrun swiftc -sdk $(xcrun --show-sdk-path --sdk iphoneos) Alamofire.swift -target armv7-apple-ios7.1 -target-cpu cyclone -emit-library -emit-module -module-name Alamofire -v -o libswiftAlamofire.dylib -module-link-name swiftAlamofire -Xlinker -install_name -Xlinker @rpath/libswiftAlamofire.dylib

mv Alamofire.swiftdoc Alamofire.swiftmodule libswiftAlamofire.dylib ./64

# 创建 universal lib
lipo -create ./{32,64}/libswiftAlamofire.dylib  -output ./libswiftAlamofire.dylib

# 创建模拟器用 lib
xcrun swiftc -sdk $(xcrun --show-sdk-path --sdk iphonesimulator) Alamofire.swift -target i386-apple-ios7.1 -target-cpu yonah -emit-library -emit-module -module-name Alamofire -v -o libswiftAlamofire.dylib -module-link-name swiftAlamofire -Xlinker -install_name -Xlinker @rpath/libswiftAlamofire.dylib

其他相关 target

1
2
3
4
-target armv7-apple-ios7.1 -target-cpu cortex-a8
-target arm64-apple-ios7.1 -target-cpu cyclone
-target i386-apple-ios7.1 -target-cpu yonah
-target x86_64-apple-ios7.1 -target-cpu core2

其实你了解 Swift 模块结构的化,应该回想到,将第三方模块创建为 swiftmodule 应该是最靠谱的选择。不过实际操作发现, 编译命令无法很方便地调整,主要是因为 xcodebuild 系统,和编译命令不知道怎么导出。也是略纠结。

实际上,如果使用 Carthage 的话,即把第三方扩展作为 Framework 引入,会导致无法支持 iOS 7,但是 Swift 本身是支持 iOS 7 的, 在编译命令和生成的文件中检查发现,对于 iOS 7,Swift 使用了纯静态模块编译的方法。所以其实我们引入第三方扩展的时候也可以这样做。

以下是静态编译所需命令:

1
2
3
xcrun swift -sdk $(xcrun --show-sdk-path --sdk macosx) SwiftyJSON.swift -c -parse-as-library -module-name SwiftyJSON -v -o SwiftyJSON.o

ar rvs libswiftSwiftyJSON.a SwiftyJSON.o

如何使用?

将编译结果扔到:

1
2
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift_static

下对应目录。

然后在 Xcode 里,直接 import。

Swift Beta3 Changes ( Swift 在 Beta3 中的变化)

准确说是 beta2 Swift version 1.0 (swift-600.0.34.4.8) 到 beta3 Swift version 1.0 (swift-600.0.38.7) 的变化。

对了,补充下。 beta1 Swift version 1.0 (swift-600.0.34.4.5) 到 beta2 几乎没有什么变化。

语法

nil 成为关键字。

[KeyType : ValueType] 可以表示字典类型 Dictionary<KeyType, ValueType>

[Type] 用于表示原 Array 类型 Type[],等价 Array<T>,原用法会导致警告。

增加 @noinline 属性

.. 运算符改为 ..<,不容易和 ... 混淆。

函数、类型

sort() 改名为 sorted()。新增 sort() 函数,参数为 inout

Index 类型中的 .succ() 变为 .successor().pred() 变为 .predecessor()

C/ObjC 交互变化

增加 UnsafeMutableArray<T> 类型。

增加 CFunctionPointer<T> 类型。

删除 CConstVoidPointerCMutableVoidPointer。替换为 UnsafePointer<()>ConstUnsafePointer<Int32>

删除 CConstPointer<T>CMutablePointer<T>。替换为 UnsafePointer<T>ConstUnsafePointer<T>

这么一来指针操作简单了好多。原有会出现 COpaquePointer 的不合理情况,也都对应到适合的类型。

CString 可以从 UnsafePointer<UInt8>UnsafePointer<CChar> 两种类型构造获得,之前只支持 UInt8

module.map 中头文件声明转换为 Swift 声明不再使用 C 兼容类型,直接使用 Swift 相应类型。原有 CInt,现在成为 Int32

结构体会自动添加构造函数 init(field1:field2:...) 这样。

nil

去掉了 NilType,增加了 NilLiteralConvertiblenil 成为关键字。可以认为是 nil 常量。

1
2
3
protocol NilLiteralConvertible {
  class func convertFromNilLiteral() -> Self
}

除了 Optional 、上面所提到的指针类型外,RawOptionSet 也实现了该协议。

Array

去掉了 .copy()unshare() 方法。

增加了以下方法:

1
2
func makeUnique(inout buffer: ArrayBuffer<T>, e: T, index: Int)
func sorted(isOrderedBefore: (T, T) -> Bool) -> Array<T>

看起来 Array 对底层容器的引用有了更好的控制 ArrayBufferType 增加了判断方法 func isMutableAndUniquelyReferenced() -> Bool

Array 目前可以认为是真正的值类型。

指针

增加了 _Pointer protocol

1
2
3
4
protocol _Pointer {
  var value: RawPointer { get }
  init(_ value: RawPointer)
}

表示一个类型可以对应到原生指针。

同时成为内部桥接类型,编译器内部在转换时使用它(取出 RawPointer, 构造具体指针类型)。

模块

增加了 StdlibUnittest 模块。 声明代码。单元测试终于有了。