读完Swift开发文档后记录的小知识点

最新更新

1、在 Swift 4.2 里可以使用 CaseIterable 协议,让编译器自动为我们创建 allCases:

//这个allCases的自动合成仅替换没有参数的case值, 但是如果需要你需要所有case值, 可以重写allCases属性自己添加
//如果有枚举项标记为unavailable,则默认无法合成allCases,只能依靠自己来手动合成
enum LogLevel: CaseIterable {
    case warn
    case info
}

for level in LogLevel.allCases {
    print(level)
}

2、虽然 Swift 4.2 里最重要的是 ABI 兼容性的提升,但还是实现了一套随机数的 API:

let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomFloat = Flow.random(in: 0 ..< 1)

let greetings = ["hey", "hi", "hello", "hola"]
print(greetings.randomElement()!)

let randomlyOrderGreetings = greetings.shuffled()
print(randomlyOrderedGreetings)

var numbers = [1, 2, 3, 4, 5, 6]
let shuffledNumbers = numbers.shuffled()
// shuffledNumbers == [6, 1, 3, 2, 5, 4]
// numbers == [1, 2, 3, 4, 5, 6]
numbers.shuffle()
// numbers == [4, 2, 6, 5, 1, 3]

3、Swift 4.2 新增了一个判断库是否能导入的宏:

#if canImport(UIKit)
    import UIKit
    typealias Color = UIColor
#elseif canImport(AppKit)
    import AppKit
    typealias Color = NSColor
#else
    #error("Unsupported platform")
#endif
// Swift 4.2
//判断是否为模拟器环境的代码:
#if hasTargetEnviroment(simulator)
    ...
#endif

4、Bool类型取反:.toggle()
5、检查序列元素是否符合条件:

//判断数组的所有元素是否全部大于85
let scores = [86, 88, 95, 92]
//返回一个BOOL
let passed = scores.allSatisfy({ $0 > 85 })
print(passed)
//输出: true

6、自定义运算符
前置运算符 prefix
中间运算符 infix
后置运算符 postfix

以前整理

1、where关键字指定额外的要求,可以用来设置约束条件、限制类型
2、在方法的func关键字之前加上关键字static或者class都可以用于指定类方法.不同的是用class关键字指定的类方法可以被子类重写,但是用static关键字指定的类方法是不能被子类重写的,被static指定的类方法包含final(不可更改的)关键字的特性--防止被重写.
3、deinit属于析构函数
析构函数(destructor) 与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数
和OC中的dealloc 一样的
4、Swift中mutating关键字:为了能够在实例方法中修改属性值,可以在方法定义前添加关键字mutating;值类型的实例方法中,也可以直接修改self属性值。
5、代理继承的父类NSObjectProtocol的时候,判断代理是否实现某个方法responds(to: #selector()能提示处理,继承自class则没法判断
6、let content = (newSrting as NSString).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
7、打印内存地址:
withUnsafePointer(to: &subArr) { p in
print(p)
}
8、数组中获取子数组
let arr = [1,2,3,4,5]
//是个切片数组和数组之间可以转化
let arrSlice = arr[0..<3]
let subArr = Array(arrSlice)

9、多继承,运行时方法
10、Swift 同时也提供两个等价运算符( === 和 !== ),你可以使用它们来判断两个对象的引用是否相同。
11、Swift 标准库包含的元组比较运算符仅支持小于七个元素的元组。要比较拥有七个或者更多元素的元组,你必须自己实现比较运算符。
12、Swift 的 String类型桥接到了基础库中的 NSString类。Foundation 同时也扩展了所有 NSString 定义的方法给 String 。也就是说,如果你导入 Foundation ,就可以在 String 中访问所有的 NSString 方法,无需转换格式。
13、子字符串并不适合长期保存——因为它们重用了原字符串的内存,只要这个字符串有子字符串在使用中,那么这个字符串就必须一直保存在内存里。
14、hashValue 的实质
15、 • 使用 intersection(_:)方法来创建一个只包含两个合集共有值的新合集;

  • 使用 symmetricDifference(_:)方法来创建一个只包含两个合集各自有的非共有值的新合集;
  • 使用 union(_:)方法来创建一个包含两个合集所有值的新合集;
  • 使用 subtracting(_:)方法来创建一个两个合集当中不包含某个合集值的新合集。

16、使用 stride(from:to:by:) 函数来跳过不想要的标记。stride(from:through:by:)(闭区间)
17、continue 语句告诉循环停止正在做的事情并且再次从头开始循环的下一次遍历。它是说“我不再继续当前的循环遍历了”而不是离开整个的循环。
18、如果你确实需要 C 风格的贯穿行为,你可以选择在每个情况末尾使用 fallthrough 关键字
19、相对于使用 if 语句来做同样的事情,为需求使用 guard 语句来提升你代码的稳定性。它会让正常地写代码而不用把它们包裹进else 代码块,并且它允许你保留在需求之后处理危险的需求。
20、输入输出形式参数:在形式参数定义开始的时候在前边添加一个 inout关键字,函数能够修改一个形式参数的值,而且这些改变在函数结束之后依然生效,输入输出形式参数是函数能影响到函数范围外的另一种替代方式。
21、结构体和枚举是值类型,类是引用类型
22、 public private(set) var area: Double = 0 公开属性 私有set
23、结构体是值类型。当一个值类型的实例被标记为常量时,该实例的其他属性也均为常量。
24、你必须把延迟存储属性声明为变量(使用 var 关键字),因为它的初始值可能在实例初始化完成之前无法取得。常量属性则必须在初始化完成之前有值,因此不能声明为延迟。
25、如果被标记为 lazy 修饰符的属性同时被多个线程访问并且属性还没有被初始化,则无法保证属性只初始化一次。
26、你必须用 var 关键字定义计算属性——包括只读计算属性——为变量属性,因为它们的值不是固定的。 let 关键字只用于常量属性,用于明确那些值一旦作为实例初始化就不能更改。
27、结构体和枚举是值类型。默认情况下,值类型属性不能被自身的实例方法修改。(可以选择在 func关键字前放一个 mutating关键字来改变属性值)
28、swift正常的方法如果有返回值的话,调用的时候必须有一个接收方,否则的话编译器会报一个警告,如果在方法前加上 @discardableResult 不处理的时候就不会有警告了。或者 : _ =
29、你不能为同一个属性同时提供重写的setter和重写的属性观察器
30、你可以通过在类定义中在 class 关键字前面写 final 修饰符( final class )标记一整个类为终点。任何想要从终点类创建子类的行为都会被报告一个编译时错误。
31、你可以通过标记为终点来阻止一个方法、属性或者下标脚本被重写。通过在方法、属性或者下标脚本的关键字前写 final 修饰符(比如 final var , final func , final class func , final subscript )
32、convenience:对现有的类增加init方法、通过extension给原有的People类增加init方法(无法重写)。 • 子类对象调用父类的convenience的init方法:只要在子类中实现重写了父类convenience方法所需要的init方法的话,我们在子类中就可以使用父类的convenience初始化方法了
33、如果你想要你自己的自定义值类型能够使用默认初始化器和成员初始化器初始化,以及你的自定义初始化器来初始化,把你的自定义初始化器写在扩展里而不是作为值类型原始实的一部分。
34、指定初始化器必须保证在向上委托给父类初始化器之前,其所在类引入的所有属性都要初始化完成。
35、指定初始化器必须先向上委托父类初始化器,然后才能为继承的属性设置新值。如果不这样做,指定初始化器赋予的新值将被父类中的初始化器所覆盖。
36、便捷初始化器必须先委托同类中的其它初始化器,然后再为任意属性赋新值(包括同类里定义的属性)。如果没这么做,便捷构初始化器赋予的新值将被自己类中其它指定初始化器所覆盖。
37、在类的初始化器前添加 required 修饰符来表明所有该类的子类都必须实现该初始化器:
38、使用 defer语句来在代码离开当前代码块前执行语句
39、Any类型表示了任意类型的值,包括可选类型。
40、扩展可以向一个类型添加新的方法,但是不能重写已有的方法。
41、扩展可以添加新的计算属性,但是不能添加存储属性,也不能向已有的属性添加属性观察者。

42、如果你使用扩展提供了一个新的初始化器,你仍应确保每一个实例都在初始化完成时完全初始化。

43、结构体和枚举是值类型。默认情况下,值类型属性不能被自身的实例方法修改。你可以选择在 func关键字前放一个 mutating关键字来改变(如果你在协议中标记实例方法需求为 mutating ,在为类实现该方法的时候不需要写 mutating 关键字。 mutating 关键字只在结构体和枚举类型中需要书写。)

44、你可以通过实现指定初始化器或便捷初始化器来使遵循该协议的类满足协议的初始化器要求。在这两种情况下,你都必须使用required 关键字修饰初始化器的实现,required 修饰的使用保证了你为协议初始化器要求提供了一个明确的继承实现。(在类的初始化器前添加 required 修饰符来表明所有该类的子类都必须实现该初始化器,在重写父类的必要初始化器时,不需要添加 override 修饰符)

45、由于 final 的类不会有子类,如果协议初始化器实现的类使用了 final 标记,你就不需要使用 required 来修饰了。因为这样的类不能被继承子类

46、如果一个子类重写了父类指定的初始化器,并且遵循协议实现了初始化器要求,那么就要为这个初始化器的实现添加required 和 override 两个修饰符

47、协议作为参数的话,饮用的实例必须采纳 RandomNumberGenerator 协议

48、注意类型不会因为满足需要就自动采纳协议。它们必须显式地声明采纳了哪个协议。(满足协议,也必须显示的声明一下extension Hamster: TextRepresentable {})

49、通过添加 AnyObject 关键字到协议的继承列表,你就可以限制协议只能被类类型采纳(并且不是结构体或者枚举)。

50、协议组合使用 SomeProtocol & AnotherProtocol 的形式。你可以列举任意数量的协议,用和符号连接( & ),使用逗号分隔。除了协议列表,协议组合也能包含类类型,这允许你标明一个需要的父类。(celebrator 形式参数的类型是 Named & Aged ,这意味着“任何同时遵循 Named 和 Aged 的协议。”它不关心具体是什么样的类型传入函数,只要它遵循这两个要求的协议即可。)

51、注意 @objc 协议只能被继承自 Objective-C 类或其他 @objc 类采纳。它们不能被结构体或者枚举采纳。

52、通过给协议创建扩展,所有的遵循类型自动获得这个方法的实现而不需要任何额外的修改。

53、你可以使用协议扩展来给协议的任意方法或者计算属性要求提供默认实现。通过扩展给协议要求提供默认实现与可选协议要求的区别是明确的。尽管遵循协议都不需要提供它们自己的实现。有默认实现的要求不需要使用可选链就能调用。

54、如果遵循类型满足了为相同方法或者属性提供实现的多限制扩展的要求,Swift 会使用最匹配限制的实现。

55、你可以在一个泛型** where 分句中包含多个要求来作为扩展的一部分,就如同你在其它地方写的泛型 where **分句一样。每一个需求用逗号分隔。

56、解决循环引用(弱引用和无主引用)

lazy var someClosure: () -> String = {

[unowned self, weak delegate = self.delegate!] in

*// closure body goes here*

}

57、具体来说,如果下面的条件可以满足就说明重叠访问结构体的属性是安全的:

  • 你只访问实例的存储属性,不是计算属性或者类属性;
  • 结构体是局部变量而非全局变量;
  • 结构体要么没有被闭包捕获要么只被非逃逸闭包捕获。

如果编译器不能保证访问是安全的,它就不允许访问。

58、如果你使用@testable 属性标注了导入的生产模块并且用使能测试的方式编译了这个模块,单元测试目标就能访问任何 internal 的实体。

59、接下来的关键字被预留,不能被用作标识符,除非它们像上一节标识符中描述的那样使用反引号( ` )。

  • 用在声明中的关键字: associatedtype 、 class 、 deinit 、 enum 、 extension 、 func 、 import 、 init 、inout 、 internal 、 let 、 operator 、 private 、 protocol 、 public 、 static 、 struct 、 subscript 、typealias 和 var 。
  • 用在语句中的关键字: break 、 case 、 continue 、 default 、 defer 、 do 、 else 、 fallthrough 、 for 、guard 、 if 、 in 、 repeat 、 return 、 switch 、 where 和 while 。
  • 用在表达式和类型中的关键字: as 、 catch 、 dynamicType 、 false 、 is 、 nil , rethrows 、 super 、 self 、Self 、 throw 、 throws 、 true 和 try 。
  • 用在模式中的关键字: _ 。
  • 起始于数字标记( # )的关键字: #available 、 #column 、 #else 、 #elseif 、 #endif 、 #file 、 #function 、#if 、 #line 、 #selector 和 #sourceLocation 。
  • 特定上下文中被保留的关键字: associativity 、 convenience 、 dynamic 、 didSet 、 final 、 get 、 infix 、indirect 、 lazy 、 left 、 mutating 、 none 、 nonmutating 、 optional 、 override 、 postfix 、precedence 、 prefix 、 Protocol 、 required 、 right 、 set 、 Type 、 unowned 、 weak 和 willSet 。这些关键字在特定上下文语法之外可以被用于标识符。

以下标记被当作保留符号,不能用于自定义操作符: ( 、 ) 、 { 、 } 、 [ 、 ] 、 . 、 , 、 : 、 ; 、 = 、 @ 、 # 、 & (作为前缀操作符)、 -> 、` 、 ? 和 ! (作为后缀操作符)。

60、观察者:KVO 是一个纯 OC 的特性,所以如果是 swift class 需要在声明的时候增加 @objcMembers 关键字。否则在运行的时候你会得到一个 error。另外一件事就是被观察的属性需要用dynamic修饰,否则也无法观察到。一个好消息是不需要在对象被回收时手动 remove observer。但是这也带来了另外一个容易被忽略的事情:观察的闭包没有被强引用,需要我们自己添加引用,否则当前函数离开后这个观察闭包就会被回收了。

61、一个继承 NSObject 的 swift 类不再默认给所有函数添加 @objc。只在实现 OC 接口和重写 OC 方法时才自动给函数添加 @objc 标识。

62、在 4.2 中苹果终于响应了这个要求!现在 otional bindingself 不再作为保留关键字。

你可能感兴趣的:(读完Swift开发文档后记录的小知识点)