13.函数.
1.定义函数和外部参数:格式: func 函数名(形参列表)-> 返回类型 {//代码实现} 调用函数格式: 第一个参数忽略,第二个参数有,例子:
override func viewDidLoad() {
super.viewDidLoad()
sum(3, y: 10);
super.viewDidLoad()
sum(3, y: 10);
}
// 有参数有返回值的函数,
func sum(x: Int, y: Int) ->Int {
return x + y
}
外部参数:格式: func 函数名( 外部参数 形参1: 类型1, … .)-> 返回类型 {//代码实现}
形参:是供函数内部使用 的
好处:是供外界访问调用的,可以让函数的语义更加清楚,不会影响到内部。
例子: 调用: sum1(num: 2, num1: 2)
func sum1(num x: Int, num1 y: Int) ->Int {
return x + y
}
注意: 如果使用_作为外部参数,会忽略参数名的提示:
例子: print(sum2(2,9))
func sum2(x: Int, _ y: Int) ->Int {
return x + y
}
2.函数使用默认值:定义函数的时候 = 值 来指定,所有的指定了默认值之后的函数,可以任意组合参数,指定了那个参数就使用那个参数,否则使用默认值参与计算。
例子: print(sum3(3, y: 3, 3)) 或者 print(sum3(z: Int))
func sum3(x: Int = 2, y: Int = 4, _ z: Int ) ->Int {
return x + y + z
return x + y + z
}
3.函数的返回类型:
1.无返回类型——定义闭包的时候使用
3种写法:A:省略 B:() C:Void
例子:
func dme() {
print("eh")
}
func dem1() ->Void {
print("注意Void的V大写")
}
func dem2() -> () {
print("hehh")
}
函数在Swift中叫函数?为什么:函数名是指向函数的内存首地址,一调用函数就执行了
OC中叫方法? OC是运行时,以消息的方式,发送消息!方法!
14.闭包:和OC中的Block很像
1.回顾OCblock,面试必问的,
1. C 语言的,匿名函数
2. 提前准备好的代码,在需要的时候执行
2. 提前准备好的代码,在需要的时候执行
3. 可以当作 参数 / `返回值(函数式)` 传递
2.闭包:
let myBlock = {
} 无参数无返回值 —最简单的闭包
let myBlock = { (x:int , y:int)-> int in {
格式 { (形式参数列表: 如果有参数和返回值: (Int, Int) -> Int )->返回类型 in // 代码实现 }
} ——有参数有返回值的闭包
{} 是用来包装代码,闭包中的所有内容需要包含在 {} 中
in 是用来区分函数的定义和代码的实现
}
let 闭包名 = 函数 (block指向函数体) 如果指向的函数,没有提供外部参数,在使用的时候,没有智能提示
例子:
let myBlock1 = sum
print(block(3, y: 9))
3.闭包在实际开发中的应用。
block 的应用场景是什么? 网络回调。因为网络是异步的,代码添加到队列,队列调度完成—回调,无法通过返回值通知调用方,回调的数值,通过block的参数进行传递的
例子: 参数 (text: String) -> (),接收 字符串,没有返回值的闭包 ,闭包当做参数传递 , 闭包是提前准备好的代码,需要的时候调用
func closureDemo(callblock: (text: String) ->()) {
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
let str = "html"
dispatch_async(dispatch_get_main_queue(), { () -> Void in
// 通过回调通知调用方,把异步请求结束的结果,通过参数,传递给闭包
callblock(text: str)
})
}
}
调用: closureDemo { (text) -> () in
print(text) 答案:html
}
4.小细节:尾随闭包:(主要特点:没有圆括号)闭包是最后一个参数,函数的“)”可以前置到倒数第二个参数的末尾,后面是参数直接使用 {//执行代码}。尾随闭包不需要外部参数,用处:通常一个函数的末尾,如果添加了一个闭包参数,使用尾随闭包就方便了,
5.SWift中一般不需要使用self,但是闭包中必须要使用。为什么?
1.正常的代码,当前上下文隶属于同一个对象,直接访问属性,是没有问题的,
2.闭包是提前准备好的代码,在执行的时候,需要告诉函数访问那一个对象的属性,因此,闭包中必须使用self
3.Swift中设置控件的frame:选用可选解包。解包:有两种: !强行解包 ? 可选解包:如果有值,返回可选值,如果没有值,给 nil 发送后续的消息 setFrame ——>什么都不做!
4.闭包的补充:如果没有参数和返回值,in 前面的都可以省略
15.函数之循环引用:面试题
在开发中,判断是否循环引用,加一个nav,
OC开发中,什么时候遇到过循环引用, 1.特点: 代码中有self.的时候
- (用__weak 解除强引用)最常用,也是最容易记忆的,或者把tools 用weak修饰,__weak typeof(self) weakSelf = self, 只有把block里面的self替换成weakSelf,就可以打断循环引用链。
- 案例小结:vc 强引用 tools ,tools 强引用 finished block,block 强引用了vc(self.)解决循环引用: __weak (ARC专有的)是弱引用,如果指向的对象被释放,地址变为 nil。
- __unsafe_unretained 是 `assign`,如果指向的对象被释放,地址保持不变! 格式: __unsafe_unretained typeof(self)weakSelf = self;
- EXC_BAD_ACCESS MRC 开发最常见的错误,`野`指针(对象已经被释放,仍然访问该地址)
Swift中的循环引用
- 析构函数:deinit
- deinit与dealloc是类似的 ,在对象被释放前,自动调用一次,如果是继承关系,先调用子类的deinit(先释放子类属性的空间),在调用父类的deinit(再释放父类属性的空间)
- 析构函数特点:1.没有 func -> 不允许直接被调用。2.没有()->不允许有参数,不允许被重载。3.可以在方法做销毁的工作。4.开发的时候,哪些需要销毁?通知 : 如果不销毁,不会崩溃,但是会造成内存泄露KVO: Key-Value-Observing (监听者模式,网络---大文件下载断点续传的时候用到) —观察者。如果不销毁,会崩溃Timer/CADisplayLink : 一定要销毁,否则会一直注册在运行循环,会造成循环引用。
- 定义闭包的属性: var fininsedCallBack: ((text: String)-> ())? —带参数无返回值的闭包
- ()-> () ---无参数无返回值的闭包
3种解除循环引用的方法:
方法1: OC 的方法
'weak' must be a mutable variable, because it may change at runtime -----有可能报错的问题
weak 的变量在运行时有可能被设置为 nil,weak不能使用 let
weak var weakSelf = self
loadData { (result) -> () in
print(result)
print(weakSelf)
}
方法2: Swift 的方法 - 首选
[weak self] 表示 闭包中的 self 都是弱引用的,不需要再使用其他的变量
weak 的变量在运行时有可能被设置为 nil,weak不能使用 let
weak var weakSelf = self
loadData { (result) -> () in
print(result)
print(weakSelf)
}
方法2: Swift 的方法 - 首选
[weak self] 表示 闭包中的 self 都是弱引用的,不需要再使用其他的变量
weak 的变量在运行时有可能被设置为 nil,所以闭包中的 self,都是可选的
loadData { [weak self] (result) -> () in
unexpectedly found nil while unwrapping an Optional value
! 强行解包,值不存在抛出异常
? 可选解包,值不存在,给nil发送消息
print(self?.view)
unexpectedly found nil while unwrapping an Optional value
! 强行解包,值不存在抛出异常
? 可选解包,值不存在,给nil发送消息
print(self?.view)
}
方法3: [unowned self]
[unowned self] 和 OC 中的 assign 是一样的,对象释放之后,指针保持不变
1> 闭包中不需要考虑解包的问题
2> 但是如果 self 释放,会出现野指针访问
loadData { [unowned self] (result) -> () in
[unowned self] 和 OC 中的 assign 是一样的,对象释放之后,指针保持不变
1> 闭包中不需要考虑解包的问题
2> 但是如果 self 释放,会出现野指针访问
loadData { [unowned self] (result) -> () in
// EXC_BREAKPOINT-----有可能报错的问题
野指针访问
print(self.view)
}
}
16.构造函数:分配空间(就是给属性分配空间) alloc,初始化数值 init、initWithxxx, Swift 只有 init
重载和重写的作用都是给本类的属性分配空间和初始化数值
override关键字: 重写,覆盖父类的方法
注意:
1.init
属性的初始化应该在 super.init() 之前被初始化!———>如果 属性是必选的,必须在构造函数中设置初始值,初始化完成后才调用父类的 super.init()
2.Swift 默认所有的类都是全局共享的 ,不需要 import
3.重载: 就是函数名相同,参数类型/个数不同。分类不存在的方法,本类扩展定义。
1.定义对象的属性的时候,一定用var
2.函数重载,在OC中 就是initWithXXX。在swift中就是init(XXX:),
3.init函数增加参数 ,里面的属性必须用self.修饰 。
4. 如果没有提供任何的构造函数,系统会默认提供一个 () 的构造函数
如果重写了构造函数,系统提供的 () 构造函数就不存在了
构造函数的目的:给本类的属性分配空间/初始化
5.注意:
如果自己重写了构造函数,意味着要给自己的属性初始化,() 默认的构造函数,因为没有参数,如果直接调用父类的构造的构造函数父类方法不能给本类属性初始化
4.kvc ---放在构造函数之后,必须保证对象先创建完成后再向对象发送消息。
1.在 Swift 中使用 KVC 的代码时,需要保证属性被创建出来,如果属性不存在,运行会崩溃!抛出无法找到 key 的异常!
2.在KVC中,A:如果是基本数据类型,不能是可选项,必须设置初始值。例如: var age: Int = 0, Int是一个结构体,在 OC 中没有对应的数据类型。 B:必选的参数,意味着,对象一但创建,就需要在 init 函数中分配空间。例子:
var name: String?
overrideinit() {
super.init()
}
c: 调用 setValuesForKeysWithDictionary 之前需要调用 super.init,因为 在调用 super.init 方法之前,父类的属性不能保证完全分配完成,对象没有创建完成!,所以在调用KVC方法之前必须先调用super.init
例子: init(dict: [String: AnyObject]) {
super.init()
// 对象在此已经创建完成,可以向对象发送消息!
setValuesForKeysWithDictionary(dict)
// 对象在此已经创建完成,可以向对象发送消息!
setValuesForKeysWithDictionary(dict)
}