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) -> 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来消除警告。

你可能感兴趣的:(Swift知识点(2))