1.typealias
//声明一个闭包类型 AddBlock
typealias AddBlock = (Int,Int)->(Int);
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let add:AddBlock = {
(a,b) in
return a + b;
}
let result = add(1100, 200);
print("result=(result)");
} }
2.escaping
逃逸闭包一般用于异步函数的回调,比如网络请求成功的回调和失败的回调。语法:在函数的闭包行参前加关键字“@escaping” ((Any?)->(Void))?,也可以表示网络请求成功的回调,不过后面需带“?”,这代表闭包可选类,这并不是闭包类型的,两者对比如下:
可选闭包
func requestData(urlString:String,succeed: ((Any?)->(Void))?,failure:((Any?)->(Void))?){}
逃逸闭包
func requestData(urlString:String,succeed: @escaping (Any?)->(Void),failure:@escaping (Any?)->(Void)){}
注意
如果是在闭包内部调用控制器,那么必须显示的写self。防止闭包带来的循环引用的问题
如何解决闭包的循环引用, 同样有三种方式:
使用weak修饰变量, 打破强引用, 因为使用weak修饰的变量有一次变成nil的机会
使用[weak self] 修饰闭包原理跟__weak类似, 这样在闭包中使用self, 就是弱引用
使用[unowned self ] 修饰闭包, 跟__unsafe_unretained类似, 不安全
eg:
// 解决方式一: weak
weak var weakSelf = self
loadData { (dataString) -> () in
print("(dataString) (weakSelf?.view)")
}
//解决方式二: 在swift中 有特殊的写法 ,跟OC __weak 相似 [weak self]
loadData { [weak self] (dataString) -> () in
//以后在闭包中中 使用self 都是若引用的
print("\(dataString) \(self?.view)")
}
//解决方式三: [unowned self] 跟 _unsafe_unretained 类似 不推荐使用
loadData { [unowned self] (dataString) -> () in
print("(dataString) (self.view)")
}
}
3.两个访问关键字介绍(fileprivate、open)
flieprivate
fileprivate 其实就是过去的 private。其修饰的属性或者方法只能在当前的 Swift 源文件里可以访问。即在同一个文件中,所有的 fileprivate 方法属性都是可以访问到的。
class A {
fileprivate func test(){
print("this is fileprivate func!")
}
}
class B:A {
func show(){
test()
}
}
而private现在变为了真正的私有访问控制。就是说不管在不在同一个文件中,用private修饰的方法也不可以被代码域之外的地方访问。
class A {
private func printA(){
print("A")
}
}
class B:A {
func printB(){
printA()
}
}
上面的实例是我们使用private方法定义一个父类A的成员方法,但是当继承该类的子类调用该方法时将会报错
open
过去public有两个作用:
修饰的属性或者方法可以在其他作用域被访问;
修饰的属性或者方法可以在其他作用域被继承或重载override。
但这样就会有问题,为了安全,我们可能希望某个类或属性能够被外部访问,但又不想其被继承或修改。如果将其标记成final后又会造成任何地方都不能被override。比如对lib设计者来说,他希望的结果是在module内可以被override,而被import 到外部后不能被override。 现在新添加的open起的就是原来public的作用。而现在的public表示在其他module中不可以被override和继承,而在module内可以被 override和继承
5种修饰符访问权限排序
从高到低排序如下:
open> public > interal > fileprivate > private
4.Designated,Convenience 和 Required
在 Objective-C 中,init 方法是非常不安全的:没有人能保证 init 只被调用一次,也没有人保证在初始化方法调用以后实例的各个变量都完成初始化,甚至如果在初始化里使用属性进行设置的话,还可能会造成各种问题,虽然 Apple 也明确说明了不应该在 init 中使用属性来访问,但是这并不是编译器强制的,因此还是会有很多开发者犯这样的错误。
所以 Swift 有了超级严格的初始化方法。一方面,Swift 强化了 designated 初始化方法的地位。Swift 中不加修饰的 init 方法都需要在方法中保证所有非 Optional 的实例变量被赋值初始化,而在子类中也强制 (显式或者隐式地) 调用 super 版本的 designated 初始化,所以无论如何走何种路径,被初始化的对象总是可以完成完整的初始化的。
class ClassA {
let numA: Int
init(num: Int) {
numA = num
}
}
class ClassB: ClassA {
let numB: Int
override init(num: Int) {
numB = num + 1
super.init(num: num)
}
}
在上面的示例代码中,注意在 init 里我们可以对 let 的实例常量进行赋值,这是初始化方法的重要特点。在 Swift 中 let 声明的值是常量,无法被写入赋值,这对于构建线程安全的 API 十分有用。而因为 Swift 的 init 只可能被调用一次,因此在 init 中我们可以为常量进行赋值,而不会引起任何线程安全的问题。
与 designated 初始化方法对应的是在 init 前加上 convenience 关键字的初始化方法。这类方法是 Swift 初始化方法中的 “二等公民”,只作为补充和提供使用上的方便。所有的 convenience 初始化方法都必须调用同一个类中的 designated 初始化完成设置,另外 convenience 的初始化方法是不能被子类重写或者是从子类中以 super 的方式被调用的。
class ClassA {
let numA: Int
init(num: Int) {
numA = num
}
convenience init(bigNum: Bool) {
self.init(num: bigNum ? 10000 : 1)
}
}
class ClassB: ClassA {
let numB: Int
override init(num: Int) {
numB = num + 1
super.init(num: num)
}
}
只要在子类中实现重写了父类 convenience 方法所需要的 init 方法的话,我们在子类中就也可以使用父类的 convenience 初始化方法了。比如在上面的代码中,我们在 ClassB 里实现了 init(num: Int) 的重写。这样,即使在 ClassB 中没有 bigNum 版本的 convenience init(bigNum: Bool),我们仍然还是可以用这个方法来完成子类初始化:
let anObj = ClassB(bigNum: true) anObj.numA //10000 anObj.numB //10001
因此进行一下总结,可以看到初始化方法永远遵循以下两个原则:
初始化路径必须保证对象完全初始化,这可以通过调用本类型的 designated 初始化方法来得到保证;
子类的 designated 初始化方法必须调用父类的 designated 方法,以保证父类也完成初始化。
对于某些我们希望子类中一定实现的 designated 初始化方法,我们可以通过添加 required 关键字进行限制,强制子类对这个方法重写实现。这样做的最大的好处是可以保证依赖于某个 designated 初始化方法的 convenience 一直可以被使用。一个现成的例子就是上面的 init(bigNum: Bool):如果我们希望这个初始化方法对于子类一定可用,那么应当将 init(num: Int) 声明为必须,这样我们在子类中调用 init(bigNum: Bool) 时就始终能够找到一条完全初始化的路径了:
class ClassA {
let numA: Int
required init(num:Int) {
numA = num
}
convenience init(bigNum: Bool) {
self.init(num: bigNum ? 10000 : 1)
}
}
class ClassB:ClassA {
let numB: Int
required init(num: Int) {
numB = num + 1
super.init(num: num)
}
}
另外需要说明的是,其实不仅仅是对 designated 初始化方法,对于 convenience 的初始化方法,我们也可以加上 required 以确保子类对其进行实现。这在要求子类不直接使用父类中的 convenience 初始化方法时会非常有帮助。
4.final
可以通过把方法,属性或下标标记为final来防止它们被重写,只需要在声明关键字前加上final修饰符即可(例如:final var,final func,final class func,以及final subscript)。如果你重写了final方法,属性或下标,在编译时会报错。
5.static和class的区别
在方法的func关键字之前加上关键字static或者class都可以用于指定类方法. 不同的是用class关键字指定的类方法可以被子类重写, 如下:
override class func work() {
print("Teacher: University Teacher")
}
但是用static关键字指定的类方法是不能被子类重写的, 根据报错信息: Class method overrides a 'final' class method. 我们可以知道被static指定的类方法包含final关键字的特性--防止被重写.
这是我自己总结的,如有不对,望指出