wift Part 5 oc -> swift

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个字节内存对齐

结论:

  1. 纯 Swift 类(不继承 NSObject)完全可以正常工作,并且有自己的内存管理机制(通过单独的引用计数区域)。
  2. 这证明了 :在 Swift 中,NSObject 不是必须的基类。纯 Swift 类更轻量、更独立,不依赖 Objective-C 运行时。
  3. 继承 NSObject 的类后, 引用计数相关压缩进了前8个字节

9 可选协议

  1. 仅仅能被类遵守的协议
swift 复制代码
protocol Runnable1: AnyObject {}
protocol Runnable2: class {}  // 这个过期了 AnyObject代替
@objc protocol Runnable3 {} // 除了需要被类swift类遵守, 还将暴露给外部oc类调用
swift 复制代码
// 协议
protocol Runnable {
    func run()
}

class TestViewController: UIViewController, Runnable {
// ❌ 不实现协议方法 func run() 报错
}
  1. 遵守协议, 不用写实现了
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
  }
}
相关推荐
sakiko_7 小时前
Swift学习笔记33-多线程与UI渲染
笔记·学习·swiftui·swift
Gavin-Wang1 天前
swift 代码规范
蓝桥杯·swift·代码规范
2501_915921431 天前
Xcode与iOS SDK完整教程:从下载安装到配置优化全解析
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
东坡肘子2 天前
从社区路标到生态基石:Dave Verwer 的新篇章 -- 肘子的 Swift 周报 #137
人工智能·swiftui·swift
人月神话-Lee3 天前
【图像处理】Sobel 边缘检测——让机器“看见“轮廓
图像处理·人工智能·计算机视觉·ios·ai编程·swift
songgeb3 天前
Swift Optional几个名词备忘
swift
sakiko_4 天前
Swift学习笔记31-网络请求
网络·笔记·学习·swift
人月神话-Lee5 天前
【图像处理】高斯模糊——最优雅的模糊算法
图像处理·人工智能·算法·ios·ai编程·swift
Daniel_Coder5 天前
iOS Widget 开发-20:从旧版 API 迁移到 iOS 17+ 现代 Widget
ios·swift·widget·widgetcenter