基本数据类型,及可选值 Optional Value
常量变量
常量的值一旦设定就不能改变,而变量的值可以随意更改。常量和变量必须在使用前声明,用 let 来声明常量,用 var 来声明变量。下面的例子展示了如何用常量和变量来记计算两个整数的和:
let num1 = 10
let num2 = 20
let num3 = 30
var sum = num1 + num2
sum = num2 + num3
你可以在一行中声明多个常量或者多个变量,用逗号隔开:
var a = 1, b = 2, c = 3
注:如果你的代码中有不需要改变的值,请使用 let 关键字将它声明为常量。只将需要改变的值声明为变量。
类型标注
当你声明常量或者变量的时候可以加上类型标注(type annotation),说明常量或者变量中要存储的值的类型。如果要添加类型标注,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。如:冒号+空格+数据类型
var name: String
如果不加类型标注,也可以给数据赋初始值,系统会根据值的类型判断其类型
var name = "李四" //系统根据"李四" 这个值 判断的出name是字符串
常用数据类型的使用
String
1、初始化
let name = "Jack"
var emptyString = ""
var anotherEmptyStr = String()
2、相关使用
//字符串的拼接
var combineString = "I" + "am" + "Siri"
combineStr += "Hello"
//字符串的长度
print(combineString.character)
//字符串的转化
let intStr = "123"
print(Int(intStr)!) //123
let doubleStr = "123.5"
print(Double(doubleStr)!) //123.5
//与NSStirng相互转化
let nsStringName:NSString = "hello"
print(String(nsStringName))
let stringName = "hi,my name is siri,who are you?"
let nsStrName = NSString(string: stringName)
//字符串索引String.index
//截取 字符串
let fromIndex = stringName.index(after: "hi".endIndex) //2
print(stringName.substring(from: fromIndex)) // ,my name is siri,who are you?
let toIndex = stringName.index(before: "who".endIndex) // 2
print(stringName.substring(to: toIndex)) // hi
let subRange = stringName.range(of: "my name is siri")
print(stringName.substring(with: subRange!)) //my name is siri
//Range 默认是 Range<Int> 而字符串用到的是Range<String.Inde>
let range = 1..<3
var myString = "abcde"
let start = myString.index(myString.startIndex, offsetBy: 1)
let end = myString.index(myString.startIndex, offsetBy: 4)
let myRange = start..<end
myString.substring(with: myRange) // "bcd"
myString = "a😀cde"
let start2 = myString.index(myString.startIndex, offsetBy: 1)
let end2 = myString.index(myString.startIndex, offsetBy: 4)
let myRange2 = start2..<end2
myString.substring(with: myRange2) // "😀cd"
//会觉得range用起来更费劲了吗? NO,自然有他出现的道理,当我们用NSRange截取带有表情符号的字符串,我们会发现错误
let nsRange:NSRange = NSMakeRange(1, 3)
let emotionStr:NSString = NSString(string: myString)
emotionStr.substring(with: nsRange) //d 哪儿去了,所有用Range就没事了
字符串索引
每一个String值都有一个关联的索引(index)类型,String.Index,它对应着字符串中的每一个Character的位置。
前面提到,不同的字符可能会占用不同数量的内存空间,所以要知道Character的确定位置,就必须从String开头遍历每一个 Unicode 标量直到结尾。因此,Swift 的字符串不能用整数(integer)做索引。
使用startIndex属性可以获取一个String的第一个Character的索引。使用endIndex属性可以获取最后一个Character的后一个位置的索引。因此,endIndex属性不能作为一个字符串的有效下标。如果String是空串,startIndex和endIndex是相等的。
通过调用 String 的 index(before:) 或 index(after:) 方法,可以立即得到前面或后面的一个索引。您还可以通过调用 index(_:offsetBy:) 方法来获取对应偏移量的索引,这种方式可以避免多次调用 index(before:) 或 index(after:) 方法。
你可以使用下标语法来访问 String 特定索引的 Character。
let greeting = "Guten Tag!"
greeting[greeting.startIndex] // G
greeting[greeting.index(before: greeting.endIndex)] // !
[greeting.index(after: greeting.startIndex)] // u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index] // a
试图获取越界索引对应的 Character,将引发一个运行时错误。
greeting[greeting.endIndex] // error
greeting.index(after: endIndex) // error
数组Array
写 Swift 数组应该遵循像Array<Element>这样的形式,其中Element是这个数组中唯一允许存在的数据类型。我们也可以使用像[Element]这样的简单语法。尽管两种形式在功能上是一样的,但是推荐较短的那种
//数组
var arr:[Int] = Array.init()
let testArr = Array.init(repeating: 4, count: 3)
var intArr = [Int]()
intArr.count
intArr.append(4)
intArr.insert(3, at: 0)
intArr.remove(at: 1)
//同类型2个数组 相加的一个新数组
let intArr2 = [5]
let intArr3 = intArr + intArr2
//数组元素为字典
var dicArr = [Dictionary<String,Any>]()
dicArr.append(["name":"siri"])
//数组元素个数
dicArr.count
//是否为空
dicArr.isEmpty
//获取元素
dicArr[0]
// 插入数据
dicArr.insert(["sex":"W"], at: 1)
dicArr.insert(["age":"4"], at: 1) // [["name":"siri"],["age":"4"],["sex":"W"]]
//替换 修改
dicArr.replaceSubrange(0..<2, with: [["name":"Lily"],["age":"20"]]) /*[["name":"Lily"],["age":"20"],["sex":"W"]] */
dicArr[0...1] = [["name":"Ella"],["age":"30"]] /*[["name":"Ella"],["age":"30"],["sex":"W"]]*/
//遍历
for dic in dicArr{
print(dic)
}
for i in 0..<dicArr.count{
print(dicArr[i])
}
//删除
dicArr.remove(at: 2) //返回被删除的Element
print(dicArr) //[["name":"Lily"],["age":"20"]]
dicArr.removeAll()
字典
字典是一种存储多个相同类型的值的容器。每个值(value)都关联唯一的键(key),键作为字典中的这个值数据的标识符。和数组中的数据项不同,字典中的数据项并没有具体顺序。我们在需要通过标识符(键)访问数据的时候使用字典,这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。
//初始化
var strDic = [String:Any]() // <==> var strD:[String:Any] = Dictionary()
strDic = ["name":"siri"]
strDic["age"] = "26"
strDic["sex"] = "W"
print(strDic) //["name":"siri","age":"26","sex":"W"]
//更新value
strDic["name"] = "wss"
print(strDic) //["name":"wss","age":"26"]
//删除
strDic.removeValue(forKey: "age")
strDic.remove(at: strDic.startIndex)
strDic.removeAll()
//与NSDictionary的区别 Dictionary的value可以是Int Double等,因为swift的String,Int,等基本数据类型都是值类型
var dic = ["age":30]
dic["height"] = 175
//遍历字典
for (key,value) in dic{
print("\(key)---\(value)")
}
for key in dic.keys{
print(key)
}
for value in dic.values{
print(value)
}
//可以根据key进行排序
let arrKey = dic.keys.sorted { (k1, k2) -> Bool in
return k1 > k2
}
print(arrKey) //["height","age"]
let arrV = dic.values.sorted { (v1, v2) -> Bool in
return v1 > v2
}
print(arrV) //[175,30]
值类型和引用类型
Swift中的类型分为两类:一,值类型(value types),每个值类型的实例都拥有各自唯一的数据,通常它们是结构体,枚举或元组;二,引用类型(reference types),引用类型的实例共享它们的数据,通常是一个类。在这篇文章中我们将会探索值类型和引用类型的价值,以及如何在它们二者间抉择。
有什么区别?
值类型最基本的特征就是复制在赋值、初始化和传递参数过程中的数据,并为这个数据创建一个独立的实例:
// 值类型例子
struct S {
var data: Int = -1
}
var a = S()
var b = a
// 把a复制给b
a.data = 42 // a被改变了, b却没有
print("\(a.data), \(b.data)") // prints "42, -1"
复制一个引用类型的时候,其实是隐式地创建了一个共享的实例。在复制后,两个实例指向了同一块数据,所以当修改其中一个实例数据的时候,另一个实例的数据也被修改了,比如:
// 引用类型的例子
class C {
var data: Int = -1
}
var x = C()
var y = x // x被复制给了y
x.data = 42 // x指向的数据被修改了 (同时y也被修改了)
print("\(x.data), \(y.data)") // prints "42, 42"
可变性在安全中的作用
选择值类型而不是引用类型的一个主要原因是能让你的代码变得更加简单。你在任何情况下用一个值类型,都能够假设你的其他代码不会使它改变,这通常在多线程环境中很有用,如果一个线程中使用的数据被另一个线程给意外的修改了,这通常会产生非常严重的Bug,且相当难以调试。
由于只有当你需要修改数据时两者的区别才会得到体现,所以当你的实例不会对数据进行修改的时候,值类型和引用类型看起来是完全相同的。
你也许会想,写一个完全不可变的类,这或许是有价值的,使用Cocoa的NSObject能简化这个过程,并且能很好地保持原有的语义。现在,你能通过使用不可变的存储属性,以及避免暴露修改数据的接口,从而在Swift里实现一个不可变的类。事实上,大多数的Cocoa类,比如NSURL等,都被设计为不可变的类,然而,Swift当前并没有提供任何语言机制去强制申明一个类不可改变(比如子类化就能修改一个类的实现),只有结构体和枚举才是强制不可变的。
如何选择?
所以如果你想要创建一个新的类型,你怎么选择?当你写Cocoa程序的时候,大多数APIs都需要从NSObject继承,你就已经是一个类了(引用类型),针对其他情况,这里有些指导规则:
使用值类型,当...:
通过使用==去比较实例的数据
你想得到一个实例的独立副本
数据在多线程环境下被修改
使用引用类型(比如使用一个类),当...:
通过使用===去判断两个实例是否恒等
你想要创建一个共享的,可变的对象
在Swift里,Array、String和Dictionary都是值类型,他们的行为和C语言中的int类似,每个实例都有自己的数据,你不需要额外做任何事情,比如做一个显式的copy,防止其他代码在你不知情的情况下修改等,更重要的是,你能安全地在线程间传递它,而不需要使用同步技术。在提高安全性的精神下,这个模型将帮助你在Swift中写出更多可预知的代码。
可选值Optional的安全性
Optional 泛型的强制安全性体现在: swift环境下,开发者定义一个可能没有值的类型,使用过程中,xcode 会一直提示是否强转的警告,告知开发者对没有值的情况的必要考虑, 所以swift 是一个强制安全性的语言, 在OC中,我们会经常碰到一些定义的类型,在实际使用过程中崩溃的情况,就是当值为nil的时候,并且的确存在为nil的情况,但是我们使用的时候没有做判空处理, 但是swift的Optional机制 实现了时刻提醒开发者做判空处理。
可选型是不能够被直接使用的(因为 swift 是类型安全的,可选型的值又可能会是 nil,如果不做处理可能导致程序 crash),如果我们想使用可选型的值,那么在这之前我们需要做的一项工作就是:解包(unwrap)!
1、强制解包
所谓的强制解包意思就是我知道这个类型是可选型,但是在我的程序执行到这里的时候我可以保证它是有值得,所以我要在这里使用它。具体表现形式就是在可选型后面加个 !,如下
let name: String ? = “张三”
print("My name is \( title ! )")
但是这样的解包是不安全,因为你不知道什么时候你的这个可选型就会变成 nil,如果我们代码非常多的话,一不小心为 nil了,可能会导致程序崩溃。这个时候我们会想到一种方法:判空 ! 如下,
var name: String? = “loveway"
if name != nil {
"My name is " + name!
} else {
print("name is nil")
}
2、 if let解包 更安全的解包方式
if let age = age, let name = name{
sentence = name + age
}else{
print("nil")
}
print(sentence!)
3、可选链式调用
let testTitle:String? = "abcdfrg"
print(testTitle?.uppercased()) //类型安全的写法,testTitle有值就调用uppercased()方法,没有就直接返回nil
//类似 person?.name?.uppercased() 就是典型的可选链式调用
4、空合运算符:http://www.jianshu.com/p/3ede1ec31351
let nilString:String? = nil
let nilName = nilString ?? "defaultValue" //如果nilString为nil,则为??后的默认值,否则就是nilString
print(nilName) //defaultValue
网友评论