变量就是类似于鞋盒之类的概念,装变量的盒子。所以需要有指定的型号。
变量作用域和生命周期
-
变量,在定义的地方赋予了这个变量的作用域和生命周期。
-
swift中的变量可以在任何地方定义。按生命周期分为几类:
- 全局变量。在swift文件的最上一级定义。生命周期和程序生命周期一致。可以被文件内的所有的代码看到。可以被文件外同一模块的代码看到。
- 属性。 属性是定义在对象类型顶层的变量。有实例属性和类属性之分。
* 实例属性:每个实例的值不同。生命周期和实例相同。
* 类属性:前面加static/class。生命周期和对象类型的生命周期相同。
属性在内对象声明的代码内可见。类属性在类型可见的地方可见。
-
本地变量。本地变量就是定义在函数体中的变量。在大括号内有效。
变量申明
- 变量声明用let或var。let声明常量,var声明变量。
- 变量声明之后,类型是不可以改变的。可以隐式给定类型也可以显示给定类型。隐式给定就是用初始化值的类型给定类型。可以同时指定类型又赋初值。这个有时候是多余的,有时候不是:
- 有时候swift类型推导会出错。比如用语法量数字的时候,swift默认推导为Int或Double类型,但是我如果想要别的类型的话,就需要制定。
- 有时候swift推导不出类型来。
- 程序员推导不出类型来。所以有的时候干脆写上,让自己心里清楚。
- 因为类型清楚,所以变量可以不初始化。只是声明。但最好别这么干,尤其是本地变量。引用本地没有初始化的变量,编译器会报错。一般在需要条件初始化的时候使用。
- 变量在作为参数传入函数之前,需要被定义和初始化。即使初始化的值是假的。
- 有时候,调用一个函数返回一个值。然后在这个函数参数中传入一个函数作为参数,这个作为参数的函数中又用到这个返回值。此时需要先定义这个返回值然后赋值个假的。如果不定义,参数函数中将不能使用。如果不赋初值,也是不能使用的。
- 对象的属性可以在对象初始化函数中进行赋初值,而不是在声明的时候赋初值。一般都会给出明确的类型。
计算初始化
- 有时候需要做一些运算才给变量赋初值。这时候最好的办法就是使用匿名函数。然后立刻调用。属性初始化也可以这么干。给属性初始化也就只有这种方式了,毕竟此时实例还没有初始化。
计算变量
- 变量可以被计算的。也就是说,变量不一定只是一个值,也可以是一个函数。有seter/getter函数。此时变量只能定义为let型。类型也必须明确定义。
- 使用的时候,和普通变量没有什么区别,只是调用了get、set方法。
- set方法的默认参数是newValue。也可以设置成别的。
- 如果没有set方法,那么这个变量就不可更改。get是必须要要有的。如果没有set,那么get可以直接跟在后面。
- 计算变量有很多应用场景:
- 设置只读变量
- 装饰函数
- 装饰其他变量
- 计算变量可以引用其他属性。也可以调用实例方法。因为这个方法直到实例存在的时候才会被调用。
Setter观察者
- 计算变量可以注入函数监控设置值。willSet,didSet。
- didSet函数可以修改存储变量的值。但是不会调用观察函数,否则就死循环了。
- 实际编程过程中,经常使用存储变量的观察函数,而不是计算变量。
- 计算变量没有setter观察者,因为没有必要。
懒初始化
- 懒初始化的懒不是道德审判。是一种重要的行为。存储变量带着初始值声明,如果采用懒加载的方式,那么直到用到的时候,才会真正计算和赋值变量。
- 全局变量。是自动懒初始化的。在底层是等同于dispatch_once的。因此是线程安全的的单列。
- 静态变量。也是自动懒初始化的。和全局变量一样。
- 实例属性。默认不是懒初始化。如果需要懒初始化,就要用lazy标示。并且必须声明为var类型,不能是let类型。
- swift3中dispatch_once已经没有了。全局和静态的有这个特性。懒初始化常常用来实现单例模式。
class MyClass {
static let = sharedSingleton = MyClass()
}
- 为什么会需要懒加载呢?大部分情况下是因为初始化的值代价很大。还有一种情况是,懒加载可以引用实例的内容。
- 常常用定义调用函数来初始化懒初始化变量。
lazy var prog : UIprogressView = {
let p = UIProgressView(progressViewStyle: .default)
p.xxx = xxx
......
return p
}()
- 语言也有一些问题。懒属性不能有setter 观察。let变量不能懒加载。不是原子操作,有多线程问题。
内置简单类型
Bool
- Bool 对象类型只有两个值,true,false。
- Cocoa中经常有函数返回Bool类型值,表明这个函数执行情况。
- swift中,没有隐含别的东西表明是true或false,不如C语言中的0或1。
- Bool类型的操作。(!,&&,||)与或非。
Numbers
- 大部分情况下,Int和Double就够了。其他类型是为了兼容OC。
- Int 整形。多大和机器字相关。
- Int 可以使用各种进制。0b,0o,0x等。
- Double型。15位小数。默认的语法小数是Double类型。可以用科学计数法标示Double类型。
- 数字转换。直接函数强转即可。
- 数字作为参数传入函数时,仅仅会对字面量的数字做隐式转换,确定类型的变化不会做转换。需要自己明确转换。
- 其他数字类型。主要是和Cocoa兼容的操作。很多时候需要强制类型。
- 有时候你要赋值的变量类型是什么你也不知道,需要用numbericast()来动态转换。
- 算术运算。+,-,*,/
- swift3之后,数字带了各种方便的函数。
- 数字的比较。
String
- swift的字符串是非常现代的,内部是Unicode。
- \u{...}可以拿Unicode码转换成字符串。还有其他转义。 \n 换行 \t tab \" 普通引号 \\普通反斜杠
- swift字符串最常用的功能是字符串插入。(某对象) 可以输出字符串。当然括号里面的也可以是表达式。
- 字符串重载了加号。加号是拼接字符串的意思。也可以字符串加数字。都是拼接字符串。重载了 +=,相当于append。
- 数组可以用joined加上分隔符的方法输出一个带分隔符的字符串。
- 比较方法 == 也重载了。两个字符串相等。
- 还有一些比较实用的函数。 isEmpty,hasPrefix(:),hasSuffix(:),uppercase,lowercase。
- 字符串和Int类型之间的转换是可以的。数字转字符串,直接调用函数,还可以按进制转。
- 如果是表示数字的字符串,也可以转换为数字。
- 任何对象都可以转换成字符串,需要遵循三个协议:Streamable, CustomStringConvertible, CustomDebugStringConvertible。
- 字符串的长度,是字符的长度,不是空间的长度。
- 你可以一个个遍历字符串中的字符,还可以按编码格式要求。
- swift语言本身没有类似大小写之类的函数,这些功能都在Foundation中,一般情况下都可以使用。
- swift中的Stirng和NSString的混用。有时候需要强制转换。
Character
- character代表着一个单独的unicode字符。
- 字符类型没有语法字符。需要用类型初始化。
Range
- range类型标示一组范围。有两种语法形式。前后有空格也是合法的。
- a...b : 从a到b。包括b。
- a..<b:从a到b。不包括b。
- 大部分情况下是Int类型。如果后面那个是负数,那么必须用括号或者空格分开。
- 没有颠倒的range。起始必须比终点小。如果起点比终点大,编译器不管,但是运行时会挂掉。可以调用reversed方法来逆序。
- range的端点可以是double类型的。
- 也有办法检测是否两个range重叠,包含等。
- range另一个应用是索引序列。
let s = "hello"
let arr = Array(s.characters)
let result = arr[1...3]
let s2 = String(result) //"ell"
- swift的string可以配合range做各种操作
- swift的range类型和NSRange是完全不同的结构。但是可以相互转换。
Tuple
- 元组是轻量级的可变有序集合。
var pair : (Int, String)
- 类型可以推导的话,就可以不用明确给出了。
- 元组是swift特有的语法,OC中没有,所以要在OC看不到的地方使用。
- 元组可以用作函数返回多个值。
- 元组可以直接引用其中的元素,可以用序号来引用。
- 元组也可以带标签,用标签来标示。带标签的不带标签的可以相互赋值。
- 可以用typealias来给元组命名,方便使用。
- Void返回值,实际上是返回一个空的元组()
Optional
-
Optional类型是个枚举。包了个其他类型。之所以是option,是说他可能是某种类型的值,也可能不是。
-
Optional初始化后成为某一类型之后,就不能变了。只能是包这一类型的东西。
var stringMaybe = Optional("abcd") stringMaybe = Optional(123) // compile error
-
Optional类型对swift非常重要,以至于嵌入进语言内部了。
-
Optional通常都不是初始化的方式使用它,而是赋值的方式使用。
var stringMaybe = Potional("howdy")
stringMaybe = "farewell"//看似类型不匹配,但这是可以的的,合法的。
- 语法糖。类型后面加个问号,表示是optional类型的。
- 函数参数需要传入一个optional类型的。可以直接传入optional类型包含的类型。因为传参数就类似赋值。在函数内部,还是一个optional类型。
- 反过来则不行。如果你想用optional中的具体类型,你就需要解包。
- 可以强制解包。在optional类型后面加个感叹号。
- 隐式解包optional。隐式解包的optional类型依然是optional类型。只是让编译器帮你更方便。就是告诉编译器,如果optional用到包含类型的地方,帮忙解一下。
String? 普通的optional类型
String! 隐含解包的optional类型
- 隐式解包的optional类型不是一个独立的类型,仅仅是optional类型做了一些特殊的标记。
- nil关键词。一个optional类型不包含任何类型,怎么办?可以和nil做比较,然后判定。
- optional是唯一可以隐式初始化为nil的类型。其他的类型,没有初始化是不能使用的。编译报错。
- 不能解包一个为nil的optional。否则会crash。
网友评论