Developing iOS 8 Apps with Swift
swfit中的数据结构
- class
- struct
- enum
Similarities
-
声明语法很相似
class Foo {}
struct Foo {}
enum op {}
-
都能拥有 属性 和 方法 ,enum本身不能存储任何值,只能将值存储在枚举的关联信息中
-
结构体和类会有初始化构造器(Initializers)
Differences
- 只有类可以继承
- 也只有类具有内省和转型的性质(Introspection and casting)
-
最重要的区别 值类型(strcut, enum) VS 引用类型(class)
结构体和枚举的传递和存储时通过拷贝过的变量,而类属于引用类型,我们传递的是这些对象的指针,而这些对象本身存储在堆内存中(堆内存中的对象,系统会通过ARC自动为我们管理)
Methods
-
重写一个父类函数需要使用 override
-
在函数前面标记 final 表示这个函数将不能被重写
-
每个函数都有internal name(函数内部使用的名字) 和 external name(函数外部使用的名字)
func foo(external internal: Int) { let local = internal }
func bar() { let result = foo(external: 123) }
-
如果不想使用external name可以使用 _ ,表示 I don't care what is external name
func foo(_ internal: Int) { let local = internal }
func bar() { let result = foo(123) }
-
_ 一般是默认加在第一个参数上的, so
func foo(internal: Int) { let local = internal }
func bar() { let result = foo(123) }
-
或者也可以强制第一个参数是必要的,通过 #
func foo(#internal: Int) { let local = internal }
func bar() { let result = foo(internal: 123) }
-
除了第一个参数,我们一般是会给其他的参数命名,不管是通过外部命名或者内部命名
func foo(first: Int, second: Double) { let local = first }
func bar() { let result = foo(123, second: 5.5) }
Properties
Property Observers
- willSet and didSet (通常用在改变UI,当Properties发生改变时,意味着UI也需要产生相应的更新)
Lazy Initialization
- 只有在被用到的时候才会产生初始化
lazy var brain = CalculatorBrain()
- 可以使用闭包进行初始化
lazy var someProperty: Type = { return <the constructed value> }()
- 可以使用函数进行初始化
lazy var myProperty = self.initializeMyProperty()
- 依旧遵循所有属性必须初始化的规则
- 只有var可以进行懒加载,let并不醒
- 这个特性一般用来处理一些错综复杂的初始化依赖
Initialization
Free init
- 一个基类(没有父类)里的所有Property都有赋值,那你就可以使用一个默认没有参数的 init 方法
- 一个结构体当且仅当没有初始化函数时,它可以得到一个将所有Property作为参数的初始化方法
What can you do inside an init?
- 给变量赋值
- 给常量赋值
- 调用其他init方法
self.init()
- 调用
super.init()
What are you required to do inside init?
- 所有Property必需赋值
- 在Swfit的类中里提供两种类型的构造器来初始化
Convenience Init
- 只能调用本类的designated initializer
- 它能通过调用其他的convenience initializer来间接调用designated initializer
- 必需先直接或者间接调用designated initializer,才能访问其他的值
- 其他初始化函数的调用,无论是convenience initializer调用designator initializer,还是designator initializer调用父类的designator initializer。
所有这些都必需在你开始访问property和方法之前完成
Designated Init
- 只能调用父类的designated initializer
- 必需执行父类的初始化函数,才能给父类的Property赋值
继承初始化(Inheriting init)
- 如果你没有在你的类中实现任何designated initializers,那么你将继承父类中的所有designated initializers,否则你将不能继承父类的任何一个designated initializers
(要么就全部继承,要么就一个都不要) - 如果你重写了父类的所有designated initializers,那你就继承了父类的所有convenient initializers
- 如果你一个初始化函数都没写,那将继承父类中全部初始化函数
Required init
- 在初始化函数前面加上required
- 它的子类必需实现这个方法
Failable init
- 有些初始化函数允许失败并且返回nil
init?(arg1: Type1, ...) { //might return nil here }
- 举个梨子
let image = UIImage(named: "foo") // image is an Optional UIImage
- 所以我们经常使用 if let
if let image = UIImage(named: "foo") { } else { }
AnyObject
- 指向一个对象的指针,也就是一个类的实例,只是你不知道它的类是什么
var destinationViewController : AnyObject
- 防止应用崩溃,使用
if let calcVC = destinationViewController as? CalculatorViewController { ... }
- 判断是否是对应类类型可以使用 is (比较少用)
if destinationViewController is CalculatorViewController { ... }
- [AnyObject] 数组
var toolbarItems: [AnyObject]
for item in toolbarItems { if let toolbarItem = item as? UIBarButtonItem { ...} }
- 有一个更酷的方法
for toolbarItem in toolbarItems as [UIBarButtonItem] { ... }
- 另一个出现AnyObject的地方
@IBAction func appendDigit(sender: AnyObject) { if let sendingButton = sender as? UIButton { let digit = sendingButton.currentTitle! } }
- One more thing
let button: AnyObject = UIButton.buttonWithType(UIButtonType.System)
let title = (button as UIButton).currentTitle
Casting
- 如果
let vc: UIViewController = CalculatorViewController()
我们不能使用vc.enter()
,因为enter是CalculatorViewController类的方法
转换if let calVC = vc as? CalculatorViewController { ... }
Function
Some Array<T> Methods
- += [T] //not += T
- first -> T //optional
- last -> T //optional
Basic
var a = [a,b,c]
append(T)
insert(T, atIndex: Int) //a.insert(d, atIndex:1), a = [a,d,b,c]
splice(Array<T>, atIndex: Int) //a.splice([d,e], atIndex:1), a = [a,d,e,b,c] 在array中插入array
rmoveAtIndex(Int) //a.removeAtIndex(1). a = [b,c]
removeRange(Range) //a.removeRange(0..<2), a= [c]
replaceRange(Range, [T]) // a.replaceRange(0...1, with: [x,y,z]) , a = [x,y,z,b]
sort(isOderedBefore: (T, T) -> Bool) // 需要提供一个比较函数,通常使用闭包 e.g. a.sort { $0 < $1 }
More Cool
filter(includeElement: (T) -> Bool) -> [T] //过滤掉不想要的元素然后返回过滤后的Array
-
map(transform: (T) -> U) ->[U] //将原来Array中的每一个元素映射到一个新的Array
let stringified: [String] = [1,2,3].map { "\($0)" } //将他们映射到字符串的形式
- reduce(initial: U, combine: (U, T) -> U) -> U
let sum: Int = [1,2,3].reduce(0) { $0 + $1 } // 将数组中的元素累加起来
String
String.Index
- 字素是显示为单字符的文本单元,在Unicode中,一个字素有可能由多个字符组成,所以不能在一个字符串中像数组一样使用索引
var s = "hello"
let index = advance(s.startIndex, 2) //得到指向l的指针
s.splice("abc", index) // s = "heabcllo"
let startIndex = advance(s.startIndex, 1)
let endIndex = advance(s.startIndex, 6)
let substring = s[index..<endIndex] //substring = "eabcl"
- rangeOfString
let num = "56.25"
if let decimalRange = num.rangeOfString(".") { let wholeNumberPart = num[num.startIndex..<decimalRange.startIndex] }
Other Sring Methods
description -> string //Printable
endIndex -> String.Index
hasPrefix(String) -> Bool
hasSuffix(String) -> Bool
toInt() ->Int? // no toDoule
为什么没有toDouble,因为当你想要转换成Double时,你需要指定一些东西,你需要多少的有效位数?你需要小数点后几位?事实上没有地方去指定这些东西。如果有一个toDouble方法,那么会有一大堆的参数来指定转换中的细节
capitalizedString -> String
lowercaseString -> String
uppercaseString -> String
join(Array) -> String //",".join(["1","2","3"]) = "1,2,3"
componentsSeparatedByString(String) -> [String] //"1,2,3".csb(",") = ["1","2","3"]
Type Conversion
let d: Double = 37.5
let f: Float = 37.5
let x = Int(d)
let xd = Double(x)
let cfg = CGFloat(d)
let a = Array("abc") // a = ["a","b","c"]
let s = String(["a","b","c"]) //s = "abc"
let s = String(52)
let s = "\(37.5)"
Assertions
Debugging Aid
assert(() -> Bool, "message")
这个函数的参数是一个"autoclosure", 所以并不需要加{}
assert(validation() != nil , "the validation function returned nil")