Swift tips 笔记
- CaseIterable协议
enum遵守了CaseIterable协议可以实现遍历枚举值
enum Season : CaseIterable {
case spring, summer, autumn, winter
}
print(Season.allCases.count)
Season.allCases.forEach { (s) in
print(s)
}
- defer 语句
defer 用来定义离开代码块前必须执行的代码
- assert 断言
断言机制:不符合指定条件就抛出运行时错误,常用于调试Debug阶段的条件编译。
默认情况下Swift的断言只会在Debug模式下生效,Release模式下会失效。
修改断言机制的行为:
-assert-config Release: 强制关闭断言
-assert-config Debug: 强制开启断言
- 指针
Swift中的指针被定义为不安全的。
UnsafePointer<Int> 类似于 const int *
UnsafeMutablePointer<Int> 类似于 int *
UnsafeRawPointer 类似于 const void *
UnsafeMutableRawPointer 类似于 void *
Demo:
var number = 0
test(&number)
print("number = \(number)")
test1(&number)
test2(&number)
print("number = \(number)")
test3(&number)
func test(_ ptr: UnsafeMutablePointer<Int>) {
ptr.pointee += 10
}
func test1(_ ptr: UnsafePointer<Int>) {
print(ptr.pointee)
}
func test2(_ ptr: UnsafeMutableRawPointer) {
// 修改数据
ptr.storeBytes(of: 30, as: Int.self)
}
func test3(_ ptr: UnsafeMutableRawPointer) {
// 读取数据
print(ptr.load(as: Int.self))
}
1、获取指向某个变量的指针
// 通过变量 获取 UnsafePointer 类型的指针
var ptr = withUnsafePointer(to: &number) { $0 }
// 通过变量 获取 UnsafeMutablePointer 类型的指针
var ptr1 = withUnsafeMutablePointer(to: &number) { $0 }
// 通过变量 获取 UnsafeMutableRawPointer 类型的指针
var ptr2 = withUnsafeMutablePointer(to: &number, { UnsafeMutableRawPointer($0)})
// 通过变量 获取 UnsafeRawPointer 类型的指针
var ptr3 = withUnsafePointer(to: &number, {UnsafeRawPointer($0)})
2、获取指向堆空间实例的指针
class Person {}
var p = Person()
// ptr 指向 p 相当于存放的是 变量p的内存地址
var ptr = withUnsafePointer(to: &p, { UnsafeRawPointer($0) })
print(ptr)
// addressPtr 读取8个字节 相当于就是p存储的地址,即Person()在堆中的地址
let addressPtr = ptr.load(as: Int.self)
// 用addressPtr初始化一个地址,相当于helpPtr指向堆空间的地址
var helpPtr = UnsafeRawPointer(bitPattern: addressPtr)
print(helpPtr)
3、创建指针
方式一:
var ptr = malloc(16)
// 向前8个字节存入数据
ptr?.storeBytes(of: 10, as: Int.self)
ptr?.storeBytes(of: 20, toByteOffset: 8, as: Int.self)
print(ptr?.load(as: Int.self))
print(ptr?.load(fromByteOffset: 8, as: Int.self))
free(ptr)
方式二:
var ptr2 = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
// 向前8个字节存入数据
ptr2.storeBytes(of: 23, as: Int.self)
// 先偏移8个字节然后存入数据
ptr2.advanced(by: 8).storeBytes(of: 44, as: Int.self)
print(ptr2.load(as: Int.self))
print(ptr2.advanced(by: 8).load(as: Int.self))
ptr2.deallocate()
方式三:
var ptr = UnsafeMutablePointer<Int>.allocate(capacity: 3)
ptr.initialize(to: 11)
ptr.successor().initialize(to: 22)
ptr.successor().successor().initialize(to: 33)
print(ptr.pointee) // 11
print((ptr + 1).pointee) // 22
print((ptr + 2).pointee) // 33
print(ptr[0]) // 11
print(ptr[1]) // 22
print(ptr[2]) // 33
ptr.deinitialize(count: 3)
ptr.deallocate()
方式四:
class Person {
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
deinit { print(name, "deinit") } }
var ptr = UnsafeMutablePointer<Person>.allocate(capacity: 3)
ptr.initialize(to: Person(age: 10, name: "Jack"))
(ptr + 1).initialize(to: Person(age: 11, name: "Rose"))
(ptr + 2).initialize(to: Person(age: 12, name: "Kate"))
// 反初始化
ptr.deinitialize(count: 3)
ptr.deallocate()
4、指针之间的转换
unsafeBitCast是忽略数据类型的强制转换,不会因为数据类型的变化而改变原来的内存数据 p 类似于C++中的reinterpret_cast
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
ptr.assumingMemoryBound(to: Int.self).pointee = 11
(ptr + 8).assumingMemoryBound(to: Double.self).pointee = 22.0
print(unsafeBitCast(ptr, to: UnsafePointer<Int>.self).pointee) // 11
print(unsafeBitCast(ptr + 8, to: UnsafePointer<Double>.self).pointee)
ptr.deallocate()
- 代码标记
// MARK: 类似于OC中的 #pragma mark
// MARK: - 类似于OC中的 #pragma mark - n
// TODO: 用于标记未完成的任务
// FIXME: 用于标记待修复的问题
- 条件编译
// 操作系统:macOS\iOS\tvOS\watchOS\Linux\Android\Windows\FreeBSD
#if os(macOS) || os(iOS)
// CPU架构:i386\x86_64\arm\arm64
#elseif arch(x86_64) || arch(arm64)
// swift版本
#elseif swift(<5) && swift(>=3)
// 模拟器
#elseif targetEnvironment(simulator) // 可以导入某模块
#elseif canImport(Foundation)
#else
#endif
#if DEBUG
print("debug")
#else
print("release")
#endif
#if OTHER
print("debug")
#else
print("release")
#endif
定义Debug打印方法
func log<T>(_ msg: T,
file: NSString = #file,
line: Int = #line,
fn: String = #function) {
#if DEBUG
let prefix = "\(file.lastPathComponent)_\(line)_\(fn):" print(prefix, msg)
#endif
}
- 系统版本检测
if #available(iOS 10, macOS 10.12, *) {
// 对于iOS平台,只在iOS10及以上版本执行
// 对于macOS平台,只在macOS 10.12及以上版本执行
// 最后的*表示在其他所有平台都执行
}
- API可用性
- OC和Swift之间的相互调用
1、Swift暴露给OC的类最终继承自NSObject
2、使用@objc修饰需要暴露给OC的成员
3、通过@objc重命名Swift暴露给OC的符号(类名、属性名、函数名)
4、使用@objcMembers修饰类
代表默认所有都会暴露给OC
最终是否成功暴露,还需要考虑成员自身的访问级别
5、被@objc dynamic 修饰的内容会具有动态性,调用方法会走runtime流程
- 协议
1、利用协议实现前缀效果
// 定义前缀结构体
struct MJ<Base> {
let base: Base
init(_ base: Base) {
self.base = base
}
}
// 定义协议,扩展实现默认的前缀
protocol MJCompatible {}
extension MJCompatible {
var mj: MJ<Self> {
set {}
get {
MJ(self)
}
}
static var mj: MJ<Self>.Type {
set {}
get {
MJ<Self>.self
}
}
}
// 对应的类实现协议,扩展特定的方法
extension String: MJCompatible {}
extension MJ where Base == String {
func numberCount() -> Int {
return 5
}
}
2、Base: 类
class Person {}
class Student: Person {}
// 给Person扩展mj前缀
extension Person: MJCompatible {}
// 给Person类型 添加扩展test方法
extension MJ where Base: Person {
func test() {
print("test")
}
}
let s = Student()
s.mj.test()
let p = Person()
p.mj.test()