Install Supervisor on CentOS

CentOS default repository is very limited, and even if you install EPEL you will get old packages, in my case I needed to install Supervisor to manage my Django application, after trying to do it manually and through EPEL I ended up with the following setup.

Install Needed Package

1
2
3
4
sudo yum install python-setuptools
sudo easy_install pip
sudo pip install supervisor
Setup Supervisor

We’ve already installed “Supervisor” globally, but we need to create its configuration, luckily it comes with default config:

1
2
3
4
5
6
7
8
9
10
echo_supervisord_conf > supervisord.conf
sudo cp supervisord.conf /etc/supervisord.conf
sudo mkdir /etc/supervisord.d/
sudo vim /etc/supervisord.conf


:
[include]
files = /etc/supervisord.d/*.conf
:

Next we need to set “Supervisor” to run automatically every time you restart your machine, we need to create /etc/rc.d/init.d/supervisord with the following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
sudo vi /etc/rc.d/init.d/supervisord
#!/bin/sh
#
# /etc/rc.d/init.d/supervisord
#
# Supervisor is a client/server system that
# allows its users to monitor and control a
# number of processes on UNIX-like operating
# systems.
#
# chkconfig: - 64 36
# description: Supervisor Server
# processname: supervisord

# Source init functions
. /etc/rc.d/init.d/functions

prog="supervisord"

prefix="/usr/"
exec_prefix="${prefix}"
prog_bin="${exec_prefix}/bin/supervisord"
PIDFILE="/var/run/$prog.pid"

start()
{
echo -n $"Starting $prog: "
daemon $prog_bin --pidfile $PIDFILE
[ -f $PIDFILE ] && success $"$prog startup" || failure $"$prog startup"
echo
}

stop()
{
echo -n $"Shutting down $prog: "
[ -f $PIDFILE ] && killproc $prog || success $"$prog shutdown"
echo
}

case "$1" in

start)
start
;;

stop)
stop
;;

status)
status $prog
;;

restart)
stop
start
;;

*)
echo "Usage: $0 {start|stop|restart|status}"
;;

esac

Then make sure CentOS knows about it:

1
2
3
4
sudo chmod +x /etc/rc.d/init.d/supervisord
sudo chkconfig --add supervisord
sudo chkconfig supervisord on
sudo service supervisord start

Sample Supervisor App

Here is a sample of Django App to be controlled and monitored by Supervisor, just put it:

1
2
3
4
5
6
7
8
9
10
11
12
13
sudo vi /etc/supervisord.conf

:
[program:shadowsocks-server]
command=/home/xjliao/goworkspace/bin/shadowsocks-server -c /etc/shadowsocks.json
autostart=true
autorestart=true

[program:nginx-blogserver]
command=/home/xjliao/blogserver/sbin/nginx
autostart=true
autorestart=true
:

from:
https://rayed.com/wordpress/?p=1496

Deploy shadowsocks-goalang server

Get golang and set golang path

1
2
3
4
5
wget https://storage.googleapis.com/golang/go1.5.1.linux-amd64.tar.gz
tar -zxvf /home/xjliao/go1.5.1.linux-amd64.tar.gz
mkdir /home/xjliao/goworkspace
sudo vim /etc/profile
go

Use go cmd to test.

add

1
2
3
4
5
6
GOROOT=$HOME/go
GOPATH=$HOME/goworkspace

PATH=$PATH:$GOROOT/bin

export GOROOT GOPATH PATH

Get shadowsocks-goalang

1
go get github.com/shadowsocks/shadowsocks-go/cmd/shadowsocks-server

And then. We can look for a execute file at $GOPATH/bin name’s shadowsocks-server.
Ok. We need a configuration file to run the shadowsocks-server command.
So….

1
sudo vim /etc/shadowsocks.json

add

1
2
3
4
5
6
7
8
9
10
11
{
"server":"0.0.0.0",
"local_address": "127.0.0.1",
"local_port":1080,
"port_password":{
"8383":"1"
},
"timeout":600,
"method":"aes-128-cfb",
"fast_open": false
}

Execute shadowsocks-server command. if do right steps, it will run at listener your set port and work well.

1
/home/xjliao/goworkspace/bin/shadowsocks-server/ -c /etc/shadowsocks.json

Make it autostart when system start or reboot.

Install supervisor and edit configuration file.

1
2
3
sudo yum install supervisor
sudo vim /etc/supervisord.conf
sudo chkconfig supervisord on

Add

1
2
3
4
[program:shadowsocks-server]
command=/home/xjliao/goworkspace/bin/shadowsocks-server -c /etc/shadowsocks.json
autostart=true ; start at supervisord start (default: true)
autorestart=true ; retstart at unexpected quit (default: true)

Linux yum install last stable version for nginx

1
vim /etc/yum.repos.d/nginx.repo

Centos

1
2
3
4
5
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1

RHEL

1
2
3
4
5
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/rhel/$releasever/$basearch/
gpgcheck=0
enabled=1

from:
http://wiki.nginx.org/Install

Linux nginx autostart scripts

Should work on RHEL, Fedora, CentOS. Tested on CentOS 5.

Save this file as [/etc/init.d/nginx]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/bin/sh
#
# nginx - this script starts and stops the nginx daemon
#
# chkconfig: - 85 15
# description: Nginx is an HTTP(S) server, HTTP(S) reverse \
# proxy and IMAP/POP3 proxy server
# processname: nginx
# config: /etc/nginx/nginx.conf
# config: /etc/sysconfig/nginx
# pidfile: /var/run/nginx.pid

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0

# Your nginx config
nginx="/usr/sbin/nginx"
prog=$(basename $nginx)

# Your nginx config
NGINX_CONF_FILE="/etc/nginx/nginx.conf"

[ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx

lockfile=/var/lock/subsys/nginx

make_dirs() {
# make required directories
user=`$nginx -V 2>&1 | grep "configure arguments:" | sed 's/[^*]*--user=\([^ ]*\).*/\1/g' -`
if [ -z "`grep $user /etc/passwd`" ]; then
useradd -M -s /bin/nologin $user
fi
options=`$nginx -V 2>&1 | grep 'configure arguments:'`
for opt in $options; do
if [ `echo $opt | grep '.*-temp-path'` ]; then
value=`echo $opt | cut -d "=" -f 2`
if [ ! -d "$value" ]; then
# echo "creating" $value
mkdir -p $value && chown -R $user $value
fi
fi
done
}

start() {
[ -x $nginx ] || exit 5
[ -f $NGINX_CONF_FILE ] || exit 6
make_dirs
echo -n $"Starting $prog: "
daemon $nginx -c $NGINX_CONF_FILE
retval=$?
echo
[ $retval -eq 0 ] && touch $lockfile
return $retval
}

stop() {
echo -n $"Stopping $prog: "
killproc $prog -QUIT
retval=$?
echo
[ $retval -eq 0 ] && rm -f $lockfile
return $retval
}

restart() {
configtest || return $?
stop
sleep 1
start
}

reload() {
configtest || return $?
echo -n $"Reloading $prog: "
killproc $nginx -HUP
RETVAL=$?
echo
}

force_reload() {
restart
}

configtest() {
$nginx -t -c $NGINX_CONF_FILE
}

rh_status() {
status $prog
}

rh_status_q() {
rh_status >/dev/null 2>&1
}

case "$1" in
start)
rh_status_q && exit 0
$1
;;
stop)
rh_status_q || exit 0
$1
;;
restart|configtest)
$1
;;
reload)
rh_status_q || exit 7
$1
;;
force-reload)
force_reload
;;
status)
rh_status
;;
condrestart|try-restart)
rh_status_q || exit 0
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
exit 2
esac
1
2
3
sudo chmod +x /etc/init.d/nginx
sudo chkconfig --add /etc/init.d/nginx
sudo chkconfig nginx on

from:
http://wiki.nginx.org/RedHatNginxInitScript

Golang 交叉编译

在此之前假设你的Golang环境已经设置好了

mac 二进制包安装golang默认可以使用linux/windows/freebsd, GOARCH为amd64的交叉编译, 386需要手动添加

准备目标平台的包和工具文件

以下过程不是重新编译,安装go,而是添加对其他平台的支持。

方式一

1
2
3
4
$ cd /usr/local/go/src
$ CGO_ENABLED=0 GOOS=linux GOARCH=386 ./make.bash
$ CGO_ENABLED=0 GOOS=windows GOARCH=386 ./make.bash
$ CGO_ENABLED=0 GOOS=freebsd GOARCH=386 ./make.bash

CGO_ENABLED=0 表示交叉编译禁用cgo
如果go文件夹是owner为root, 请使用sudo

如果需要提高编译速度,加入。

1
./make.bash --no-clean

方式二

1
brew install --cross-compile-all go

安装完成后,我们可以看到 Go的安装目录下 多了这个平台特有的几个命令行工具。

各平台的GOOS和GOARCH参考



OS ARCH OS version
linux 386 / amd64 / arm >= Linux 2.6
darwin 386 / amd64 OS X (Snow Leopard + Lion)
freebsd 386 / amd64 >= FreeBSD 7
windows 386 / amd64 >= Windows 2000

编译成对应平台下的执行文件

到源代码目录下执行:

1
$ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build

不带前面参数的 go build 只是编译出开发环境适用的执行文件。

golang-crosscompile工具

下载地址
github.com/davecheney/golang-crosscompile
使用方法
http://dave.cheney.net/2012/09/08/an-introduction-to-cross-compilation-with-go

下载脚本

1
2
$ git clone git://github.com/davecheney/golang-crosscompile.git
$ source golang-crosscompile/crosscompile.bash

编译支持所有平台

1
$ go-crosscompile-build-all

这个时间要花个几分钟

查看平台的包

1
$ ls -1 $(go env GOROOT)/pkg

使用交叉编译

1
$ go-linux-arm build

要不同平台的编译,替换即可, 如:go-windows-386 build

为方面以后使用,将下面的加入到.bashrc或.zshrc

1
2
3
$ mkdir ~/.golang-crosscomplie
$ cp golang-crosscompile/crosscompile.bash ~./golang-crosscomplie
$ source ~/.zshrc

Golang 快速入门 方法 接口

Golang 快速入门 方法 接口

内容来自https://tour.golang.org/

格式说明:

// 正常注释
//——-依赖关系
//—>关联关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package main

import "os"
import "time"
import (
"fmt"
"image"
"io"
"log"
"math"
"net/http"
"strings"
)

//--- 方法
type Vertex struct {
X, Y float64
}

//Go 没有类。然而,仍然可以在结构体类型上定义方法。
//方法接收者 出现在 func 关键字和方法名之间的参数中。
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

//---

//-- 方法(续)
//你可以对包中的 任意 类型定义任意方法,而不仅仅是针对结构体。
//但是,不能对来自其他包的类型或基础类型定义方法。
type MyFloat float64

func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}

//---

// 接收者为指针的方法
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}

// 接口
//接口类型是由一组方法定义的集合。
//接口类型的值可以存放实现这些方法的任何值。
type Abser interface {
Abs() float64
}

// 隐式接口
//类型通过实现那些方法来实现接口。 没有显式声明的必要;所以也就没有关键字“implements“。
//隐式接口解藕了实现接口的包和定义接口的包:互不依赖。
//因此,也就无需在每一个实现上增加新的接口名称,这样同时也鼓励了明确的接口定义。
type Reader interface {
Read(b []byte) (n int, err error)
}

type Writer interface {
Write(b []byte) (n int, err error)
}

type ReadWriter interface {
Reader
Writer
}

//---

// Stringers 与java的toString()方法类似
// 在打印或format时生效
type Person struct {
Name string
Age int
}

func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

//---

// 错误
// Go 程序使用 error 值来表示错误状态
type MyError struct {
When time.Time
What string
}

func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %v", e.When, e.What)
}

func run() error {
return &MyError{
time.Now(),
"it didn't work",
}
}

//---

// Web服务器
type Hello struct{}

func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello")
}

func main() {
//--> 方法
v := &Vertex{3, 4}
fmt.Println(v.Abs())

//--> 方法续
f := MyFloat(-math.Sqrt2)
fmt.Println(f.Abs())
//--> 接收者为指针的方法
v.Scale(5)
fmt.Println(v, v.Abs())

//--> 接口
var a Abser
f1 := MyFloat(-math.Sqrt2)
v1 := Vertex{3, 4}
// a MyFloat 实现了Abser
a = f1
// a *Vertext 实现了Abser
a = &v1
// 下面一行,v1是一个 Vertex(而不是 *Vertex)
// 所以没有实现 Abser。
//a = v1
fmt.Println(a.Abs())

//-->隐式接口
var w Writer
w = os.Stdout
fmt.Fprintf(w, "Hello, writer\n")

//-->Stringers
p1 := Person{"xjliao", 25}
p2 := Person{"lllou", 25}
fmt.Println(p1, p2)

// 错误
if err := run(); err != nil {
fmt.Println(err)
}

// Readers
r := strings.NewReader("Hello, Reader!")
br := make([]byte, 8)
for {
n, err := r.Read(br)
fmt.Printf("n = %v err = %v br=%v\n", n, err, br)
fmt.Printf("br[:n] = %q\n", br[:n])
if err == io.EOF {
break
}
}

//--> 图片
m := image.NewRGBA(image.Rect(0, 0, 100, 100))
fmt.Println("m.Bounds=", m.Bounds())
fmt.Println(m.At(0, 0).RGBA)

//--> Web服务器
var h Hello
err := http.ListenAndServe("localhost:4000", h)
if err != nil {
log.Fatal(err)
}

}

Golang 快速入门 slice array map 函数闭包

Golang 快速入门 slice array map 函数闭包

内容来自https://tour.golang.org/

格式说明:

// 正常注释
//——-依赖关系
//—>关联关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package main

import (
"fmt"
"math"
)

// 结构体
type Vertex struct {
X int
Y int
}

// 字面结构体
var (
v1 = Vertex{1, 2}
v2 = Vertex{X: 1}
v3 = Vertex{}
p = &Vertex{1, 2}
)

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

// map
type Location struct {
Lat, Long float64
}

var m map[string]Location

// 字面map
var mi = map[string]Location{
"Bell Labs": {34.34499, -89.09492},
"Google": {34.34123, -123.34321},
}

// 字面map(续)
// 如果顶级的类型只有类型名的话,可以在文法的元素中省略键名。
// 这里的类型名是指Location
var mj = map[string]Location{
"Bell Labs": {34.34499, -89.09494},
"Google": {34.34123, -123.34321},
}

func main() {
//指针
i, j := 42, 8000

p := &i
fmt.Println(p)
*p = 21
fmt.Println(i)
fmt.Println(p)
fmt.Println(&i)

p = &j
*p = *p / 2
fmt.Println(j)

//-->结构体
fmt.Println(Vertex{1, 2})

// 结构体字段
v := Vertex{3, 4}
v.X = 5
fmt.Println(v.X)

// 结构体指针
q := &v
q.X = 1000
fmt.Println(v)

//-->字面结构体
fmt.Println(v1, p, v2, v3)

// 数组
var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
fmt.Println(a)

// 片
s := []int{2, 3, 5, 7, 11, 13}
fmt.Println("s==", s)
for i := 0; i < len(s); i++ {
fmt.Printf("s[%d] == %d\n", i, s[i])
}

// 对slice切片
// 从下标1开始到4,但不包含4 fmt.Println("s[1:4] ==", s[1:4])
// 省略下标从0开始到3,但不包含3
fmt.Println("s[:3] ==", s[:3])
// 省略上标 len(s)结束
fmt.Println("s[4:] ==", s[4:])
// 构造切片
a1 := make([]int, 5)
printSlice("a1", a1)
b := make([]int, 0, 5)
printSlice("b", b)
c := b[:2]
printSlice("c", c)
d := c[2:5]
printSlice("d", d)

// nil slice, slice 的零值是nil
// 一个 nil 的 slice 的长度和容量是 0。
var z []int
fmt.Println(z, len(z), cap(z))
if z == nil {
fmt.Println("nil!")
}

// 像slice添加元素
z = append(z, 0)
printSlice("z", z)

z = append(z, 1)
printSlice("z", z)

z = append(z, 2, 3, 4)
printSlice("z", z)

// range 范围
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
// range 范围续
pow1 := make([]int, 10)
for i := range pow1 {
pow1[i] = 1 << uint(i)
}
// 如果不适用_, 则value默认是rang的index值
for _, vlaue := range pow1 {
fmt.Printf("value=%d\n", vlaue)
}

//-->map
m = make(map[string]Location)
m["Bell Labs"] = Location{
40.99922, -73.94391,
}
fmt.Println(m["Bell Labs"])

//-->字面map
fmt.Println(mi)
//-->字面map(续)
fmt.Println(mj)
// 修改map
mk := make(map[string]Location)
mk["liaoxinjian"] = Location{120.00, -110.00}
mk["loulihua"] = Location{120.00, -110.00}
fmt.Println("mk=", mk)
// 删除
delete(mk, "loulihua")
fmt.Println("loulihua.location", mk["loulihua"])
// 通过双赋值检测某个键存在
v1, ok := m["loulihua"]
fmt.Println("value=", v1, " Present?", ok)

// 函数值
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(3, 4))

//-->函数的闭包
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}

// 函数的闭包
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}

func printSlice(s string, x []int) {
fmt.Printf("%s len=%d cap=%d %v\n", s, len(x), cap(x), x)
}

Golang 快速入门 流程控制语句

Golang 快速入门 流程控制语句

内容来自https://tour.golang.org/

格式说明:

// 正常注释
//——-依赖关系
//—>关联关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package main

import (
"fmt"
"math"
"runtime"
"time"
)

func sqrt(x float64) string {
// if
if x < 0 {
return sqrt(-x) + "i"
} else {
fmt.Printf("x < 0")
}

return fmt.Sprint(math.Sqrt(x))
}

// if 便捷语句
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
}

return lim
}

func main() {
// for
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
fmt.Println(sum)

// for 续
sumb := 1
for sumb < 1000 {
sumb += sumb
}
fmt.Println(sumb)

// while 用for表示, 所以没有while了
sumc := 1
for sumc < 1000 {
sumc += sumc
}
fmt.Println(sumc)

// 无限循环 这里先注释掉, 要不然后面无法执行
//for {
//
//}

//-->if else
fmt.Println(sqrt(2))

//-->if 便捷语句
fmt.Println(pow(3, 2, 10))

// switch 当符合
// fallthrough 这个关键字的作用和java的switch 不加break的
// 执行顺序有上而下,匹配成功停止
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "linux":
fmt.Println("Linux.")
case "darwin":
fmt.Println("OS X")
// 当进入这里时,使用fallthrough,还会执行defluat
fallthrough
default:
// freebsd, openbsd,
fmt.Println("%s", os)
}

// 没有条件的switch
// 和 switch true {
//case:
//default:
//}
// 一样
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning.")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}

// defer 延迟函数的执行直到上层函数返回
// 延迟调用的参数会立刻生成,但是在上层函数返回前函数都不会被调用
defer World()
fmt.Println("hello")
// defer 栈
fmt.Println("counting.....")
for i := 0; i < 100; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}

func World() {
fmt.Println("world")
}

Golang 快速入门 基础 包 变量 函数

Golang 快速入门 基础 包 变量 函数

内容来自https://tour.golang.org/

格式说明:

// 正常注释
//——-依赖关系
//—>关联关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// 包
package main

// 导入方式1
import "fmt"

// 导入方式2
import (
"math"
"math/cmplx"
)

// 多值返回
func swap(lastname string, firstname string) (string, string) {
return firstname, lastname
}

// 命名返回值
func split(sum int) (x, y, sm int) {
sm = sum
x = sum * 4 / 9
y = sum - x
return
}

// 变量
var c, python, java bool
var x int

// 初始化变量
var v, u int = 2, 1

// 变量组
var (
// Go 基本数据类型
// bool, string, int, int8, int16, int32, int64,
// uint, uint8, uint16, uint32, uint64, uintptr,
// byte(unit8的别名), rune(int32的别名,代表一个Unicode码)
// float32, float64
ToBe bool = false
MaxInt uint64 = 1<<64 - 1
z complex128 = cmplx.Sqrt(-5 + 12i)
)

// 常量(常量不能使用 := 语法定义)
const Pi = 3.14

//-----
//数值常量(数值常量是高精度的值)
const (
Big = 1 << 100
Small = Big >> 99
)

func needInt(x int) int {
return x*10 + 1
}

func needFloat(x float64) float64 {
return x * 0.1
}

//-----

func main() {
//-->多值返回&命名返回值
lastname, firstname := swap("jian", "liaoxin")
fmt.Println(lastname, firstname)
fmt.Println(split(100))

//-->变量
var i int
var y int
fmt.Println(i, c, python, java, x, y)
fmt.Println(v, u)

// 短声明变量,只能在函数内使用,函数外的必须以关键字var, func 等等开头
k := 100
fmt.Println(k)
//-->Go基本数据类型
fmt.Println(ToBe, MaxInt, z)

//零值
var q int
var f float64
var w bool
var s string
fmt.Printf("%v %v %v %q\n", q, f, w, s)

// 类型转换
var x, g int = 3, 4
var r float64 = math.Sqrt(float64(x*x + g*g))
var z int = int(r)
fmt.Println(x, g, z)

// 类型推导
v := 42
fmt.Printf("v的类型是%T\n", v)

//-->常量
const World = "世界"
fmt.Println("你好", World)
fmt.Println("原周率Pi=", Pi)
const Truth = true
fmt.Println("Go rules?", Truth)

//-->数值常量
fmt.Println(needInt(Small))
fmt.Println(needFloat(Small))
fmt.Println(needFloat(Big))
}

Objective-C method 'application:didFinishLaunchingWithOptions:' provided by method 'application(_:didFinishLaunchingWithOptions:)'

Error

1
AppDelegate.swift:17:10: Objective-C method 'application:didFinishLaunchingWithOptions:' provided by method 'application(_:didFinishLaunchingWithOptions:)' conflicts with optional requirement method 'application(_:didFinishLaunchingWithOptions:)' in protocol 'UIApplicationDelegate'

Solution

1
2
3
4
5
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {  
}
//replaced to
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
}

python 替换指定文件夹内所有文件的指定内容

code

a.py
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
import os
import os.path

old_str = "old_str"
new_str = "new_str"

inws = "/Users/xjliao/Desktop/tmep"

def is_md(f):
hz_list = ['.md']
test_str = f.lower()
for hz in hz_list:
if test_str.endswith(hz):
return(True)

return(False)

def main():
for wroot, wdirs, wfiles in os.walk(inws):
for wfile in wfiles:
infile = os.path.join(wroot, wfile)
if is_md(infile) == False:
continue

cnts=file(infile).read()
if old_str in cnts:
outfile = infile
fo = open(outfile, 'w')
cnts_new = cnts.replace(old_str, new_str)
fo.write(cnts_new)
fo.close()
print(wfile)
else:
pass

if __name__ == '__main__':
main()

Golang

参考资料:

Golang 官网
英文 中文
Learning Go
Network programming with Go
Go by Example
Programming in Go
国内社区
golanghome.com
golangtc.com
视频教程
http://study.163.com/course/courseMain.htm?courseId=306002

1
import "fmt"

Mac vim7.3 update to vim7.4

转自:Mac中安装Vim7.4

Mac上的Vim

Mac本身其实是预装了Vim的,但是目前的系统中都是Vim7.3版本的,而最新的Vim已经是7.4版了,因此为了能够使用最新版的vim,必须要对Mac中的vim要么升级,要么重装。在折腾过程中,遇到了一些问题,这里记录,以鉴后人。

可选方案

升级原生的Vim7.3

这种方法貌似是最”干净”的,不会引入其他任何多余的东西,对于有洁癖的人这应该是一种比较好的方案。但是,这个方案也有缺点,那就是它会覆盖原生的Vim,会改变系统的默认设置,并且一旦升级过程中出现了问题,那你就再也没有可用的vim了。另外一个问题是,以后当你系统升级的时候,很有可能你自己的vim又会被新系统的vim给覆盖,这样会比较麻烦。

使用MacVim

这是一种比较好的方案,Vim官网上也是推荐使用这种方案的,MacVim是针对Mac系统特别定制的Vim版本,安装过程也很简单,网上一搜一大把。它功能上和vim完全一致,不会有任何的问题。要说这种方案其实已经算是一个完美的解决方案了,但是它有一点不太方便的地方,那就是不能直接在终端中使用vim,每次使用MacVim的时候都会单独开启一个窗口,有点类似于windows中的gvim。而我个人是比较习惯在终端中写代码的,因此这个方案还是不能满足我的需求。

自己编译

这是一种终极的方案,但是自己编译的时候注意要手动更改默认的安装目录,不然它就会覆盖原生的vim7.3,这样就会变成第一种方案了。将vim7.4安装在其他目录,然后在.bash_profile中添加一个vim命令的别名,将其指向新安装的vim7.4的目录,而不是原生的vim7.3目录。这个方案就能在终端中直接使用vim7.4了,并且不会对原生的vim7.3又任何影响。这个方案唯一的缺点大概就是会在系统中产生两个不同版本的vim了,这也许对一些有洁癖的人是难以接受的。

我自己最终选择了第三套方案,也就是自己编译新版本的vim.

重新编译

好了,现在让我们开始折腾吧。

首先上vim的官网下载vim7.4的源文件,地址是:http://www.vim.org/sources.php

新建目录/opt/loacl,这个目录就是用来存放我们新安装的vim7.4的,你也可以建立其他的目录,这里只是一个示例。

进入vim的源文件目录中,在终端中运行命令:

1
./configure --with-features=huge --enable-pythoninterp=yes  --enable-cscope --enable-fontset --enable-perlinterp --enable-rubyinterp --with-python-config-dir=/usr/lib/python2.6/config --prefix=/opt/local

这个命令是完成对vim的一些配置选项,启用了python和ruby的支持特性,这还是比较重要的,因为vim中有些插件会使用python和ruby的,如果没有开启这些特性,有些插件是无法运行的。在这些配置命令中,最后一个—prefix=/opt/local是用来指明安装目录的,你也可以修改成你自己的目录。

在写配置命令的时候,需要注意的是,不能写上—enable-gui,这是开启gui特性的,但是我们是在终端环境下安装的,因此不能开启这个特性,否则会出现编译错误。

在终端中执行make命令.在make过程中,会出现一个错误,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
:info:build os_unix.c:830:46: warning: declaration of 'struct sigaltstack' will not be visible outside of this function [-Wvisibility]
:info:build extern int sigaltstack __ARGS((const struct sigaltstack *ss, struct sigaltstack *oss));
:info:build ^
:info:build ./os_unix.h:88:21: note: expanded from macro '__ARGS'
:info:build # define __ARGS(x) x
:info:build ^
:info:build os_unix.c:830:13: error: conflicting types for 'sigaltstack'
:info:build extern int sigaltstack __ARGS((const struct sigaltstack *ss, struct sigaltstack *oss));
:info:build ^
:info:build /usr/include/signal.h:89:5: note: previous declaration is here
:info:build int sigaltstack(const stack_t * __restrict, stack_t * __restrict) __DARWIN_ALIAS(sigaltstack);
:info:build ^
:info:build 1 warning and 1 error generated.
:info:build make[1]: *** [objects/os_unix.o] Error 1
:info:build make[1]: *** Waiting for unfinished jobs….)

解决方案也很简单,只需要在os_unix.h中加上#include 就可以了。

执行make install.执行完成之后,vim7.4就安装完成了。

添加vim命令的别名,在.bash_profile中添加一行alias vim=’/opt/local/bin/vim’,然后在终端中执行source ~/.bash_profile

好了,现在你的Mac系统已经安装好了vim7.4了,现在可以开始愉快的工作了。

Swift subscripts

官方教程

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/ Swift_Programming_Language/Subscripts.html#//apple_ref/doc/uid/TP40014097-CH16-ID305

下标脚本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
subscript(index: Int) -> Int {
get {
// 返回与入参匹配的Int类型的值
}

set(newValue) {
// 执行赋值操作
}
}

subscript(index: Int) -> Int {
// 返回与入参匹配的Int类型的值
}

demo

1
2
3
4
5
6
7
8
9
struct TimesTable {
let multiplier: Int
subscript(index: Int) -> Int {
return multiplier * index
}
}
let threeTimesTable = TimesTable(multiplier: 3)
println("3的6倍是\(threeTimesTable[6])")
// 输出 "3的6倍是18"

下标脚本用法

1
2
var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2

注意:

Swift 中字典的附属脚本实现中,在get部分返回值是Int?,上例中的numberOfLegs字典通过附属脚本返回的是一个Int?或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是nil;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为nil即可

下标脚本选项

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
struct Matrix {
let rows: Int, columns: Int
var grid: [Double]
init(rows: Int, columns: Int) {
self.rows = rows
self.columns = columns
grid = Array(count: rows * columns, repeatedValue: 0.0)
}
func indexIsValidForRow(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
subscript(row: Int, column: Int) -> Double {
get {
assert(indexIsValidForRow(row, column: column), "Index out of range")
return grid[(row * columns) + column]
}
set {
assert(indexIsValidForRow(row, column: column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
}

var matrix = Matrix(rows: 2, columns: 2)

// 示意图
grid = [0.0, 0.0, 0.0, 0.0]

col0 col1
row0 [0.0, 0.0,
row1 0.0, 0.0]

matrix[0, 1] = 1.5
matrix[1, 0] = 3.2

[0.0, 1.5,
3.2, 0.0]

Swift method

官方教程

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/ Swift_Programming_Language/Methods.html#//apple_ref/doc/ uid/TP40014097-CH15-ID234

实例方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Counter {
var count = 0
func increment() {
count++
}
func incrementBy(amount: Int) {
count += amount
}
func reset() {
count = 0
}
}

let counter = Counter()
// 初始计数值是0
counter.increment()
// 计数值现在是1
counter.incrementBy(5)
// 计数值现在是6
counter.reset()
// 计数值现在是0

在实例方法中修改值类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveByX(2.0, y: 3.0)
println("The point is now at (\(somePoint.x), \(somePoint.y))")
// 输出 "The point is now at (3.0, 4.0)"

let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveByX(2.0, y: 3.0)
// this will report an error

在变异方法中给self赋值

1
2
3
4
5
func sayHelloWorld() -> String {
return "hello, world"
}
println(sayHelloWorld())
// prints "hello, world"

类型方法

class 关键字类似java的static类方法和objective-c的+方法
注:

class 变量apple暂未实现, 但方法可以使用
static 只用用于值类型,比如枚举和结构体

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
class SomeClass {
class func someTypeMethod() {
// type method implementation goes here
}
}
SomeClass.someTypeMethod()

class Player {
var tracker = LevelTracker()
let playerName: String
func completedLevel(level: Int) {
LevelTracker.unlockLevel(level + 1)
tracker.advanceToLevel(level + 1)
}
init(name: String) {
playerName = name
}
}

var player = Player(name: "Argyrios")
player.completedLevel(1)
println("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")

player = Player(name: "Beto")
if player.tracker.advanceToLevel(6) {
println("player is now on level 6")
} else {
println("level 6 has not yet been unlocked")
}

Swift inheritance

官方教程

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/ Swift_Programming_Language/Inheritance.html#//apple_ref/ doc/uid/TP40014097-CH17-ID193

定义一个基类

1
2
3
4
5
6
7
8
9
10
11
12
13
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// 什么也不做-因为车辆不一定会有噪音
}
}

let someVehicle = Vehicle()
println("Vehicle: \(someVehicle.description)")
// Vehicle: traveling at 0.0 miles per hour

子类生成

为了指明某个类的超类,将超类名写在子类名的后面,用冒号分隔:

1
2
3
class SomeClass: SomeSuperclass {
// 类的定义
}

demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Bicycle: Vehicle {
var hasBasket = false
}

let bicycle = Bicycle()
bicycle.hasBasket = true

bicycle.currentSpeed = 15.0
println("Bicycle: \(bicycle.description)")

class Tandem: Bicycle {
var curerntNumberOfPassengers = 0
}

let tandem = Tandem()
tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22.0
println("Tandem: \(tandem.description)")

重写(Overriding)

子类可以为继承来的实例方法(instance method),类方法(class method),实例属性(instance property),或下标脚本(subscript)提供自己定制的实现(implementation)。我们把这种行为叫重写(overriding)。

如果要重写某个特性,你需要在重写定义的前面加上override关键字。这么做,你就表明了你是想提供一个重写版本,而非错误地提供了一个相同的定义。意外的重写行为可能会导致不可预知的错误,任何缺少override关键字的重写都会在编译时被诊断为错误。

override关键字会提醒 Swift 编译器去检查该类的超类(或其中一个父类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。

访问超类的方法,属性及下标脚本

当你在子类中重写超类的方法,属性或下标脚本时,有时在你的重写版本中使用已经存在的超类实现会大有裨益。比如,你可以优化已有实现的行为,或在一个继承来的变量中存储一个修改过的值。

在合适的地方,你可以通过使用super前缀来访问超类版本的方法,属性或下标脚本:

在方法someMethod的重写实现中,可以通过super.someMethod()来调用超类版本的someMethod方法。
在属性someProperty的 getter 或 setter 的重写实现中,可以通过super.someProperty来访问超类版本的someProperty属性。
在下标脚本的重写实现中,可以通过super[someIndex]来访问超类版本中的相同下标脚本。

重写方法

1
2
3
4
5
6
7
8
class Train: Vehicle {
override func makeNoise() {
println("Choo Choo")
}
}

let train = Train()
train.makeNoise()

重写属性

你可以重写继承来的实例属性或类属性,提供自己定制的getter和setter,或添加属性观察器使重写的属性观察属性值什么时候发生改变

重写属性的Getters和Setters

注意:

如果你在重写属性中提供了 setter,那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接通过super.someProperty来返回继承来的值,其中someProperty是你要重写的属性的名字。

1
2
3
4
5
6
7
8
9
10
11
class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}

let car = Car()
car.currentSpeed = 25.0
car.gear = 3
println("Car: \(car.description)")

重写属性观察器

注意:

你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供willSet或didSet实现是不恰当。此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已经为那个属性提供了定制的 setter,那么你在 setter 中就可以观察到任何值变化了。

1
2
3
4
5
6
7
8
9
10
11
class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}

let automatic = AutomaticCar()
automatic.currentSpeed = 30
println("AutomaticCar: \(automatic.description)")

防止重写

你可以通过把方法,属性或下标脚本标记为final来防止它们被重写,只需要在声明关键字前加上@final特性即可。(例如:final var, final func, final class func, 以及 final subscript)

如果你重写了final方法,属性或下标脚本,在编译时会报错。在扩展中,你添加到类里的方法,属性或下标脚本也可以在扩展的定义里标记为 final。

你可以通过在关键字class前添加final特性(final class)来将整个类标记为 final 的,这样的类是不可被继承的,否则会报编译错误。

Swift properties

官方教程

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/ Swift_Programming_Language/Properties.html#//apple_ref/ doc/uid/TP40014097-CH14-ID254

存储属性

1
2
3
4
5
6
7
8
9
struct FixedLengthRange {
var firstValue: Int
let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// the range represents integer values 0, 1, and 2
rangeOfThreeItems.firstValue = 6
// the range now represents integer values 6, 7, and 8return greeting
}

常量结构体实例存储属性

延迟存储属性

延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用lazy来标示一个延迟存储属性。

注意:

必须将延迟存储属性声明成变量(使用var关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。

延迟属性很有用,当属性的值依赖于在实例的构造过程结束前无法知道具体值的外部因素时,或者当属性的值需要复杂或大量计算时,可以只在需要的时候来计算它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class DataImporter {
/*
DataImporter 是一个将外部文件中的数据导入的类。
这个类的初始化会消耗不少时间。
*/

var fileName = "data.txt"
// 这是提供数据导入功能
}

class DataManager {
lazy var importer = DataImporter()
var data = [String]()
// 这是提供数据管理功能
}

let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// DataImporter 实例的 importer 属性还没有被创建

println(manager.importer.fileName)

存储属性和实例变量

如果您有过 Objective-C 经验,应该知道Objective-C为类实例存储值和引用提供两种方法。对于属性来说,也可以使用实例变量作为属性值的后端存储。

Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。 一个类型中属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义

计算属性

除存储属性外,类、结构体和枚举可以定义计算属性,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值

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
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
println("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// 输出 "square.origin is now at (10.0, 10.0)”

便捷 setter 声明

如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称newValue。下面是使用了便捷 setter 声明的Rect结构体代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct AlternativeRect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}

只读属性计算

只有 getter 没有 setter 的计算属性就是只读计算属性。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。

注意:

必须使用var关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。let关键字只用来声明常量属性,表示初始化后再也无法修改的值。

只读计算属性的声明可以去掉get关键字和花括号:

1
2
3
4
5
6
7
8
9
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
return width * height * depth
}
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// prints "the volume of fourByFiveByTwo is 40.0"

属性观察器

性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。

可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重载请参考继承一章的重载。

注意:

不需要为无法重载的计算属性添加属性观察器,因为可以通过 setter 直接监控和响应值的变化。

可以为属性添加如下的一个或全部观察器:

willSet在设置新的值之前调用
didSet在新的值被设置之后立即调用

willSet观察器会将新的属性值作为固定参数传入,在willSet的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称newValue表示。

类似地,didSet观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名oldValue。

注意:

willSet和didSet观察器在属性初始化过程中不会被调用,它们只会当属性的值在初始化之外的地方被设置时被调用。

这里是一个willSet和didSet的实际例子,其中定义了一个名为StepCounter的类,用来统计当人步行时的总步数,可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用

1
2
3
4
func someFunction(parameterName: Int) {
// function body goes here, and can use parameterName
// to refer to the argument value for that parameter
}

全局变量和局部变量

计算属性和属性观察器所描述的模式也可以用于全局变量和局部变量,全局变量是在函数、方法、闭包或任何类型之外定义的变量,局部变量是在函数、方法或闭包内部定义的变量。

前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它提供特定类型的存储空间,并允许读取和写入。

另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器,计算型变量跟计算属性一样,返回一个计算的值而不是存储值,声明格式也完全一样。

注意:

全局的常量或变量都是延迟计算的,跟延迟存储属性相似,不同的地方在于,全局的常量或变量不需要标记lazy特性。
局部范围的常量或变量不会延迟计算。

2、速记外部参数名

1
2
3
4
5
6
7
8
9
10
11
func containsCharacter(#string: String, #characterToFind: Character) -> Bool {
for character in string {
if character == characterToFind {
return true
}
}
return false
}

let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v")
// containsAVee equals true, because "aardvark" contains a "v"

3、默认参数值

1
2
3
4
5
6
7
8
9
10
func join(string s1: String, toString s2: String,
withJoiner joiner: String = " ")
-> String {

return s1 + joiner + s2
}

join(string: "hello", toString: "world", withJoiner: "-")
// returns "hello-world"

join(string: "hello", toString: "world")
// returns "hello world"

4、参数外部名默认值

1
2
3
4
5
6
func join(s1: String, s2: String, joiner: String = " ") -> String {
return s1 + joiner + s2
}

join("hello", "world", joiner: "-")
// returns "hello-world"

5、可变参数

1
2
3
4
5
6
7
8
9
10
11
func arithmeticMean(numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers

6、常量和变量参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func alignRight(var string: String, totalLength: Int, pad: Character) -> String {
let amountToPad = totalLength - count(string)
if amountToPad < 1 {
return string
}
let padString = String(pad)
for _ in 1...amountToPad {
string = padString + string
}
return string
}
let originalString = "hello"
let paddedString = alignRight(originalString, 10, "-")
// paddedString is equal to "-----hello"
// originalString is still equal to "hello"

7、In-Out 参数

1
2
3
4
5
6
7
8
9
10
11
func swapTwoInts(inout a: Int, inout b: Int) {
let temporaryA = a
a = b
b = temporaryA
}

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// prints "someInt is now 107, and anotherInt is now 3"

函数类型

1
2
3
4
5
6
7
8
9
10
func addTwoInts(a: Int, b: Int) -> Int {
return a + b
}
func multiplyTwoInts(a: Int, b: Int) -> Int {
return a * b
}

func printHelloWorld() {
println("hello, world")
}

1、使用函数类型

1
2
3
4
5
6
7
8
9
10
11
var mathFunction: (Int, Int) -> Int = addTwoInts

println("Result: \(mathFunction(2, 3))")
// prints "Result: 5"

mathFunction = multiplyTwoInts
println("Result: \(mathFunction(2, 3))")
// prints "Result: 6"

let anotherMathFunction = addTwoInts
// anotherMathFunction is inferred to be of type (Int, Int) -> Int

2、函数类型作为参数类型

1
2
3
4
5
func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) {
println("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// prints "Result: 8"

3、函数类型作为返回类型

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
func stepForward(input: Int) -> Int {
return input + 1
}
func stepBackward(input: Int) -> Int {
return input - 1
}

func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
return backwards ? stepBackward : stepForward
}

var currentValue = 3
let moveNearerToZero = chooseStepFunction(currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function

println("Counting to zero:")
// Counting to zero:
while currentValue != 0 {
println("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
println("zero!")
// 3...
// 2...
// 1...
// zero!

函数嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backwards ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
println("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
println("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!

Swift nested types

官方教程

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/ Swift_Programming_Language/NestedTypes.html#//apple_ref/doc/uid/TP40014097-CH23-ID242

嵌套类型

枚举类型常被用于实现特定类或结构体的功能。也能够在有多种变量类型的环境中,方便地定义通用类或结构体来使用,为了实现这种功能,Swift允许你定义嵌套类型,可以在枚举类型、类和结构体中定义支持嵌套的类型。

要在一个类型中嵌套另一个类型,将需要嵌套的类型的定义写在被嵌套类型的区域{}内,而且可以根据需要定义多级嵌套。

嵌套类型实例

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
struct BlackjackCard {
// 嵌套定义枚举型Suit
enum Suit: Character {
case Spades = "♠", Hearts = "♡", Diamonds = "♢", Clubs = "♣"
}

// 嵌套定义枚举型Rank
enum Rank: Int {
case Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King, Ace
struct Values {
let first: Int, second: Int?
}
var values: Values {
switch self {
case .Ace:
return Values(first: 1, second: 11)
case .Jack, .Queen, .King:
return Values(first: 10, second: nil)
default:
return Values(first: self.rawValue, second: nil)
}
}
}

// BlackjackCard 的属性和方法
let rank: Rank, suit: Suit
var description: String {
var output = "suit is \(suit.rawValue),"
output += " value is \(rank.values.first)"
if let second = rank.values.second {
output += " or \(second)"
}
return output
}
}

let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades)
println("theAceOfSpades: \(theAceOfSpades.description)")
// 打印出 "theAceOfSpades: suit is ♠, value is 1 or 11"

嵌套类型的引用

在外部对嵌套类型的引用,以被嵌套类型的名字为前缀,加上所要引用的属性名:

1
let heartsSymbol = BlackjackCard.Suit.Hearts.toRaw()

// 红心的符号 为 “♡”
对于上面这个例子,这样可以使Suit, Rank, 和 Values的名字尽可能的短,因为它们的名字会自然的由被定义的上下文来限定。

Swift initialization

官方教程

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/ Swift_Programming_Language/Initialization.html#// apple_ref/doc/uid/TP40014097-CH18-ID203
在此只记录比较特殊的情况

构造过程

1
2
3
init() {
// perform some initialization here
}

demo

1
2
3
4
5
6
7
8
9
10
struct Fahrenhit {
var temperature: Double
init() {
self.temperature = 32.0
}
}

var f = Fahrenheit()
println("The default temperature is \(f.temperature)° Fahrenheit")
// prints "The default temperature is 32.0° Fahrenheit"

默认属性值

1
2
3
struct Fahrenheit {
var temperature = 32.0
}

自定义构造过程

初始化参数

无返回值函数

Swift function

官方教程

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/ Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-ID158

函数的定义和调用

1
2
3
4
func funcName(parameter1: parameterType, parameter2: parameterType) -> returnType {
// do something.
return value
}

demo

1
2
3
4
func sayHello(personName: String) -> String {
let greeting = "Hello, " + personName + "!"
return greeting
}

函数的参数与返回类型

1、多个输入参数

1
2
3
4
5
func halfOpenRangeLength(start: Int, end: Int) -> Int {
return end - start
}
println(halfOpenRangeLength(1, 10))
// prints "9"

2、无参数函数

1
2
3
4
5
func sayHelloWorld() -> String {
return "hello, world"
}
println(sayHelloWorld())
// prints "hello, world"

3、无返回值函数

1
2
3
4
5
func sayGoodbye(personName: String) {
println("Goodbye, \(personName)!")
}
sayGoodbye("Dave")
// prints "Goodbye, Dave!

多个返回值函数

使用元组作为返回类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func minMax(array: [Int]) -> (min: Int, max: Int) {
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}

let bounds = minMax([8, -6, 2, 109, 3, 71])
println("min is \(bounds.min) and max is \(bounds.max)")
// prints "min is -6 and max is 109"

可选元组返回类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func minMax(array: [Int]) -> (min: Int, max: Int)? {
if array.isEmpty { return nil }
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}

if let bounds = minMax([8, -6, 2, 109, 3, 71]) {
println("min is \(bounds.min) and max is \(bounds.max)")
}

函数参数名

1
2
3
4
func someFunction(parameterName: Int) {
// function body goes here, and can use parameterName
// to refer to the argument value for that parameter
}

1、外部参数名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func someFunction(externalParameterName localParameterName: Int) {
// function body goes here, and can use localParameterName
// to refer to the argument value for that parameter
}

func join(s1: String, s2: String, joiner: String) -> String {
return s1 + joiner + s2
}

join("hello", "world", ", ")
// returns "hello, world"

join(string: "hello", toString: "world", withJoiner: ", ")
// returns "hello, world"

2、速记外部参数名

1
2
3
4
5
6
7
8
9
10
11
func containsCharacter(#string: String, #characterToFind: Character) -> Bool {
for character in string {
if character == characterToFind {
return true
}
}
return false
}

let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v")
// containsAVee equals true, because "aardvark" contains a "v"

3、默认参数值

1
2
3
4
5
6
7
8
9
10
func join(string s1: String, toString s2: String,
withJoiner joiner: String = " ")
-> String {

return s1 + joiner + s2
}

join(string: "hello", toString: "world", withJoiner: "-")
// returns "hello-world"

join(string: "hello", toString: "world")
// returns "hello world"

4、参数外部名默认值

1
2
3
4
5
6
func join(s1: String, s2: String, joiner: String = " ") -> String {
return s1 + joiner + s2
}

join("hello", "world", joiner: "-")
// returns "hello-world"

5、可变参数

1
2
3
4
5
6
7
8
9
10
11
func arithmeticMean(numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers

6、常量和变量参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func alignRight(var string: String, totalLength: Int, pad: Character) -> String {
let amountToPad = totalLength - count(string)
if amountToPad < 1 {
return string
}
let padString = String(pad)
for _ in 1...amountToPad {
string = padString + string
}
return string
}
let originalString = "hello"
let paddedString = alignRight(originalString, 10, "-")
// paddedString is equal to "-----hello"
// originalString is still equal to "hello"

7、In-Out 参数

1
2
3
4
5
6
7
8
9
10
11
func swapTwoInts(inout a: Int, inout b: Int) {
let temporaryA = a
a = b
b = temporaryA
}

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// prints "someInt is now 107, and anotherInt is now 3"

函数类型

1
2
3
4
5
6
7
8
9
10
func addTwoInts(a: Int, b: Int) -> Int {
return a + b
}
func multiplyTwoInts(a: Int, b: Int) -> Int {
return a * b
}

func printHelloWorld() {
println("hello, world")
}

1、使用函数类型

1
2
3
4
5
6
7
8
9
10
11
var mathFunction: (Int, Int) -> Int = addTwoInts

println("Result: \(mathFunction(2, 3))")
// prints "Result: 5"

mathFunction = multiplyTwoInts
println("Result: \(mathFunction(2, 3))")
// prints "Result: 6"

let anotherMathFunction = addTwoInts
// anotherMathFunction is inferred to be of type (Int, Int) -> Int

2、函数类型作为参数类型

1
2
3
4
5
func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) {
println("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// prints "Result: 8"

3、函数类型作为返回类型

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
func stepForward(input: Int) -> Int {
return input + 1
}
func stepBackward(input: Int) -> Int {
return input - 1
}

func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
return backwards ? stepBackward : stepForward
}

var currentValue = 3
let moveNearerToZero = chooseStepFunction(currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function

println("Counting to zero:")
// Counting to zero:
while currentValue != 0 {
println("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
println("zero!")
// 3...
// 2...
// 1...
// zero!

函数嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backwards ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
println("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
println("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!

Swift extensions

官方教程

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/ Swift_Programming_Language/NestedTypes.html#//apple_ref/doc/uid/TP40014097-CH23-ID242

扩展

扩展就是向一个已有的类、结构体或枚举类型添加新功能(functionality)。这包括在没有权限获取原始源代码的情况下扩展类型的能力(即逆向建模)。扩展和 Objective-C 中的分类(categories)类似。(不过与Objective-C不同的是,Swift 的扩展没有名字。)

Swift 中的扩展可以:

添加计算型属性和计算静态属性
定义实例方法和类型方法
提供新的构造器
定义下标
定义和使用新的嵌套类型
使一个已有类型符合某个协议

扩展语法(Extension Syntax)

1
2
3
4
5
声明一个扩展使用关键字extension

extension SomeType {

// 加到SomeType的新功能写到这里
}

一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议(protocol)。当这种情况发生时,协议的名字应该完全按照类或结构体的名字的方式进行书写:

1
2
3
extension SomeType: SomeProtocol, AnotherProctocol {
// 协议实现写到这里
}

计算型属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
extension Double {
var km: Double { return self * 1_000.0 }
var m : Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1_000.0 }
var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
println("One inch is \(oneInch) meters")
// 打印输出:"One inch is 0.0254 meters"
let threeFeet = 3.ft
println("Three feet is \(threeFeet) meters")
// 打印输出:"Three feet is 0.914399970739201 meters"

let aMarathon = 42.km + 195.m
println("A marathon is \(aMarathon) meters long")
// 打印输出:"A marathon is 42195.0 meters long"

构造器

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
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
}

let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))

extension Rect {
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}

let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
// centerRect的原点是 (2.5, 2.5),大小是 (3.0, 3.0)

方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extension Int {
func repetitions(task: () -> ()) {
for i in 0..<self {
task()
}
}
}

3.repetitions({
println("Hello!")
})
// Hello!
// Hello!
// Hello!

//可以使用 trailing 闭包使调用更加简洁:
3.repetitions{
println("Goodbye!")
}
// Goodbye!
// Goodbye!
// Goodbye!

变异实例方法

1
2
3
4
5
6
7
8
9
10
extension Int {
mutating func square() {
self = self * self
}
}

var threeInt = 3
threeInt.square()
let threeSquare = threeInt
println(threeSquare)

下标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extension Int {
subscript(var digitIndex: Int) -> Int {
var decimalBase = 1
while digitIndex > 0 {
decimalBase *= 10
--digitIndex
}
return (self / decimalBase) % 10
}
}
746381295[0]
// returns 5
746381295[1]
// returns 9
746381295[2]
// returns 2
746381295[8]
// returns 7

746381295[9]
//returns 0, 即等同于:
0746381295[9]

嵌套类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extension Character {
enum Kind {
case Vowel, Consonant, Other
}
var kind: Kind {
switch String(self).lowercaseString {
case "a", "e", "i", "o", "u":
return .Vowel
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
return .Consonant
default:
return .Other
}
}
}

Swift enumerations

原文地址:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/ Swift_Programming_Language/Enumerations.html#//apple_ref/doc/uid/TP40014097-CH12-ID145

枚举语法

1
2
3
enum SomeEnumeration {
// enumeration definition goes here
}

example

1
2
3
4
5
6
7
8
9
10
11
12
13
enum CompassPoint {
case North
case South
case East
case West
}

enum Planet {
case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}

var directionToHead = CompassPoint.West
directionToHead = .East

匹配枚举值和Switch语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
directionToHead = .South
switch directionToHead {
case .North:
println("Lots of planets have a north")
case .South:
println("Watch out for penguins")
case .East:
println("Where the sun rises")
case .West:
println("Where the skies are blue")
}
// 输出 "Watch out for penguins”

let somePlanet = Planet.Earth
switch somePlanet {
case .Earth:
println("Mostly harmless")
default:
println("Not a safe place for humans")
}
// 输出 "Mostly harmless”

相关值

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
enum Barcode {
case UPCA(Int, Int, Int)
case QRCode(String)
}

var productBarcode = Barcode.UPCA(8, 85909_51226, 3)
productBarcode = .QRCode("ABCDEFGHIJKLMNOP")

switch productBarcode {
case .UPCA(let numberSystem, let identifier, let check):
println("UPC-A with value of \(numberSystem), \(identifier), \(check).")
case .QRCode(let productCode):
println("QR code with value of \(productCode).")
}
// 输出 "QR code with value of ABCDEFGHIJKLMNOP.”

switch productBarcode {
case let .UPCA(numberSystem, identifier, check):
println("UPC-A with value of \(numberSystem), \(identifier), \(check).")
case let .QRCode(productCode):
println("QR code with value of \(productCode).")
}
// 输出 "QR code with value of ABCDEFGHIJKLMNOP."

switch productBarcode {
case let .UPCA(numberSystem, identifier, check):
println("UPC-A with value of \(numberSystem), \(identifier), \(check).")
case let .QRCode(productCode):
println("QR code with value of \(productCode).")
}
// 输出 "QR code with value of ABCDEFGHIJKLMNOP."

原始值

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
enum ASCIIControlCharacter: Character {
case Tab = "t"
case LineFeed = "n"
case CarriageReturn = "r"
}

let acc = ASCIIControlCharacter.Tab
println("acc=\(acc.rawValue)")

enum Planet1: Int {
case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}

let earthsOrder = Planet1.Earth.rawValue

let positionToFind = 9
if let somePlanet = Planet(rawValue: positionToFind) {
switch somePlanet {
case .Earth:
println("Mostly harmless")
default:
println("Not a safe place for humans")
}
} else {
println("There isn't a planet at position \(positionToFind)")
}
// 输出 "There isn't a planet at position 9

Swift deinitialization

官方教程

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/ Swift_Programming_Language/Deinitialization.html#//apple_ref/doc/uid/TP40014097-CH19-ID142

析构过程

Swift 会自动释放不再需要的实例以释放资源。如自动引用计数那一章描述,Swift 通过自动引用计数(ARC)处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前关闭该文件。

在类的定义中,每个类最多只能有一个析构函数。析构函数不带任何参数,在写法上不带括号:

1
2
3
deinit {
// 执行析构过程
}

demo
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
struct Bank {
static var coinsInBank = 10_000
static func vendCoins(var numberOfCoinsToVend: Int) -> Int {
numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank)
return numberOfCoinsToVend
}

static func receiveCoins(coins: Int) {
coinsInBank += coins
}
}


class Player {
var coinsInPurse: Int
init(coins: Int) {
coinsInPurse = Bank.vendCoins(coins)
}

func winCoins(coins: Int) {
coinsInPurse += Bank.vendCoins(coins)
}

deinit {
Bank.receiveCoins(coinsInPurse)
}
}

var playerOne: Player? = Player(coins: 100)
println("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
// 输出 "A new player has joined the game with 100 coins"
println("There are now \(Bank.coinsInBank) coins left in the bank")
// 输出 "There are now 9900 coins left in the bank"

playerOne = nil
println("PlayerOne has left the game")
// 输出 "PlayerOne has left the game"
println("The bank now has \(Bank.coinsInBank) coins")
// 输出 "The bank now has 10000 coins"

Swift closures

原文地址:http://fuckingclosuresyntax.com/

How Do I Declare a Closure in Swift?

As a variable:

1
var closureName: (parameterTypes) -> (returnType)

As an optional variable:

1
var closureName: ((parameterTypes) -> (returnType))?

As a type alias:

1
typealias closureType = (parameterTypes) -> (returnType)

As a constant:

1
let closureName: closureType = { ... }

As an argument to a function call:

1
func({(parameterTypes) -> (returnType) in statements})

As a function parameter:

1
array.sort({ (item1: Int, item2: Int) -> Bool in return item1 < item2 })

As a function parameter with implied types:

1
array.sort({ (item1, item2) -> Bool in return item1 < item2 })

As a function parameter with implied return type:

1
array.sort({ (item1, item2) in return item1 < item2 })

As the last function parameter:

1
array.sort { (item1, item2) in return item1 < item2 }

As the last parameter, referred to by numbers:

1
array.sort { return $0 < $1 }

As the last parameter, with an implied return value:

1
array.sort { $0 < $1 }

As the last parameter, as a reference to an existing function:

1
array.sort(<)

This site is not intended to be an exhaustive list of all possible uses of closures.

Unable to access this site due to the profanity in the URL? http://goshdarnclosuresyntax.com is a more work-friendly mirror.

Swift type casting

官方教程

https://developer.apple.com/library/prerelease/ios/ documentation/Swift/Conceptual/ Swift_Programming_Language/TypeCasting.html#//apple_ref/ doc/uid/TP40014097-CH22-ID338

类型转换

类型转换可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。

类型转换在 Swift 中使用is 和 as操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。
is:检查类型
as 类型转换

定义一个类层次作为例子

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
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}

class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}

class Song: MediaItem {
var artist: String
init(name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}

let library = [
Movie(name: "Casablanca", director: "Michael Curtiz"),
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
Movie(name: "Citizen Kane", director: "Orson Welles"),
Song(name: "The One And Only", artist: "Chesney Hawkes"),
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
// the type of "library" is inferred to be MediaItem[]

类型检查

使用is

1
2
3
4
5
6
7
8
9
10
11
12
13
var movieCount = 0
var songCount = 0

for item in library {
if item is Movie {
++movieCount
} else if item is Song {
++songCount
}
}

println("Media library contains \(movieCount) movies and \(songCount) songs")
// prints "Media library contains 2 movies and 3 songs"

向下转型

使用as

1
2
3
4
5
6
7
8
9
10
11
12
13
for item in library {
if let movie = item as? Movie {
println("movie.name=\(movie.name), movie.dir=\(movie.director)")
} else if let song = item as? Song {
println("Song: '\(song.name)', by \(song.artist)")
}
}

// Movie: 'Casablanca', dir. Michael Curtiz
// Song: 'Blue Suede Shoes', by Elvis Presley
// Movie: 'Citizen Kane', dir. Orson Welles
// Song: 'The One And Only', by Chesney Hawkes
// Song: 'Never Gonna Give You Up', by Rick Astley

Any和AnyObject的类型转换

Swift为不确定类型提供了两种特殊类型别名:

AnyObject可以代表任何class类型的实例。
Any可以表示任何类型,除了方法类型(function types)。
注意: 只有当你明确的需要它的行为和功能时才使用Any和AnyObject。在你的代码里使用你期望的明确的类型总是更好的。

AnyObject类型

1
2
3
4
5
6
7
8
9
let someObjects: [AnyObject] = [
Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"),
Movie(name: "Moon", director: "Duncan Jones"),
Movie(name: "Alien", director: "Ridley Scott")]

for object in someObjects {
let movie = object as Movie
println("Movie: '\(movie.name)', dir. \(movie.director)")
}

Any类型

这里有个示例,使用 Any 类型来和混合的不同类型一起工作,包括非class类型。它创建了一个可以存储Any类型的数组 things。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
for thing in things {
switch thing {
case 0 as Int:
println("zero as an Int")
case 0 as Double:
println("zero as a Double")
case let someInt as Int:
println("an integer value of \(someInt)")
case let someDouble as Double where someDouble > 0:
println("a positive double value of \(someDouble)")
case is Double:
println("some other double value that I don't want to print")
case let someString as String:
println("a string value of \"\(someString)\"")
case let (x, y) as (Double, Double):
println("an (x, y) point at \(x), \(y)")
case let movie as Movie:
println("a movie called '\(movie.name)', dir. \(movie.director)")
case let stringConverter as String -> String:
println(stringConverter("Michael"))
default:
println("something else")
}
}

Swift automatic reference counting

官方教程

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/ Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/ TP40014097-CH20-ID48

自动引用计数

Swift 使用自动引用计数(ARC)这一机制来跟踪和管理你的应用程序的内存。通常情况下,Swift 的内存管理机制会一直起着作用,你无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时,自动释放其占用的内存。

然而,在少数情况下,ARC 为了能帮助你管理内存,需要更多的关于你的代码之间关系的信息。本章描述了这些情况,并且为你示范怎样启用 ARC 来管理你的应用程序的内存。

注意:

引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。

自动引用计数的工作机制

当你每次创建一个类的新的实例的时候,ARC 会分配一大块内存用来储存实例的信息。内存中会包含实例的类型信息,以及这个实例所有相关属性的值。此外,当实例不再被使用时,ARC 释放实例所占用的内存,并让释放的内存能挪作他用。这确保了不再被使用的实例,不会一直占用内存空间。

然而,当 ARC 收回和释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你试图访问这个实例,你的应用程序很可能会崩溃。

为了确保使用中的实例不会被销毁,ARC 会跟踪和计算每一个实例正在被多少属性,常量和变量所引用。哪怕实例的引用数为一,ARC都不会销毁这个实例。

为了使之成为可能,无论你将实例赋值给属性,常量或者是变量,属性,常量或者变量,都会对此实例创建强引用。之所以称之为强引用,是因为它会将实例牢牢的保持住,只要强引用还在,实例是不允许被销毁的。

自动引用计数实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person {
let name: String
init(name: String) {
self.name = name
println("\(name) is being initialized")
}
deinit {
println("\(name) is being deinitialized")
}
}

var reference1: Person?
var reference2: Person?
var reference3: Person?

reference1 = Person(name: "John Appleseed")

reference2 = reference1
reference3 = reference1

类实例之间的循环强引用

解决实例之间的循环强引用

1
2
3
4
5
func sayGoodbye(personName: String) {
println("Goodbye, \(personName)!")
}
sayGoodbye("Dave")
// prints "Goodbye, Dave!

弱引用

无主引用

和弱引用类似,无主引用不会牢牢保持住引用的实例。和弱引用不同的是,无主引用是永远有值的。因此,无主引用总是被定义为非可选类型(non-optional type)。你可以在声明属性或者变量时,在前面加上关键字unowned表示这是一个无主引用。

由于无主引用是非可选类型,你不需要在使用它的时候将它展开。无主引用总是可以被直接访问。不过 ARC 无法在实例被销毁后将无主引用设为nil,因为非可选类型的变量不允许被赋值为nil。

如果你试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误。使用无主引用,你必须确保引用始终指向一个未销毁的实例。
还需要注意的是如果你试图访问实例已经被销毁的无主引用,程序会直接崩溃,而不会发生无法预期的行为。所以你应当避免这样的事情发生。

由于信用卡总是关联着一个客户,因此将customer属性定义为无主引用,用以避免循环强引用:

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
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { println("\(name) is being deinitialized") }
}

class CreditCard {
let number: Int
unowned let customer: Customer
init(number: Int, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { println("Card #\(number) is being deinitialized") }
}

var john: Customer?

john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)

john = nil

无主引用以及隐式解析可选属性

上面弱引用和无主引用的例子涵盖了两种常用的需要打破循环强引用的场景。

1.Person和Apartment的例子展示了两个属性的值都允许为nil,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。

2.Customer和CreditCard的例子展示了一个属性的值允许为nil,而另一个属性的值不允许为nil,并会潜在的产生循环强引用。这种场景最适合通过无主引用来解决。

3.然而,存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后不能为nil。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。

这使两个属性在初始化完成后能被直接访问(不需要可选展开),同时避免了循环引用。这一节将为你展示如何建立这种关系。

闭包引起的循环强引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func minMax(array: [Int]) -> (min: Int, max: Int)? {
if array.isEmpty { return nil }
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}

if let bounds = minMax([8, -6, 2, 109, 3, 71]) {
println("min is \(bounds.min) and max is \(bounds.max)")
}

解决闭包引起的循环强引用

1
2
3
4
func someFunction(parameterName: Int) {
// function body goes here, and can use parameterName
// to refer to the argument value for that parameter
}

定义捕获列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func someFunction(externalParameterName localParameterName: Int) {
// function body goes here, and can use localParameterName
// to refer to the argument value for that parameter
}

func join(s1: String, s2: String, joiner: String) -> String {
return s1 + joiner + s2
}

join("hello", "world", ", ")
// returns "hello, world"

join(string: "hello", toString: "world", withJoiner: ", ")
// returns "hello, world"

弱引用和无主引用

1
2
3
4
5
6
7
8
9
10
11
func containsCharacter(#string: String, #characterToFind: Character) -> Bool {
for character in string {
if character == characterToFind {
return true
}
}
return false
}

let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v")
// containsAVee equals true, because "aardvark" contains a "v"

Swift Advanced Operators

官方教程

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/ Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-ID28

高级运算符

除了基本操作符中所讲的运算符,Swift还有许多复杂的高级运算符,包括了C语言和Objective-C中的位运算符和移位运算。

不同于C语言中的数值计算,Swift的数值计算默认是不可溢出的。溢出行为会被捕获并报告为错误。你是故意的?好吧,你可以使用Swift为你准备的另一套默认允许溢出的数值运算符,如可溢出的加号为&+。所有允许溢出的运算符都是以&开始的。

自定义的结构,类和枚举,是否可以使用标准的运算符来定义操作?当然可以!在Swift中,你可以为你创建的所有类型定制运算符的操作。

可定制的运算符并不限于那些预设的运算符,你可以自定义中置,前置,后置及赋值运算符,当然还有优先级和结合性。这些运算符在代码中可以像预设的运算符一样使用,你也可以扩展已有的类型以支持你自定义的运算符

位运算符

位操作符可以操作数据结构中原始数据的每个比特位。位操作符通常在诸如图像处理和创建设备驱动等底层开发中使用,位操作符在同外部资源的数据进行交互的时候也很有用,比如在使用用户协议进行通信的时候,运用位运算符来对原始数据进行编码和解码

1、按位取反运算符

按位取反运算符~对一个操作数的每一位都取反。

1
2
let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // 等于 0b11110000

2、按位与运算符

1
2
3
let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8 = 0b00111111
let middleFourBits = firstSixBits & lastSixBits // 等于 00111100

Swift Classes and Structures

官方教程

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/ Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-ID82

类和结构体对比

Swift 中类和结构体有很多共同点。共同处在于:

定义属性用于存储值
定义方法用于提供功能
定义附属脚本用于访问值
定义构造器用于生成初始化值
通过扩展以增加默认实现的功能
符合协议以对某类提供标准功能

更多信息请参见 属性,方法,下标脚本,初始过程,扩展,和协议。

与结构体相比,类还有如下的附加功能:

继承允许一个类继承另一个类的特征
类型转换允许在运行时检查和解释一个类实例的类型
解构器允许一个类实例释放任何其所被分配的资源
引用计数允许对一个类的多次引用

更多信息请参见继承,类型转换,初始化,和自动引用计数。

定义语法

1
2
3
4
5
6
class SomeClass {
// class definition goes here
}
struct SomeStructure {
// structure definition goes here
}

example

1
2
3
4
5
6
7
8
9
10
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}

类和结构体实例

1
2
let someResolution = Resolution()
let someVideoMode = VideoMode()

访问属性

1
2
3
4
5
6
7
8
9
println("The width of someResolution is \(someResolution.width)")
// prints "The width of someResolution is 0"

println("The width of someVideoMode is \(someVideoMode.resolution.width)")
// prints "The width of someVideoMode is 0"

someVideoMode.resolution.width = 1280
println("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// prints "The width of someVideoMode is now 1280"

结构类型的成员初始化

1
let vga = Resolution(width: 640, height: 480)

结构体和枚举是值类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
println("cinema is now \(cinema.width) pixels wide")
// 输出 "cinema is now 2048 pixels wide"
println("hd is still \(hd.width ) pixels wide")
// 输出 "hd is still 1920 pixels wide"
enum CompassPoint {
case North, South, East, West
}
var currentDirection = CompassPoint.West
let rememberedDirection = currentDirection
currentDirection = .East
if rememberDirection == .West {
println("The remembered direction is still .West")
}
// 输出 "The remembered direction is still .West"

类是引用类型

1
2
3
4
5
6
7
8
9
10
11
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// 输出 "The frameRate property of theEighty is now 30.0"

恒等运算符

Swift 内建了两个恒等运算符:

等价于 ( === )
不等价于 ( !== )

以下是运用这两个运算符检测两个常量或者变量是否引用同一个实例:

1
2
3
if tenEighty === alsoTenTighty {
println("tenTighty and alsoTenEighty refer to the same Resolution instance.")
}

请注意“等价于”(用三个等号表示,===) 与“等于”(用两个等号表示,==)的不同:

“等价于”表示两个类类型(class type)的常量或者变量引用同一个类实例。
“等于”表示两个实例的值“相等”或“相同”,判定时要遵照类设计者定义定义的评判标准,因此相比于“相等”,这是一种更加合适的叫法。

指针

如果你有 C,C++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用指针来引用内存中的地址。一个 Swift 常量或者变量引用一个引用类型的实例与 C 语言中的指针类似,不同的是并不直接指向内存中的某个地址,而且也不要求你使用星号(*)来表明你在创建一个引用。Swift 中这些引用与其它的常量或变量的定义方式相同。

类和结构体的选择

在你的代码中,你可以使用类和结构体来定义你的自定义数据类型。

然而,结构体实例总是通过值传递,类实例总是通过引用传递。这意味两者适用不同的任务。当你在考虑一个工程项目的数据构造和功能的时候,你需要决定每个数据构造是定义成类还是结构体。

按照通用的准则,当符合一条或多条以下条件时,请考虑构建结构体:

结构体的主要目的是用来封装少量相关简单数据值。
有理由预计一个结构体实例在赋值或传递时,封装的数据将会被拷贝而不是被引用。
任何在结构体中储存的值类型属性,也将会被拷贝,而不是被引用。
结构体不需要去继承另一个已存在类型的属性或者行为

合适的结构体候选者包括:

几何形状的大小,封装一个width属性和height属性,两者均为Double类型。
一定范围内的路径,封装一个start属性和length属性,两者均为Int类型。
三维坐标系内一点,封装x,y和z属性,三者均为Double类型。

在所有其它案例中,定义一个类,生成一个它的实例,并通过引用来管理和传递。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。

集合(Collection)类型的赋值和拷贝行为

Swift 中字符串(String),数组(Array)和字典(Dictionary)类型均以结构体的形式实现。这意味着String,Array,Dictionary类型数据被赋值给新的常量(或变量),或者被传入函数(或方法)中时,它们的值会发生拷贝行为(值传递方式)。

Objective-C中字符串(NSString),数组(NSArray)和字典(NSDictionary)类型均以类的形式实现,这与Swfit中以值传递方式是不同的。NSString,NSArray,NSDictionary在发生赋值或者传入函数(或方法)时,不会发生值拷贝,而是传递已存在实例的引用。

注意: 以上是对于数组,字典,字符串和其它值的拷贝的描述。 在你的代码中,拷贝好像是确实是在有拷贝行为的地方产生过。然而,在 Swift 的后台中,只有确有必要,实际(actual)拷贝才会被执行。Swift 管理所有的值拷贝以确保性能最优化的性能,所以你也没有必要去避免赋值以保证最优性能。(实际赋值由系统管理优化)

Cordova 初步脚手架

官方教程

http://cordova.apache.org/docs/en/4.0.0/guide_platforms_ios_webview.md.html#iOS%20WebViews

Cordova debugging

Cordova debugging

Debugging your Application

With the desktop browser, you can leverage the browser’s debugging tools. In a WebKit browser, you can open WebInspector to inspect the DOM and debug JavaScript. Most other browsers have an equivalent debugging tool.

Additional Tools

PhoneGap Emulator uses Ripple to emulate the PhoneGap API.
Ripple Emulator emulates the PhoneGap API.
ScreenQueri.es to preview exact device screen sizes.
thumbs.js is a TouchEvent polyfill.
PhoneGap Desktop is a mocking library for the PhoneGap API.

Remote Web Inspector

After you’ve developed the first stage of your application with your desktop browser, you will want to test it on a mobile device. The main hurdle with testing on a mobile device is that you will not have access to WebInspector. This is where a remote web inspector steps in.

Safari Remote Debugging

If you are doing iOS PhoneGap debugging and have the Safari Develop Menu enabled, you can access the currently active session through the built-in Safari Web Inspector. To activate, go to Develop -> (iPad || iPhone) Simulator (normally, the third menu item) and click the active session you want to connect to. Voila!

Chrome Remote Debugging

If you are doing Android PhoneGap debugging and have an Android 4.4 device and Chrome 30+, you can use the new WebView Debugging tools added in Android 4.4. If you are using Cordova 3.3 or higher, this is already supported, and only requires the Debuggable flag in your AndroidManifest.xml. For Cordova 3.2, you will need to enable WebView debugging using some code, or by use of a plugin.

GapDebug

A complete iOS and Android debugging experience for PhoneGap and Cordova apps, debug on both Windows and Mac platforms. Automated plug-and-go debugging with no requirement to build debug support into your app. GapDebug integrated versions of the Safari Webkit Inspector for iOS debugging and Chrome Dev Tools for Android debugging. GapDebug is maintained by Genuitec and is a always free tool.

Try GapDebug here.

Weinre

Weinre is a remote WebInspector that will debug any browser remotely. It is not quite a full-fledged debugger, but gives you a live view of the DOM and access to the JavaScript lang:bash. Unfortunately, no breakpoints or stack traces are available, but the JavaScript lang:bash can lend clues about errors.

You can run Weinre locally or use debug.phonegap.com.

WebKit’s Remote Web Inspector

WebKit has the ability to connect it’s WebInspector to remote WebKit browsers, although few mobile platform support this feature.

BlackBerry supports Remote Web Inspector
iOS has unofficial support for Remote Web Inspector
jsHybugger

jsHybugger is a (commercially available) tool that lets you debug PhoneGap apps on Android using Chrome DevTools. A plugin for PhoneGap 3.x and library for PhoneGap 2.x is available. Install jsHybugger from GitHub with the following command. This adds the plugin to your PhoneGap 3.x application.

phonegap local plugin add https://github.com/jsHybugger/cordova-plugin-jshybugger.git
After installing and starting the app on the device, you can use Chrome DevTools on a desktop machine to debug the app.

Example workflow

phonegap create
cd
phonegap local plugin add https://github.com/jsHybugger/cordova-plugin-jshybugger.git
phonegap build android
phonegap run android —device
See the jsHybugger home page for details.

Additional Debugging Resources

JSBin

Log Messages

When all else fails, you can always use lang:bash.log(‘…’); or alert(‘…’); to mobile web application. I know it’s not pretty, but it can help when you’re in a jam.

Advanced log message handling can be found on Exception Log Handling.

Android

On Android, all lang:bash.log(‘…’); messages will appear as printouts in the command-line tool logcat, which is bundled with the Android SDK and integrated into the Android Eclipse plugin.

BlackBerry

On BlackBerry, all lang:bash.log(‘…’); are printed to the BlackBerry’s Event Log. The Event Log can be accessed by pressing ALT + LGLG.

iOS

On iOS, all lang:bash.log(‘…’); are output to the Xcode Debug Area lang:bash.