Swift知识点(2)
1、as、as?、as!三种区别
as
- 从派生类转换为基类,向上转型(upcasts)
- 数值类型(CGFloat, Int, Double)转换
- switch 语句中进行模式匹配
switch person1 {
case let person1 as Student:
print("是Student类型,打印学生成绩单...")
case let person1 as Teacher:
print("是Teacher类型,打印老师工资单...")
default: break
}
as!
向下转型(Downcasting, 基类转换为派生类)时使用。由于是强制类型转换,如果转换失败会报 runtime 运行错误。
as?
as? 和 as! 操作符的转换规则完全一样。但 as? 如果转换不成功的时候便会返回一个 nil 对象。成功的话返回可选类型值。由于 as? 在转换失败的时候也不会出现错误,所以对于如果能确保100%会成功的转换则可使用 as!,否则使用 as?
2、便利构造函数
示例:
convenience init(imageName: String, bgImageName: String){
self.init()
setImage(UIImage(named:imageName), for: .normal)
}
使用convenience修饰的构造函数叫做便利构造函数
便利构造函数通常用在对系统的类进行构造函数的扩充时使用。
- 便利构造函数通常都是写在extension里面(也可用在类中)
- 便利函数init前面需要加载convenience
- 在便利构造函数中需要明确的调用self.init()
3、private(set)
private(set)将set方法的访问级别降低,低于get访问级别,set方法只有在该模块中调用,对外是只读的。
示例:
public private(set) lazy var name: String = {
return "hello";
}()
4、Swift和OC混编
- 在OC项目中创建一个swift文件的时候,Xcode 会提示 需要创建一个桥接文件,其名字为:工程名-Bridging-Header.h
- 进入TARGETS ->Build Settings -> Packaging 中设置Defines Module为YES。
- Objective-C Bridging Header中设置桥接文件,路径为:工程名/工程名-Bridging-Header.h
- OC调用Swift时,在OC文件中导入 #import “工程名-Swift.h”,就可以调用Swift的代码了。
- Swift调用OC时,需要将要调用的OC头文件导入到桥接文件中,就能调用了。
注意:最好每一步操作前comman+B编译一下,看是否报错。
5、NS_ASSUME_NONNULL_BEGIN & NS_ASSUME_NONNULL_END
Swift中的对象使用 ? 和 ! 可以分为可选和不可选,OC里面没有这个区分,导致OC和Swift混编的时候,Swift不知道一个Objective-C对象到底是optional还是non-optional,因此这种情况下编译器会隐式地将Objective-C的对象当成是non-optional。
为了解决这个问题,苹果在Xcode 6.3引入了一个Objective-C的新特性:nullability annotations。这一新特性的核心是两个新的类型注释: __nullable 和 __nonnull,如果需要每个属性或每个方法都去指定nonnull和nullable,是一件非常繁琐的事。苹果为了减轻我们的工作量,专门提供了两个宏:NS_ASSUME_NONNULL_BEGIN, NS_ASSUME_NONNULL_END。在这两个宏之间的代码,所有简单指针对象都被假定为nonnull,因此我们只需要去指定那些nullable的指针。
NS_ASSUME_NONNULL_BEGIN
@interface TestObject : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic) BOOL isOpen;
- (void)setDataAccessDate:(NSDate*)date forRequestWithIdentifier:(NSString* _Nullable)identifier;
@end
NS_ASSUME_NONNULL_END
6、Switch语句
Swift中Switch语句不需要在每个case后面加break,会自动跳出不执行后面的case,如果想按顺序执行下面的case,可以加上fallthrough语句。
7、可失败构造器
如果一个类,结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败构造器。使用 init? 的方法,当返回nil的时候表示构造失败。
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
8、try try? try! 的区别
发生异常的时候的三种处理方式:
- try:发生异常处理异常
do {
var str = try testFunc(str: "three")
} catch {
print(“error”)
}
- try?: 如果不想处理异常那么可以用这个关键字,使用这个关键字返回一个可选值类型,如果有异常出现,返回nil.如果没有异常,则返回可选值
var str = try? testFunc(str: "three")
- try!:如果不想处理异常,而且不想让异常继续传播下去,可以使用try!.这有点儿类似NSAssert().但是一旦使用try!后,在可能抛出异常的方法中抛出了异常,那么程序会立刻停止.不推荐使用。
var str = try! testFunc(str: "three")
9、逃逸闭包和非逃逸闭包
闭包只有在函数中做参数时才会区分逃逸闭包和非逃逸闭包。
Swift 3.0之后,传递闭包到函数中的时候,系统会默认为非逃逸闭包类型@noescaping,逃逸闭包在闭包前要添加@escaping关键字。
两者生命周期的区别:
非逃逸闭包的生命周期与函数相同:
- 把闭包作为参数传给函数;
- 函数中调用闭包;
- 退出函数。结束
逃逸闭包的生命周期:
- 闭包作为参数传递给函数;
- 退出函数;
- 闭包被调用,闭包生命周期结束
即逃逸闭包的生命周期长于函数,函数退出的时候,逃逸闭包的引用仍被其他对象持有,不会在函数结束时释放
逃逸闭包示例:
//ClassDetailsDAO.swift
func enrollToClass(classUUID: String, spot: String?, completion: @escaping (Result<ClassData>) -> Void) {
guard let calendarEntry = model.calendarEntry(withUUID: classUUID, createNew: false) else {
completion(.failure(ClassDetailsDAOError.entryNotFound))
return
}
}
逃逸闭包一般使用的场景:网络请求请求结束后才调用的闭包,因为发起请求后过了一段时间后这个闭包才执行,并不一定是在函数作用域内执行。
循环引用
非逃逸闭包不会产生循环引用,它会在函数作用域内释放,编译器可以保证在函数结束时闭包会释放它捕获的所有对象。
逃逸闭包会产生循环引用的问题,为了解决循环引用问题,使用[weak self]
classDetailsDAO.enrollToClass(classUUID: classUUID, spot: spot) { [weak self] (result) in
guard let strongSelf = self else {
return
}
switch result {
case .success(let classData):
strongSelf.presenter.didEnrollToClass(classData: classData)
case .failure(let error):
strongSelf.presenter.didFailToEnrollToClass(error: error)
}
}
10、Any和AnyObject
- 有点类似OC中的id,Swift为不确定的类型,提供了两种特殊别名,any和anyobject,
- AnyObject可以表示class类型的实例。
- Any可以表示任何类型的实例。
11、String(describing:)
获取类名称的字符串
class Foo {
// 实例属性中指定明确的类名来获取名称
var typeName: String {
return String(describing: Foo.self)
}
// 实例属性中动态获取类名来获取名称
var otherTypeName: String {
let thisType = type(of: self)
return String(describing: thisType)
}
// 类属性中直接获取名称
static var typeName: String {
return String(describing: self)
}
}
Foo().typeName // = "Foo"
Foo().otherTypeName // = "Foo"
Foo.typeName // = "Foo"
12、defer的使用
Swift2.0中加入了defer新语法声明,defer译为延缓、推迟之意。
使用场景:
比如,读取某目录下的文件内容并处理数据,你需要首先定位到文件目录,打开文件夹,读取文件内容以及处理数据,在读取文件内容失败、处理数据失败的情况下要关闭文件的操作,在读取成功的情况下最后也要关闭文件的操作,这个时候可以把关闭文件的代码放到defer中来延迟执行。
所以,defer{}中的代码会在作用域结束之前或者说return之前最后调用
例子:
func fridgeContains(_ food: String) -> Bool {
fridgeIsOpen = true
defer {
//最后调用
fridgeIsOpen = false
}
let result = fridgeContent.contains(food)
return result
}
13、@discardableResult的作用
@discardableResult func doSomething() -> Bool {
return true
}
例如上面的方法,如果使用 doSomething() 调用该函数,但是没有用变量来接收函数的返回参数时,就会报警告,这时候@discardableResult来消除警告。