第6期博客(响应链)

iOS响应链中HitTest、nextReponder的介绍

iOS的UIEvent事件有好几种:Touch Events(触摸事件)、Motion Events(运动事件)、Remote Events(远程事件),其中最常用的应该就是Touch Events了,今天我们主要就讲它,核心就是到UIView
func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
以及nextReponder两个

比如成果:魔图&图片浏览 复杂的视图层级拆分

wechatimg166
pb

考一考: 直接继承于UIResponder的类有哪些?

UIResponder ?? 响应链 ??

  • 响应链是什么时候怎样构建的?
  • 事件第一个响应者是怎么确定的?
  • 事件第一个响应者确定后,系统是怎样传递事件的?

响应链的构建

先看看UIResponder类

1
2
3
4
5
6
7
8
9
10
11
12
13
open var next: UIResponder? { get }
open var canBecomeFirstResponder: Bool { get } // default is NO
// default is NO
open func becomeFirstResponder() -> Bool
open var canResignFirstResponder: Bool { get } // default is YES
// default is YES
open func resignFirstResponder() -> Bool
open var isFirstResponder: Bool { get }
open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)

常见的触摸响应链构建

  1. addSubview
  2. ViewController 初始化

res

事件响应过程

核心的方法

1
2
func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
func point(inside point: CGPoint, with event: UIEvent?) -> Bool

注意:hittest & pointinside是uiview的实例方法

比如tableview的cell点击,hitTest寻找响应cell的过程

  • windows
  • windows.rootController (navController).view
  • navController.rootControler (vc).view
  • tableview
  • some cell
  • contentView

Note: 查询过程也不是这么顺利的,有弯路,也有属性限制

  • hittest打算从后往前遍历subviews,直到找到才停止遍历
  • subview必须符合
    • pointInside return YES
    • 属性限制有
      • alpha > 0.01
      • hidden == NO
      • userInteractionEnabled == YES
1
2
3
4
5
6
7
8
9
10
11
// 模拟原生未干预下的代码
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if !isUserInteractionEnabled || isHidden || alpha<=0.01 { return nil }
if !self.point(inside: point, with: event) { return nil }
for subview in subviews.reversed() {
let convertPoint = subview.convert(point, from: self)
let subHitTestView = subview.hitTest(convertPoint, with: event)
if let hitTestView = subHitTestView { return hitTestView }
}
return self
}

那我们干预后,HitTest的实战应用就有

  • 复杂的视图层级的拆分
1
2
3
4
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
return view == self ? nil : view
}
  • 自定义Touch事件传递对象
1
2
3
4
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
return view == someBtn ? anotherBtn : view
}
  • 自定义控件的响应区域及响应层级

view修改响应区域方式有两种,一种如下,它的superview来hitTest,第二种自己hitTest

1
2
3
4
5
6
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if someBtn.frame.insetBy(dx: -10, dy: -10).contains(point) {
return someBtn
}
return super.point(inside: point, with: event)
}

响应链 .nextResponder

和 hitTest自底向上相反,它是从最末端的responder向下传递,hitTest自下而上查找响应的view,nextResponder是基于hitTest基础上,自上而下寻找event的响应者

常见的应用案例:BPUIResponderAdditions中的方法,可以查找view的Controller或navigationController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (nullable __kindof UIResponder *)findNextResonderInClass:(nonnull Class)responderClass
{
UIResponder *next = self;
do {
next = [next nextResponder];
if ([next isKindOfClass:responderClass]) {
break;
}
// next 不为空 且 不是达到最底层的 appdelegate
} while (next!=nil && ![next conformsToProtocol:@protocol(UIApplicationDelegate)]);
return next;
}
// 查询响应最近的 controller
UIViewController *vc = [view findNextResonderInClass:[UIViewController class]];

知识点:

  • 响应链事件传递

通过nextResponder向下传递event

演示

touchesBegan / super.touchesBegan 或 nextResponder.touchesBegan 一直调用下去

  • 第一响应者

    有拦腰截断的意思,原有的顶部第一响应者被修改,响应链中接收事件第一人(常见textField,webView及UImenuController,见demo)

演示

  • Button event 先下传递
  • becomeFirstResponder后,nextResponder无事件下传
  • canBecomeFirstResponder \ becomeFirstResponder 拦截
  • cancelsTouchesInView

系统会将Touch cancel消息发送给hitTestView ,并调用hitTestView的TouchCancel

演示

demo:button的touchUpInside对UIPanGestureRecognizer的影响

通用链接( Universal Links )

  • 通过唯一的网址, 就可以链接一个特定的视图到你的 APP 里面, 不需要特别的 schema
  • 不再需要JavaScript检查平台,跳转也不需要js去做特定处理
  • 比scheme更灵活更安全的匹配URL跳转
  • 实现App与Web一体化

注意:不能使用模拟器调试

image

工作原理:When the app is installed, the system downloads and verifies the site association file for each of its associated domains. If the verification is successful, the app is associated with the domain.

Configure your file server

要求如下:

Alright! So you have your signed apple-app-site-association file. Now you just need to configure your file server to host this for you. There are a few caveats:

  • It must be sent with the header ‘application/pkcs7-mime’
  • It must be sent from the endpoint youdomain.com/apple-app-site-association
  • It must return a 200 http code.

We set up the one for all Branch integrated apps using our Node+Express link servers. Here’s the code we used in case that’s helpful:

1
2
3
4
5
var aasa = fs.readFileSync(__dirname + '/static/apple-app-site-association');
app.get('/apple-app-site-association', function(req, res, next) {
res.set('Content-Type', 'application/pkcs7-mime');
res.status(200).send(aasa);
});

可以从原有的scheme过渡过来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
[self handleRouting:url];
return YES;
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler {
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
[self handleRouting:userActivity.webpageURL];
}
return YES;
}
- (void)handleRouting:(NSURL *)url {
....
}

视频优先推荐
https://developer.apple.com/videos/play/wwdc2015/509/

优秀博客
https://blog.branch.io/how-to-setup-universal-links-to-deep-link-on-apple-ios-9

https://blog.branch.io/ios-9.2-deep-linking-guide-transitioning-to-universal-links

http://blog.hokolinks.com/how-to-implement-apple-universal-links-on-ios-9/

可能出现的bug(巨坑,也可能是配置问题)
http://stackoverflow.com/questions/32751225/ios9-universal-links-does-not-work

移动周分享-第60期

加速库在数学运算中的使用

除了加减乘除外还有好多好多数学运算需要我们处理,但我们很多都没有用到,感觉low爆了

Apple:加速框架文档

Any time you’ve got to make some numbers happen, it’s probably worth it to consider using Accelerate

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 模拟随机数据
var doubles = (0...10000).map {_ in Double(arc4random()%10000)}
// 求和
// 常见的加法求和
let reduceSum = doubles.reduce(0) { $0+$1 }
// Accelerate 封装
let accSum = sum(doubles)
// 求最大值(最小值也一样)
let maxOfArr = max(doubles)
let maxOfArr2 = doubles.maxElement
// let maxOfArr2 = doubles.sort(>).first
// 平均值,哈哈大数据统计,可以测试准确率
let meanValue = mean(doubles)
let meanValue2 = doubles.reduce(0) { $0 + $1/Double(doubles.count) }
meanValue2
// 向量加减乘积
let vector1 = [2,4,5] as [Double]
let vector2 = [3,5,2] as [Double]
let sumArrs = add(vector1, y: vector2)

耗时上对比是不是reduce,map等系统的高阶函数被“加速库”秒了,但使用上貌似reduce,map是比较灵活的

1
2
let newReduceSum = (0...1000).reduce(0) { $0+$1 }
newReduceSum

其他计算的一点的应用

函数混合示例: 使用中文变量😄

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
let 函数点阵密度 = 64
let 频率1 = 4.0
let 相位1 = 0.0
let 幅度1 = 1.0
let 正弦函数1 = (0..<函数点阵密度).map {
幅度1 * sin(2.0 * M_PI / Double(函数点阵密度) * Double($0) * 频率1 + 相位1)
}
let 频率2 = 1.0
let 相位2 = M_PI / 2.0
let 幅度2 = 2.0
let 正弦函数2 = (0..<函数点阵密度).map {
幅度2 * sin(2.0 * M_PI / Double(函数点阵密度) * Double($0) * 频率2 + 相位2)
}
let 频率3 = 10.0
let 相位3 = M_PI / 3.0
let 幅度3 = 4.0
let 正弦函数3 = (0..<函数点阵密度).map {
幅度3 * sin(2.0 * M_PI / Double(函数点阵密度) * Double($0) * 频率3 + 相位3)
}
let 新函数1 = add(正弦函数1, y: 正弦函数2)
let 新函数2 = add(新函数1, y: 正弦函数3)
// Xcode 分栏查看图形排布,尤其是新函数的图形
新函数1.forEach { XCPlaygroundPage.currentPage.captureValue($0, withIdentifier:"新函数1") }
新函数2.forEach { XCPlaygroundPage.currentPage.captureValue($0, withIdentifier:"新函数2") }
正弦函数2.forEach { XCPlaygroundPage.currentPage.captureValue($0, withIdentifier:"正弦函数2") }
正弦函数1.forEach { XCPlaygroundPage.currentPage.captureValue($0, withIdentifier:"正弦函数1") }

傅里叶变换通俗篇讲解

1
2
3
// 查看图像发现‘新函数2’左右有三对波峰,得出它由三个正弦波组成(可对应得出振幅、频率及相位)
let 快速傅里叶转换 = fft(新函数2)
快速傅里叶转换.forEach { XCPlaygroundPage.currentPage.captureValue($0, withIdentifier:"快速傅里叶转换") }

矩阵计算

很多图像处理是根据矩阵做处理的,像素越大,处理性能要求越高

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 简单矩阵示例
// ⎛ 1 2 ⎞ ⎛ 3 2 ⎞ ⎛ 5 6 ⎞
// ⎢ ⎟ * ⎢ ⎟ = ⎢ ⎟
// ⎝ 3 -4 ⎠ ⎝ 1 2 ⎠ ⎝ 5 -2 ⎠
let A = Matrix([[1, 2], [3, -4]])
let B = Matrix([[3, 2], [1, 2]])
let C = A * B
// 利用逆矩阵求解
// ⎛ 1 1 ⎞ ⎛ 3 ⎞ ⎛ 2 ⎞
// ⎢ ⎟ * CC = ⎢ ⎟ CC = ⎢ ⎟
// ⎝ 1 -1 ⎠ ⎝ 1 ⎠ ⎝ 1 ⎠
let AA = Matrix([[1, 1], [1, -1]])
let BB = Matrix([[3], [1]])
let CC = inv(AA) * BB

应用的加速库函数

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
import Accelerate
public func sum(x: [Double]) -> Double {
var result: Double = 0.0
vDSP_sveD(x, 1, &result, vDSP_Length(x.count))
return result
}
public func max(x: [Double]) -> Double {
var result: Double = 0.0
vDSP_maxvD(x, 1, &result, vDSP_Length(x.count))
return result
}
public func mean(x: [Double]) -> Double {
var result: Double = 0.0
vDSP_meanvD(x, 1, &result, vDSP_Length(x.count))
return result
}
public func add(x: [Double], y: [Double]) -> [Double] {
var results = [Double](y)
cblas_daxpy(Int32(x.count), 1.0, x, 1, &results, 1)
return results
}
public func fft(input: [Double]) -> [Double] {
var real = [Double](input)
var imaginary = [Double](count: input.count, repeatedValue: 0.0)
var splitComplex = DSPDoubleSplitComplex(realp: &real, imagp: &imaginary)
let length = vDSP_Length(floor(log2(Float(input.count))))
let radix = FFTRadix(kFFTRadix2)
let weights = vDSP_create_fftsetupD(length, radix)
vDSP_fft_zipD(weights, &splitComplex, 1, length, FFTDirection(FFT_FORWARD))
var magnitudes = [Double](count: input.count, repeatedValue: 0.0)
vDSP_zvmagsD(&splitComplex, 1, &magnitudes, 1, vDSP_Length(input.count))
var normalizedMagnitudes = [Double](count: input.count, repeatedValue: 0.0)
vDSP_vsmulD(sqrt(magnitudes), 1, [2.0 / Double(input.count)], &normalizedMagnitudes, 1, vDSP_Length(input.count))
vDSP_destroy_fftsetupD(weights)
return normalizedMagnitudes
}

Xcode MarkDown的代码文档 ( for swift )

markdown在swift中的应用

goals

  • 描述各个属性、函数和类的真正用途
  • 高亮函数的输入和输出(参数和返回值)
  • 几个月后还能清晰地记得每个函数属性是为了什么
  • 使用工具制作具有专业外观的使用手册(比如:使用 Jazzy
  • Xcode 里写的代码文档能被预览

markdown grammar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#text#:文本标题
**text**:使文本具有加粗的效果
*text*:使文本具有斜体的效果
* text:使文本成为一个无序列表的元素,值得注意的是,有个 * 后面需要有一个空格。同样,可以使用 + 或 - 实现这个的功能
1.text:使文本成为一个有序列表的元素
[linked text](http://some-url.com):使文本成为可以点击的超链接
![image show](http://www.appcoda.com/wp-content/uploads/2016/05/t52_3_help_inspector1.png):可以显示图片
> text:创建一个块引用。
使用 4 个空格或 1 个 tab 来缩进所写的代码块,等价于 HTML 中的 \\ 标签。可以继续使用 4 个空格或 1 个 tab 来添加另一个缩进
如果不想使用空格或 tab 的话,可以使用 ` 。比如, `var myProperty` 会显示成 var myProperty
另一种创建代码块的方法是添加 4 个 `,并从下一行开始写具体的代码,最后添加 4 个 ` 表示结束
反斜杠修饰 Markdown 的特殊字符就可以避免 Markdown 语法的解析了。比如, \**this\** 就不会产生加粗的效果

注释区域: 3 个斜线(///)或以下面的形式开头:

1
2
3
/**
*/
Case
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
It calculates and returns the outcome of the division of the two parameters.
## Important Notes ##
1. Both parameters are **double** numbers.
2. For a proper result the second parameter *must be other than 0*.
3. If the second parameter is 0 then the function will return nil.
*/
func performDivisionnumber1: Double, number2: Double) -> Double! {
if number2 != 0 {
return number1 / number2
}
else {
return nil
}
}

case image

quick look

关键词

  • Parameter
  • Returns
  • Remark
  • SeeAlso
  • Precondiction
  • Requires
  • Todo
  • Version
  • Author
  • Note
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
/**
Another complicated function.
- Parameter fullname: The fullname that will be broken into its parts.
- Returns: A *tuple* with the first and last name.
- Remark:
There's a counterpart function that concatenates the first and last name into a full name.
- SeeAlso: `createFullName(_:lastname:)`
- Precondition: `fullname` should not be nil.
- Requires: Both first and last name should be parts of the full name, separated with a *space character*.
- Todo: Support middle name in the next version.
- Warning: A wonderful **crash** will be the result of a `nil` argument.
- Version: 1.1
- Author: Myself Only
- Note: Too much documentation for such a small function.
*/
func breakFullNamefullname: String) -> (firstname: String, lastname: String) {
let fullnameInPieces = fullname.componentsSeparatedByString(" "
return (fullnameInPieces[0], fullnameInPieces[1])
}

全关键字

Jazzy 自动产生代码文档

Jazzy 是一款可以为 Swift 和 Objective-C 代码产生具有 Apple 风格的代码文档工具。

效果如下

jazzy 效果

下面以Alamofire为例子:

jazzy –help 查看帮助

  • cd Alamofire 的项目path
  • jazzy –output /Users/xcodeyang/Desktop/jazzy_document

参考博客地址

枚举的特殊用法

枚举的特殊用法

源于项目中的应用太广了,但是对swift的很多简单特性不熟。之前那个动画的阶段使用到enum有遇到困难暂时绕开它了

当时的目标就是外部直接赋值操作

如下动画的enum示例:

为了省事,目前直接使用死的文案放在这里

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
enum AnimationPeriod: UInt {
// 动画执行的五个阶段
case Start,First,Second,Third,End
func description() -> String {
switch self {
case .Start, .First: return "正在提取学校最新\n录取条件"
case .Second: return "正在与学校进行匹配"
case .Third, .End: return "正在根据匹配结果\n生成选校方案"
}
}
func duration() -> NSTimeInterval {
switch self {
case .Start: return 0.8
case .First: return 1
case .Second: return 2
case .Third: return 0.5
case .End: return 0.25
}
}
}
extension AnimationPeriod {
mutating func next() {
switch self {
case .Start: self = .First
case .First: self = .Second
case .Second: self = .Third
case .Third: self = .End
default: self = .End
}
}
}

下面我们看看一些枚举的使用

携带参数

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 FamilyType {
case Father(age: Int)
case Mother(age: Int)
case Sister(age: Int)
func gift() -> String {
switch(self) {
case .Sister(let age):
if age > 15 {
return "iphone"
} else {
return "toy"
}
case .Mother(let age) where age < 40:
return "cloth"
default:
return "book"
}
}
}
let someone = FamilyType.Sister(age: 11)
let somebody = FamilyType.Mother(age: 40)
// swift的枚举技巧
let someoneGift = someone.gift() // print toy
let somebodyGift = somebody.gift() // book

####那我们要取值的有

  • 方法一
1
2
3
4
5
6
7
switch someone {
case .Father(let age):
age
case .Sister(let age):
age
default:()
}
  • 方法二
1
2
3
if case .Sister(let age) = someone {
age
}

枚举的嵌套

1
2
3
4
5
6
7
8
9
10
11
enum Colleague {
enum Weight: Int {
case Light
case Mid
case Heavy
}
case iOS(weight: Weight)
case Android(weight: Weight)
case HTML(weight: Weight)
}
let woodenHelmet = Colleague.iOS(weight: .Mid)

枚举的成员变量

之前就一直想要使用枚举做到外部动态对枚举赋值储存操作,
那成员变量用法如下

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
// 根据自身赋值
enum Device {
case iPad, iPhone
var year: Int {
switch self {
case iPhone: return 2007
case iPad: return 2010
}
}
}
// set get 方法对于枚举的成员变量是无效的,允许get调用但set不可执行
enum Book {
case Story, News
var year: Int {
set{
year = newValue
}
get{
return self.year
}
}
}
let myIpad = Device.iPad
myIpad.year
var storyBook = Book.Story
//storyBook.year = 2010 // set 方法此处报错
//storyBook.year // get 无效

初始化,感觉然并软

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
enum AppleDevice {
case iMac(price:Int)
case iPod(price:Int)
case iPhone(price:Int)
init (costMoney: Int) {
if costMoney > 10000 {
self = .iMac(price: costMoney)
} else if costMoney > 2500 {
self = .iPhone(price: costMoney)
} else {
self = .iPod(price: costMoney)
}
}
}
let myDevice = AppleDevice(costMoney: 6000)

元组的使用

多个参数的介入时,可以使用元组,此处常规使用报错暂时无法解决,
使用func函数赋值倒是没有出错。why??

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enum HumanHabit {
case Reading
case PlayGame
case Traveling
}
typealias HumanInfo = (age: Int, name: String, habit: HumanHabit)
func selectRAM(humanInfo: HumanInfo) -> HumanInfo {return (age: 32, name: humanInfo.name, habit: humanInfo.habit)}
func selectCPU(humanInfo: HumanInfo) -> HumanInfo {return (age: humanInfo.age, name: "3.2GHZ", habit: humanInfo.habit)}
func selectGPU(humanInfo: HumanInfo) -> HumanInfo {return (age: humanInfo.age, name: "3.2GHZ", habit: .Reading)}
enum Desktop {
case Cube(HumanInfo)
case Tower(HumanInfo)
case Rack(HumanInfo)
}
let aTower = Desktop.Tower(selectGPU(selectCPU(selectRAM((0, "", .Traveling) as HumanInfo))))

Swift 的 Playground 学习

Swift 的 Playground 学习

我们一开始知道 Playground,也知道然后查看一些变量及变量的历史记录。偶尔还知道可以做视图甚至动画展示但是,现在,你特么看到的是 MarkDown 语法,有没有。

参考文献

使用优势

  • 快速学习swift
  • 快速测试代码效果
  • 验证API
  • 富文本注释

如下所有案例使用Xcode7下加载playground文件,可预览效果
文件下载链接

变量

至上而下执行,显示变量的当前值及历史记录的变化。

1
2
3
4
5
6
7
8
9
var name = "杨志"
name += "平"
// playground下预览
var graph = 0.0
for i in 0...100 {
graph = sin(Double(i)/10.0)
// playground下预览
}

UI视图

简单显示UI的基础元素

1
2
3
4
5
6
7
8
9
10
11
let redView = UIView(frame:CGRectMake(0,0,100,100))
redView.backgroundColor = UIColor.redColor()
redView.layer.cornerRadius = 20
redView.layer.borderWidth = 3
redView.layer.borderColor = UIColor.whiteColor().CGColor
let circle = UIView(frame:CGRectMake(25,25,50,50))
circle.backgroundColor = UIColor.yellowColor()
circle.layer.cornerRadius = 80
redView.addSubview(circle)
// playground下预览

重头戏

动画展示 & 网络请求

这里需要对 XCPlayground 有一点了解,好像还对SpriteKit做了支持文章连接

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
// 这个库需要import
import XCPlayground
let containerView = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 480))
containerView.backgroundColor = UIColor.yellowColor()
XCPlaygroundPage.currentPage.liveView = containerView
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
view.center = CGPoint(x: containerView.center.x, y: containerView.center.y-100)
view.backgroundColor = UIColor.redColor()
containerView.addSubview(view)
// playground下预览
UIView.animateWithDuration(3, delay: 0, usingSpringWithDamping: 0.1, initialSpringVelocity: 6, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
view.center.y += 100
}, completion:{ (animation: Bool) in
view.center.y -= 100
}
)
// 异步操作:网络在家图片
// 旧接口:XCPSetExecutionShouldContinueIndefinitely(true)
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
let url = NSURL(string: "https://avatars1.githubusercontent.com/u/5317671?v=3&s=460")!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) {
data, _, _ in
let image = UIImage(data: data!)
// playground下预览
}
task.resume()
// 快速检查API接口
let url2 = NSURL(string: "http://www.test.51offer.com/mobile/abroad/query_school_by_name?name=%E4%B8%80")!
let task2 = NSURLSession.sharedSession().dataTaskWithURL(url2) {
data, _, _ in
let str = String(NSString(data: data!, encoding: NSUTF8StringEncoding))
// playground下预览
}
task2.resume()

结构介绍

  • sources

    我们直接在 Playground 上面写代码,然后编译器会实时编译我们代码,并将结果显示出来。但是效率很低,source的作用就可以发挥出来

  • resources

    可以作为sandbox使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 直接使用PicButton 这个class.
// 注意点:PicButton的类及初始化方法必须是public的
let btn = PicButton(frame: CGRectMake(0,0,200,100))
// playground下预览
// 如上述:在resources中放入jpg图片,加载本地资源
if let path = NSBundle.mainBundle().pathForResource("swift-playground", ofType: "jpg") {
let image = UIImage(contentsOfFile:path)
let imageView = UIImageView(image: image)
// playground下预览
}

快速集成TouchID

快速集成TouchID

iPhone 5s,iOS 8,iOS7时未开放

依赖 LocalAuthentication.framework

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 判断设备支持状态
LAContext *context = [[LAContext alloc] init];
[context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil];
// 如果可以
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:@"reason" reply:^(BOOL success, NSError *error) {
if (success) {
//验证成功,主线程处理UI
}
else
{
// 重点:错误处理,分为多种错误
// 有验证失败、取消、手动输入密码等等相对应的逻辑处理
// 详见demo
}
}];

错误类型

  • LAErrorAuthenticationFailed
  • LAErrorUserCancel
  • LAErrorUserFallback
  • LAErrorSystemCancel
  • LAErrorPasscodeNotSet
  • LAErrorTouchIDNotAvailable
  • LAErrorTouchIDNotEnrolled
  • LAErrorTouchIDLockout NS_ENUM_AVAILABLE(10_11, 9_0)
  • LAErrorAppCancel NS_ENUM_AVAILABLE(10_11, 9_0)
  • LAErrorInvalidContext NS_ENUM_AVAILABLE(10_11, 9_0)

高阶函数及参数的省略

常见定义函数

1
2
3
4
5
6
let normalFunc = {
() -> Int in
return 10086
}
let normalResult = normalFunc()
// normalResult = 10086

###函数参数的『省略』

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
// 以数组排序函数为例:
var numberArr = [1,7,4,6,3,2,5]
let res1 = numberArr.sort()
let res2 = numberArr.sort {
(num1: Int, num2: Int) -> Bool in
return num1>num2
}
let res3 = numberArr.sort {
(num1: Int, num2: Int) in
return num1>num2
}
let res4 = numberArr.sort {
(num1, num2) in
return num1>num2
}
let res5 = numberArr.sort {
return $0 > $1
}
let res6 = numberArr.sort {
$0 > $1
}
let res7 = numberArr.sort(>)

变化过程
{ (num1: Int, num2: Int) -> Bool in return num1>num2 }
{ (num1: Int, num2: Int) in return num1>num2 }
{ (num1, num2) in return num1>num2 }
{ return $0 > $1 }
{ $0 > $1 }

函数式编程

案例一

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
func square(a:Float) -> Float {
return a * a
}
func cube(a:Float) -> Float {
return a * a * a
}
func averageSumOfSquares(a:Float,b:Float) -> Float {
return (square(a) + square(b)) / 2.0
}
func averageSumOfCubes(a:Float,b:Float) -> Float {
return (cube(a) + cube(b)) / 2.0
}
// 像Swift的Currying(柯里化),可以灵活调配使用
func averageOfFunction(a a:Float,b:Float,f:(Float -> Float)) -> Float {
return (f(a) + f(b)) / 2
}
let squareResult1 = averageOfFunction(a: 2, b: 4, f: square)
let squareResult2 = averageOfFunction(a: 2, b: 4, f: {
(a: Float) -> Float in
return a * a
})
//{(x: Float) -> Float in return x * x}
//{x in return x * x}
//{x in x * x}
//{$0 * $0}
let squareResult3 = averageOfFunction(a: 2, b: 4, f: {$0 * $0})

案例二

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
// 正统高阶函数样式
func sum1(value: Int) -> (Int -> Int) {
func adder(otherValue: Int) -> Int {
return otherValue + value
}
return adder
}
// 省略后
func sum2(value: Int) -> (Int -> Int) {
return { $0 + value }
}
let result1 = sum1(2)(3)
let result2 = sum2(5)(3)
// 改造函数完毕
func funcMathMethod1(first: Int -> Int, _/*起到变量匿名作用*/ second: Int -> Int) -> Int -> Int {
return { second(first($0)) }
}
let f1 = funcMathMethod1({$0 + 2}, {$0 / 3})
f1(7)
let f2 = funcMathMethod1({$0 * $0}, {$0 / 4})
f2(10)
// Tip: 使用函数式编程,要是用得不好容易造成可读性很差,那优化如下
typealias MathFunc = Int -> Int
func funcMathMethod2(f: MathFunc, _ s: MathFunc) -> MathFunc {
return { s(f($0)) }
}
let readability = funcMathMethod1({$0 + 2}, {$0 / 3})
readability(7)

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
// map
var numberArr = [1,7,4,6,3,2,5]
let mapArray = numberArr.map { (num: Int) -> String in
return "第\(num)名"
}
let mapArray2 = numberArr.map({"第\($0)名"})
mapArray2
// 可选型随意解包 str 可以为 nil
let str: String? = "1234567890"
let mapStr = str.map({"第\($0)名"})
mapStr
// filter
let filArr = numberArr.filter { (num: Int) -> Bool in
return num > 4
}
let filArr2 = numberArr.filter({ $0 > 4 })
filArr2
// reduce
let sum = numberArr.reduce(0) { (presum:Int, num:Int) -> Int in
return presum + num
}
let sum2 = numberArr.reduce(10, combine: {$0 + $1})
sum2
let sumToStr = numberArr.reduce("") { (str: String, num: Int) -> String in
str + String(num)
}
// 求一个数组中偶数的平方和(一口气使用swift提供的三个高阶函数)
//[1,2,3,4,5,6,7] -> [2,4,6] -> 4+16+36 = 56
let result = numberArr.filter({$0%2==0}).map({$0*$0}).reduce(0, combine: {$0+$1})
result // result = 56

面试题1

1
2
3
4
5
6
7
8
9
10
11
12
13
// 面试题:用数组的 reduce 方法实现 map 的功能。
let arr = [1, 3, 2]
// map简易实现
let res = arr.map({$0*2})
// Array.reduce(<#T##initial: T##T#>, combine: <#T##(T, Int) throws -> T#>)
// 输出数据类型与初始化占位数据类型(initial)一致
let res2 = arr.reduce([]) {
(a: [Int], element: Int) -> [Int] in
var t = Array(a)
t.append(element * 2)
return t
}

面试题1

1
2
3
4
5
6
7
8
9
10
11
12
13
// 面试题:用 reduce 方法一次求出数组中奇数的和、以及偶数乘积
// 使用元组,注意占位数据(0, 1),第一联合数据a :(Int, Int),函数输出数据(Int, Int) 三者类型一致
let arr2 = [1, 3, 2, 4]
let res3: (Int, Int) = arr2.reduce((0, 1)) {
(a :(Int, Int), element: Int) -> (Int, Int) in
if element % 2 == 0 {
return (a.0, a.1 * element)
} else {
return (a.0 + element, a.1)
}
}

swift 的 guard & defer

guard 使用

守卫,和隐式解析搭配有奇效

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
// 原始方法 (强解包可能会出现问题)
func tranIntToString1(x: Int?) -> String {
if x == nil || x! <= 0 {
// 不符合值的要求时,写点代码
return ""
}
// 使用x
return x!.description
}
// 改进
func tranIntToString2(x: Int?) -> String {
if let x = x where x>0 {
return x.description
}
return ""
}
// 保镖模式
// 和上面写法对比
func tranIntToString3(x: Int?) -> String {
guard let x = x where x > 0 else {
return ""
}
// 变量不符合条件判断时,执行下面代码
return x.description
}
// 非可选型
func tranIntToString4(x: Int) -> String {
guard x > 0 else {
return ""
}
return x.description
}
// 常常用于条件判断拦截
var view = UIView(frame: CGRectMake(0,0,80,80))
view.backgroundColor = UIColor.redColor()
UIView.animateWithDuration(0.3) { [weak view]() -> Void in
guard let view = view where view.alpha>0 else {return}
view.alpha = 0
}

懒加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//1.分析 NSArray 是一个闭包的返回值,而这是一个没有参数的闭包
lazy var dataArray:NSArray = { [] }()
//2.也可以写成这样
lazy var dataArray:NSArray = { return NSArray() }()
//3.从plist文件加载
lazy var dataArray:Array<XWWine> = {
let winePath = NSBundle.mainBundle().pathForResource("wine.plist", ofType: nil)!
let winesM = NSMutableArray(contentsOfFile: winePath);
var tmpArray:Array<XWWine>! = []
for tmpWineDict in winesM! {
var wine:XWWine = XWWine.wineWithDict(tmpWineDict as! NSDictionary)
tmpArray.append(wine)
}
//lazy闭包里就运行一次
return tmpArray
}()

调用的时候再在家初始化方法(懒加载)

1
2
3
4
lazy private var underlineView: UIView = {
let view = UIView(frame: .zero)
return view
}()

defer 关键字

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
/*
defer 关键字
*/
postfix func ++(inout x: Int) -> Int {
defer {
x = x/2
defer {
x += 100
}
}
return x
}
//
//postfix func ++(inout x: Int) -> Int {
// let current = x
// x += 2
// return current
//}
var num = 100
let num2 = num++
num //150
num2 //100
prefix func ++(inout x:Int) -> Int {
x += 2
return x
}
var number = 100
let number2 = ++number
number // 102
number2 // 102

嵌套枚举使用(简直就是动态Model)

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
public enum MenuScrollingMode {
case ScrollEnabled
case ScrollEnabledAndBouces
case PagingEnabled
}
public enum MenuItemWidthMode {
case Flexible
case Fixed(width: CGFloat)
}
public enum MenuDisplayMode {
case Standard(widthMode: MenuItemWidthMode, centerItem: Bool, scrollingMode: MenuScrollingMode)
case SegmentedControl
case Infinite(widthMode: MenuItemWidthMode)
}
// 初始化:用来携带信息很不错
public var menuDisplayMode = MenuDisplayMode.Standard(widthMode: PagingMenuOptions.MenuItemWidthMode.Fixed(width: 44), centerItem: false, scrollingMode: PagingMenuOptions.MenuScrollingMode.PagingEnabled)
// 实例
func labelWidth(size size: CGSize, widthMode: PagingMenuOptions.MenuItemWidthMode) -> CGFloat {
switch widthMode {
case .Flexible: return ceil(size.width)
case let .Fixed(width): return width
}
}

The Bash Shell 变量(初级)

The Bash Shell 变量(初级)

@(Share)[shell]

[TOC]

###前言
我有一个想法,希望通过学习脚本来实现blog的自动创建及发布等操作,
zm推荐shell,pj建议js

常见bash熟悉的特性:

1
2
3
4
5
6
7
8
9
10
11
12
#$:(关于本 shell 的 PID)
#钱字号本身也是个变量喔!这个咚咚代表的是『目前这个 Shell 的线程代号』,亦即是所谓的 PID (Process ID)。 更多的程序观念,我们会在第四篇的时候提及。想要知道我们的 shell 的 PID ,就可以用:『 echo $$ 』即可!出现的数字就是你的 PID 号码。
XcodeYangdeMBP2:~ xcodeyang$ echo $$
10337
#?:(关于上个运行命令的回传值)
#问号也是一个特殊的变量?在 bash 里面这个变量可重要的很! 这个变量是:『上一个运行的命令所回传的值』, 上面这句话的重点是『上一个命令』与『回传值』两个地方。当我们运行某些命令时, 这些命令都会回传一个运行后的代码。一般来说,如果成功的运行该命令, 则会回传一个 0 值,如果运行过程发生错误,就会回传『错误代码』才对!一般就是以非为 0 的数值来取代。
XcodeYangdeMBP2:~ xcodeyang$ echo$
-bash: echo$: command not found
XcodeYangdeMBP2:~ xcodeyang$ echo $?
127 # <== error
XcodeYangdeMBP2:~ xcodeyang$ echo $?
0 # <== success
  • 命令编修能力 (history):
1
2
XcodeYangdeMBP2:~ xcodeyang$ echo $HISTSIZE
500
  • 命令与文件补全功能:

    [tab] 按键的好处

    • 一下和两下的区别
    • 命令与文件路径
1
2
3
4
5
6
7
8
XcodeYangdeMBP2:~ xcodeyang$ x
xar xcodebuild xgettext5.18.pl xmllint xslt-config
xargs xcodeproj xip xpath xsltproc
xattr xcrun xjc xpath5.16 xsubpp
xattr-2.6 xed xml2-config xpath5.18 xsubpp5.16
xattr-2.7 xgettext.pl xml2man xsanctl xsubpp5.18
xcode-select xgettext5.16.pl xmlcatalog xscertadmin xxd
XcodeYangdeMBP2:~ xcodeyang$ x
  • 命令别名配置功能: (alias)
1
2
3
4
5
6
7
XcodeYangdeMBP2:~ xcodeyang$ alias xcodedaniel='ls'
XcodeYangdeMBP2:~ xcodeyang$ cd Documents/
XcodeYangdeMBP2:Documents xcodeyang$ xcodedaniel
GItHub Welcome.itmz bloodsugar 技术部分享
XcodeYangdeMBP2:Documents xcodeyang$ ls
GItHub Welcome.itmz bloodsugar 技术部分享
XcodeYangdeMBP2:Documents xcodeyang$

可变性与方便性

举例:MAIL 变量不同用户设置
yzp -> /var/spool/mail/yzp
cy -> /var/spool/mail/cy
pj -> /var/spool/mail/pj
zm -> /var/spool/mail/zm

1
2
XcodeYangdeMBP2:~ xcodeyang$ echo $USER
xcodeyang

###变量的取用与配置:echo, 变量配置守则, unset

变量的取用就如同上面的范例,利用 echo 就能够读出,只是需要在变量名称前面加上 $ , 或者是以 ${变量} 的方式来取用都可以

####变量的配置守则

  • 变量与变量内容以一个等号『=』来连结,如下所示:

    『yname=zhipingyang』

  • 等号两边不能直接接空格符,如下所示为错误:

    『myname = zhipingyang』或『myname=zhiping yang』

  • 变量名称只能是英文字母与数字,但是开头字符不能是数字,如下为错误:

    『2myname=zhipingyang』

  • 变量内容若有空格符可使用双引号『”』或单引号『’』将变量内容结合起来,但
    双引号内的特殊字符如 $ 等,可以保有原本的特性,如下所示:

    『var=”lang is $LANG”』则『echo $var』可得『lang is en_US』

  • 单引号内的特殊字符则仅为一般字符 (纯文本),如下所示:

    『var=’lang is $LANG’』则『echo $var』可得『lang is $LANG』

  • 可用跳脱字符『 \ 』将特殊符号(如 [Enter], $, \, 空格符, ‘等)变成一般字符;

    XcodeYangdeMBP2:~ xcodeyang$ myname=hello\ world

  • 在一串命令中,还需要藉由其他的命令提供的信息,可以使用反单引号『命令』或 『$(命令)』。特别注意,那个 ` 是键盘上方的数字键 1 左边那个按键,而不是单引号! 例如想要取得核心版本的配置:

    『version=$(uname -r)』再『echo $version』可得『2.6.18-128.el5』

  • 若该变量为扩增变量内容时,则可用 “$变量名称” 或 ${变量} 累加内容,如下所示:

    『PATH=”$PATH”:/home/bin』

  • 若该变量需要在其他子程序运行,则需要以 export 来使变量变成环境变量:

    『export PATH』

  • 取消变量的方法为使用 unset :『unset 变量名称』例如取消 myname 的配置:

    『unset myname』

设置myname变量

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
# 例子一
XcodeYangdeMBP2:~ xcodeyang$ echo $myname
# <==这里并没有任何数据~因为这个变量尚未被配置!是空的!
XcodeYangdeMBP2:~ xcodeyang$ myname = Daniel
-bash: myname: command not found
XcodeYangdeMBP2:~ xcodeyang$ myname=Daniel
XcodeYangdeMBP2:~ xcodeyang$ echo $myname
Daniel
XcodeYangdeMBP2:~ xcodeyang$ bash
bash-3.2$ echo $myname
# <==这里并没有任何数据~因为这个变量是自定义变量(局部变量)
bash-3.2$ exit
exit
XcodeYangdeMBP2:~ xcodeyang$ export myname #修改成环境变量(全局变量)
XcodeYangdeMBP2:~ xcodeyang$ bash
bash-3.2$ echo $myname
Daniel
bash-3.2$ unset myname
bash-3.2$ echo $myname
bash-3.2$ exit
exit
XcodeYangdeMBP2:~ xcodeyang$ echo $myname
Daniel
XcodeYangdeMBP2:~ xcodeyang$
# 例子二
XcodeYangdeMBP2:~ xcodeyang$ echo $HOME
/Users/xcodeyang
XcodeYangdeMBP2:~ xcodeyang$ HOME=$HOME/home/bin
XcodeYangdeMBP2:xcodeyang xcodeyang$ echo $HOME
/Users/xcodeyang/home/bin
# myname=$mynameyes
# 如果没有双引号,name 的内容是 $nameyes 这个变量!
# tip:配错使用control+c取消继续,下面是正确的
# myname="$myname"yes
# myname=${myname}yes <==以此例较佳!
# 猜测一下,那个是对/错
myname="myname's name"
myname='myname's name'
myname=myname\'s\ name
1
2
3
4
5
6
7
8
9
XcodeYangdeMBP2:~ xcodeyang$ echo $name
yzp
XcodeYangdeMBP2:~ xcodeyang$ name="$name is daniel"
XcodeYangdeMBP2:~ xcodeyang$ echo $name
yzp is daniel
XcodeYangdeMBP2:~ xcodeyang$ name='$name is daniel'
XcodeYangdeMBP2:~ xcodeyang$ echo $name
$name is daniel
XcodeYangdeMBP2:~ xcodeyang$

环境变量的功能: env 与常见环境变量说明, set, export

bash 可不只有环境变量喔,还有一些与 bash 操作接口有关的变量,以及用户自己定义的变量存在的. set 除了环境变量之外, 还会将其他在 bash 内的变量通通显示出来哩!信息太多

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
XcodeYangdeMBP2:~ xcodeyang$ newname=daniel
XcodeYangdeMBP2:~ xcodeyang$ name=xcodeyang
XcodeYangdeMBP2:~ xcodeyang$ export name
XcodeYangdeMBP2:~ xcodeyang$ env #<==看这里
TERM_PROGRAM=Apple_Terminal #使用{大写的字母}来配置的变量一般为系统内定需要的变量
SHELL=/bin/bash
TERM=xterm-256color
TMPDIR=/var/folders/1c/hw10tv792_92fz5cswgmzqmc0000gn/T/
Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.Zcy3n3bgYL/Render
TERM_PROGRAM_VERSION=361.1
TERM_SESSION_ID=A7EE49FA-7C48-49A4-BA91-8144FACFB146
name=xcodeyang #<==看这里
USER=xcodeyang
SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.Sey2Soe8fS/Listeners
__CF_USER_TEXT_ENCODING=0x1F5:0x0:0x0
PATH=/Users/xcodeyang/.rbenv/shims:/Users/xcodeyang/.rbenv/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/git/bin
PWD=/Users/xcodeyang
XPC_FLAGS=0x0
RBENV_SHELL=bash
XPC_SERVICE_NAME=0
SHLVL=1
HOME=/Users/xcodeyang
LOGNAME=xcodeyang
LC_CTYPE=UTF-8
_=/usr/bin/env
XcodeYangdeMBP2:~ xcodeyang$

###变量的有效范围
环境变量=全局变量
自定义变量=局部变量

为什么环境变量的数据可以被子程序所引用呢?这是因为内存配置的关系!理论上是这样的:

  • 当启动一个 shell,操作系统会分配一记忆区块给 shell 使用,此内存内之变量可让子程序取用
  • 若在父程序利用 export 功能,可以让自定义变量的内容写到上述的记忆区块当中(环境变量);
  • 当加载另一个 shell 时 (亦即启动子程序,而离开原本的父程序了),子 shell 可以将父 shell 的环境变量所在的记忆区块导入自己的环境变量区块当中。

###变量键盘读取、数组与宣告: read, declare, array

我们上面提到的变量配置功能,都是由命令列直接配置的,那么,可不可以让用户能够经由键盘输入? 什么意思呢?是否记得某些程序运行的过程当中,会等待使用者输入 “yes/no” 之类的信息啊?

####read

1
2
3
4
5
6
7
8
9
10
11
12
13
14
XcodeYangdeMBP2:~ xcodeyang$ echo $name
zhipingyang
XcodeYangdeMBP2:~ xcodeyang$ read name
%^$$uhda asuh_uha!@#~ #关键是没有之前那样的语法格式限制
XcodeYangdeMBP2:~ xcodeyang$ echo $name
%^$$uhda asuh_uha!@#~
XcodeYangdeMBP2:~ xcodeyang$
#-p :后面可以接提示字符!
#-t :后面可以接等待的『秒数!』这个比较有趣~不会一直等待使用者啦!
XcodeYangdeMBP2:~ xcodeyang$ read -p "Please keyin your age: " -t 30 agenum
Please keyin your age: 33
XcodeYangdeMBP2:~ xcodeyang$ echo $agenum
33
XcodeYangdeMBP2:~ xcodeyang$

declare / typeset

declare 或 typeset 是一样的功能,就是在『宣告变量的类型』

-a :将后面名为 variable 的变量定义成为数组 (array) 类型
-i :将后面名为 variable 的变量定义成为整数数字 (integer) 类型
-x :用法与 export 一样,就是将后面的 variable 变成环境变量;
-r :将变量配置成为 readonly 类型,该变量不可被更改内容,也不能 unset

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
#示例1:
XcodeYangdeMBP2:~ xcodeyang$ sum=10+20+30
XcodeYangdeMBP2:~ xcodeyang$ echo $sum
10+20+30 #<==默认类型是string
XcodeYangdeMBP2:~ xcodeyang$ declare -i sum=10+20+30
XcodeYangdeMBP2:~ xcodeyang$ echo $sum
60
XcodeYangdeMBP2:~ xcodeyang$ declare -i sum=1/3
XcodeYangdeMBP2:~ xcodeyang$ echo $sum
0 # <==bash 环境中的数值运算,默认最多仅能到达整数形态
XcodeYangdeMBP2:~ xcodeyang$
#示例2
XcodeYangdeMBP2:~ xcodeyang$ declare -ixr sum=20+30 #只读整型环境变量
XcodeYangdeMBP2:~ xcodeyang$ bash
bash-3.2$ echo $sum
50 #子程序里依然可以访问
bash-3.2$ exit
exit
XcodeYangdeMBP2:~ xcodeyang$ sum=30
-bash: sum: readonly variable #不可修改
XcodeYangdeMBP2:~ xcodeyang$
#示例3: 建议直接以 ${数组} 的方式来读取
XcodeYangdeMBP2:~ xcodeyang$ var[1]=$SHELL
XcodeYangdeMBP2:~ xcodeyang$ var[2]=$sum
XcodeYangdeMBP2:~ xcodeyang$ var[3]="helloworld"
XcodeYangdeMBP2:~ xcodeyang$ echo "${var[1]}, ${var[2]}, ${var[3]}"
/bin/bash, 50, helloworld
XcodeYangdeMBP2:~ xcodeyang$