1. 记号/标记
作用: 打个标记, 方便寻找
swift
// MARK: 类似于OC中的 #pragma mark (红旗任务)
// TODO: 用于标记未完成的任务
// FIXME: 用于标记待修复的问题
#warning("警告!!!")

2 条件编译
swift
// 操作系统: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
查看当前模式 product -> scheme -> edit scheme -> (run) Build Configiuration -> Debug / Release
swift
// 代码区分 debug模式 或 release模式
#if DEBUG
print("debug!!!!")
#else
print("release!!!!")
#endif
swift
//统版本检测 (available 可获得的)
if #available(iOS 11.0, tvOS 11.0, *){
// ios 11.0 以上, tvOS 11.0 以上
}
3 程序入口配置 根控制器配置
swift
// 1 通过 AppDelegate 配置根控制器
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow? // 新建
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// 新建
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
return true
}
}
swift
// 2 通过 SceneDelegate 配置根控制器
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow? //新建
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
// 替换成你的控制器
let yourVC = TabBarViewController()
window?.rootViewController = yourVC
window?.makeKeyAndVisible()
}
}
4 Swift 调用 OC
swift 通过桥接文件 调用oc文件 {targetName}-Bridging-Header.h
-
桥接文件并暴露oc文件, swift通过桥接文件访问oc文件
-
桥接文件会在第一次创建oc文件同时创建
swift
// 例如
learn-swift1-Bridging-Header.h //桥接文件
learn-swift1 // 项目名称
// 默认 swift项目首次新建oc文件, 会自动创建一个桥接文件
// 支持自行创建桥接文件

swift
// 1 创建oc文件
#import "OCViewController.h"
@implementation OCViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"oc文件";
}
@end
swift
// 2 桥接文件中暴露oc文件
// 测试oc控制器
#import "OCViewController.h"
swift
// 3 swift 调用oc文件
import UIKit
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
// 使用 oc 文件
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.navigationController?.pushViewController(OCViewController(), animated: true)
}
}
5 OC 调用 swift
oc 通过桥接文件 调用 swift 文件 {targetName}-Swift.h
- 桥接文件是个隐藏文件, 非醒目在项目内
- 桥接文件自动创建(bulid setting查看)
ps: 所以 oc 调用swift 仅需要引入头文件即可

swift
// 文件名 learn-swift1
#import "learn-swift1-Swift.h" // ❌ 按道理是这个 但是找不到
#import "learn_swift1-Swift.h" // 这个能找到
// 所以 带横线的会转成 "-" -> "_"
5.1 @objcMembers & @objc
objcMembers: 全类都开放给 oc 文件使用
@objc: 某属性, 方法暴露给 oc 文件使用
swift
// swift 文件 暴露 属性方法 给oc
@objcMembers class TestViewController: UIViewController {
@objc var price: Double
var band: String
init(price: Double, band: String) {
self.price = price
self.band = band
super.init(nibName: nil, bundle: nil) // ViewController 的初始化必须确保父类也完成初始化
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func run() {
print(price, band, "run")
}
static func run() {
print("Car run")
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
swift
// oc 调用 swift
#import "OCViewController.h"
#import "learn_swift1-Swift.h" // 引入头文件
@implementation OCViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"oc文件";
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
TestViewController *con = [[TestViewController alloc] initWithPrice:10 band:@"band"];
[con run]; // print(price, band, "run")
TestViewController.run; // print("Car run")
[self.navigationController pushViewController:con animated:true];
}
5.1 @objc (别名)
@objc (TVC) 代表将类 "TestViewController" 起别名为 "TVC" 并开放给oc
SWIFT
// 起别名
@objc (TVC)
@objcMembers class TestViewController: UIViewController {
@objc var price: Double
@objc func run() {
print(price, band, "run")
}
static func run() {
print("Car run")
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
// 注意 @objc (TVC) 代表开发 TVC 给oc
// @objcMembers 必须有, 否则找不到 TVC 内的方法
6 选择器 Selector
swift也有perform 方法 , 由于是oc方法 需要添加 @objcMembers
swift
// oc 的 perform
- (void) jump {
NSLog(@"OCViewController--挑起来了");
}
[self performSelector:@selector(jump)];
swift
// swift 的 perform
@objc func test1(v1: Int) {
print("test1")
}
self.perform(#selector(test1(v1:)))
7 String 与 NSString关系 及 as 转换
swift
// String 转 NSString 方便操作字符串切割
let str = "123456789"
let str1 = str as NSString // 转oc字符串
let str3 = str1.substring(with: NSRange(location: 3, length: 2)) // 45
swift
// NSString 转 String
let str5 : NSString = "0987654321"
var str6 = str5 as String
str6.append("-111") // 0987654321-111
swift
// 对比 NSString VS String 截取字符串
// OC截取
let str2 = str1.substring(from: 4) // 56789
// Swift截取
let str4 = str[str.index(str.startIndex, offsetBy: 4) ... str.index(before: str.endIndex)] // 56789
桥接转换表
String 和 NSString 相互转换, 本质是通过桥接进行的
双向箭头: 代表可以互相转换

8 是否继承 NSObject 内存分析
swift
class Person {
var age = 10
var weight: Int = 0
}
// 内存情况
// Person对象占用32位
// 最前8个字节存放isa指针(指向元类型)
// 然后8个字节引用计数相关
// 在后8个字节 age
// 最后8个字节 weight*
swift
class Person: NSObject {
var age = 10
var weight: Int = 0
}
// 内存情况
// Person对象占用32位
// 最前8个字节存放isa指针(指向元类型)
// 然后8个字节 age
// 最后8个字节 weight
// 最后8个字节内存对齐
结论:
- 纯 Swift 类(不继承 NSObject)完全可以正常工作,并且有自己的内存管理机制(通过单独的引用计数区域)。
- 这证明了 :在 Swift 中,
NSObject不是必须的基类。纯 Swift 类更轻量、更独立,不依赖 Objective-C 运行时。 - 继承 NSObject 的类后, 引用计数相关压缩进了前8个字节
9 可选协议
- 仅仅能被类遵守的协议
swift
protocol Runnable1: AnyObject {}
protocol Runnable2: class {} // 这个过期了 AnyObject代替
@objc protocol Runnable3 {} // 除了需要被类swift类遵守, 还将暴露给外部oc类调用
swift
// 协议
protocol Runnable {
func run()
}
class TestViewController: UIViewController, Runnable {
// ❌ 不实现协议方法 func run() 报错
}
- 遵守协议, 不用写实现了
swift
// 协议
protocol Runnable {
func run()
}
// 扩展给默认实现(空)
extension Runnable {
func run() {
print("什么都不做")
}
}
class TestViewController: UIViewController, Runnable {
override func viewDidLoad() {
super.viewDidLoad()
TestViewController().run()
}
}
// TestViewController 遵守协议, 但是不需要写实现了
加 @objc dynamic 的核心作用:让方法调用走 Objective-C 的消息转发机制(objc_msgSend),而不是 Swift 的静态或虚表调用。
10 dynamic 关键字
swift
class Dog: NSObject {
@objc dynamic func bark() {
print("汪汪")
}
}
let original = #selector(Dog.bark)
// Dog.bark 走oc的消息机制
11 Swift 代码使用 OC 的 KVC / KVO
swift
class TestViewController: UIViewController {
@objc dynamic var age: Int = 0
var observation : NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
// 添加观察值 监听age
observation = observe(\Self.age, options: [.old,.new]) {
(person, change) in
print("---",change.oldValue as Any,change.newValue as Any)
}
self.age = 30 //--- Optional(0) Optional(30)
self.age = 12 //--- Optional(30) Optional(12)
}
}
// 注意 \Self.age 类型的属性这么写, 类型.age
swift
// 原方法
observe(_ keyPath: KeyPath<_KeyValueCodingAndObserving, Value>,
options: NSKeyValueObservingOptions,
changeHandler:(_KeyValueCodingAndobserving, NSKeyValueObservedChange<Value>) -> Void) -> NSKeyValueObservation
// 参数 1
_ keyPath: KeyPath<_KeyValueCodingAndObserving, Value>
// `_KeyValueCodingAndObserving` 就是"支持 KVC/KVO 的任何类型"(比如 `NSObject` 的子类)。
传一个 类型的值
swift
// swift 属性监听
class TestViewController: UIViewController {
var name : String? {
willSet { // 属性即将改变时进行监听
print("willSet--name:", name ?? "")
print("willSet--newValue:", newValue ?? "")
}
didSet { // 属性已经改变时进行监听
print("didSet--name:", name ?? "")
print("didSet--oldValue:", oldValue ?? "")
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.name = "why"
self.name = "yz"
}
}
// willSet--name:
// willSet--newValue: why
// didSet--name: why
// didSet--oldValue:
// willSet--name: why
// willSet--newValue: yz
// didSet--name: yz
12 关联对象(Associated Object)
swift
// 关联对象
class Person { }
extension Person {
// 定义全局变量, 只要为了用地址值, Void占一个内存
private static var AGE_KEY: Void?
var age: Int {
get {
(objc_getAssociatedObject(self, &Self.AGE_KEY) as? Int) ?? 0
}
set {
objc_setAssociatedObject(self, &Self.AGE_KEY, // 关联地址值
newValue, // 传进来的变量
.OBJC_ASSOCIATION_ASSIGN) // 存储策略
}
}
}
swift
var p = Person()
print(p.age) // 0
p.age = 10
print(p.age) // 10
13 多线程
13.1 异步线程
swift
class TestViewController: UIViewController {
// 创建 DispatchGroup 实例!!!
let group = DispatchGroup()
override func viewDidLoad() {
super.viewDidLoad()
// 异步并发队列
let queue1 = DispatchQueue(label: "com.Miss.queue", qos: .default, attributes: .concurrent)
queue1.async(group: group) {
Thread.sleep(forTimeInterval: 3)
print("翻翻书")
}
let queue2 = DispatchQueue(label: "com.Miss.queue", qos: .default, attributes: .concurrent)
queue2.async(group: group) {
print("画画画")
Thread.sleep(forTimeInterval: 1)
}
let queue3 = DispatchQueue(label: "com.Miss.queue", qos: .default, attributes: .concurrent)
queue3.async(group: group) {
print("看看景")
Thread.sleep(forTimeInterval: 2)
}
group.notify(queue: DispatchQueue.main) {
print("收拾下")
}
}
}
// 画画画
// 看看景
// 翻翻书
// 收拾下
13.2 延迟线程
swift
let item = DispatchWorkItem {
print("来来来\(Thread.current)")
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3, execute: item) // 主线程
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 3) {
print("来来来\(Thread.current)") // 子线程
}
// 来来来 <NSThread: 0x1159192c0>{number = 10, name = (null)}
// 来来来 <_NSMainThread: 0x1046ca130>{number = 1, name = main}
13.3 一次性任务
线程安全, 整个程序只初始化一次
swift
fileprivate let initTask2: Void = { // 静态全局变量
print("initTask2---------")
}()
class TestViewController: UIViewController {
static let initTask1: Void = { // 静态局部变量
print("initTask1---------")
}()
override func viewDidLoad() {
super.viewDidLoad()
test()
test()
test()
print("华丽的分割线")
}
func test() {
let _ = Self.initTask1 // 初始化一次
let _ = initTask2 // 初始化一次
}
}
// initTask1---------
// initTask2---------
// 华丽的分割线
// 注意 Void = ()
// 立即执行闭包 { ... }() 返回值为 () 所以用Void声明
13.4 信号量
swift
// 1 保证同一时间只有 1 个线程读写共享数据(字典、数组、变量),防止多线程崩溃。
// 2 控制线程最大并发数
class Cache {
private static var data = [String: Any]()
private static var lock = DispatchSemaphore(value: 2) // 最多两条线程可以访问
static func set(_ key: String, _ value: Any) {
lock.wait()
defer { lock.signal() } // defer: 方法退出前执行
data[key] = value
}
}