Swift 面向协议编程的 RMP 模式

Swift 面向协议编程的 RMP 模式

一、RMP 模式概述

RMP(Runtime Mixin Protocol)模式是 Swift 中一种强大的面向协议编程架构,通过三个核心要素解决传统面向对象编程的痛点,特别适合构建复杂的、需要高度复用的功能模块。通过合理的协议设计和运行时属性绑定,可以创建出既灵活又可维护的代码结构:

  • Protocol:解决多继承问题,通过协议声明能力

  • Runtime:解决协议无法储存对象变量问题,通过关联对象实现状态存储

  • Mixin:解决多协议冲突问题,通过协议组合实现能力复用

二、设计问题

面向对象继承的本质是单一树形结构 ,它在描述"是什么(is-a)"时很自然,

但在描述"能做什么(can-do)"时会遇到两个典型困境:

困境一:功能横切多个类

礼物动画、PK、连麦是独立功能,但视频房、语音房、多人房都需要它们。

用继承的话,要么写三遍,要么塞进公共基类------基类越来越臃肿。

困境二:功能组合爆炸

如果用继承来组合,"支持PK的视频房"和"支持PK的语音房"是两条继承链,

再加"支持连麦",变成四条------每增加一个维度,继承树翻倍。

解法:RMP 模式

把"能做什么"从类里剥离出来,定义为独立的能力单元(MixinProtocol)。类只需声明"我拥有这个能力",

能力的属性和逻辑全部自包含在 MixinProtocol 内,彼此不知道对方存在。


三、Swift 的实现基础

3.1 Protocol 层

Swift 的协议扩展(Protocol Extension) 天然支持为协议提供默认实现------这是实现

RMP 模式的基础。通过协议,我们可以声明能力接口,并通过扩展提供默认实现。

3.2 Runtime 层

协议扩展有一个限制:不能定义存储属性 。

这个限制通过 ObjC Runtime 关联对象(Associated Objects) 绕过:

Runtime 允许在运行时把任意值"绑定"到任意 NSObject 实例上,

等效于给协议扩展加了存储属性。

3.3 Mixin 层

通过协议组合,我们可以实现能力的自由组合,解决多协议冲突问题,

每个能力单元(MixinProtocol)专注于单一职责,通过 Mixin 协调多个能力的生命周期。

三者结合,就形成了完整的 RMP 模式:Protocol 声明能力,Runtime 存储状态,Mixin 组合功能。


四、属性绑定方式

在 RMP 模式中,我们需要在协议扩展中实现属性存储。以下是几种常用的属性绑定方式:

方式一:懒加载对象属性

适合首次访问时才创建、之后复用的对象(视图、子控制器等)。

swift 复制代码
// 协议扩展中声明懒加载属性
var scoreView: PKScoreView {
    lazyVarObject { PKScoreView() }
}

// 实现原理示例
func lazyVarObject<T: Any>(_ block: () -> T, _ fun: String = #function, _ file: String = #file, _ line: Int = #line) -> T {
    let uniqueKey = "\(file)\(line)"
    let keyString = "\(uniqueKey).\(fun)"
    let key = UnsafeRawPointer(bitPattern: keyString.hashValue)!

    guard let obj = objc_getAssociatedObject(self, key) as? T else {
        let obj = block()
        objc_setAssociatedObject(self, key, obj, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        return obj
    }
    return obj
}

方式二:绑定属性

setter\ggetter 自动处理包装和解包:

swift 复制代码
// 自动处理值类型的包装和解包
var count: Int {
    get { associatedValue(defaultValue: 0) }
    set { setAssociatedValue(newValue) }
}

var view: UIView? {
    get { associatedValue(defaultValue: nil) }
    set { setAssociatedValue(newValue) }
}

var isActive: Bool {
    get { associatedValue(defaultValue: false) }
    set { setAssociatedValue(newValue) }
}

// 实现原理示例
func associatedValue<T>(defaultValue: T, _ fun: String = #function, _ file: String = #file, _ line: Int = #line) -> T {
    let uniqueKey = "\(file)"
    let keyString = "\(uniqueKey).\(fun)"
    let key = UnsafeRawPointer(bitPattern: keyString.hashValue)!

    if let value = objc_getAssociatedObject(self, key) as? T {
        return value
    }
    return defaultValue
}

func setAssociatedValue<T>(_ value: T, _ fun: String = #function, _ file: String = #file, _ line: Int = #line) {
    let uniqueKey = "\(file)"
    let keyString = "\(uniqueKey).\(fun)"
    let key = UnsafeRawPointer(bitPattern: keyString.hashValue)!
    objc_setAssociatedObject(self, key, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}

五、RMP 模式的标准分层结构

RMP 模式的协议通常分三层,层级越高越具体:

复制代码
┌─────────────────────────────────────────────────┐
│  Layer 0  工具层(Runtime 基础)                   │
│  BaseAssociatedProtocol                          │
│  提供 lazyVarObject 等工具方法,所有 RMP 能力的基础    │
└───────────────────┬─────────────────────────────┘
                    │ 继承
┌───────────────────▼─────────────────────────────┐
│  Layer 1  能力层(Protocol 声明)                 │
│  PKProtocol / LinkMicProtocol / GiftProtocol / ...│
│  每个能力单元 = 一个文件 = 一组内聚的属性+逻辑         │
└───────────────────┬─────────────────────────────┘
                    │ 组合
┌───────────────────▼─────────────────────────────┐
│  Layer 2  组合层(Mixin 协调)                    │
│  LiveRoomMixin : PKProtocol, LinkMicProtocol     │
│  简化 Host Class 的 conformance 声明             │
└───────────────────┬─────────────────────────────┘
                    │ 遵循
┌───────────────────▼─────────────────────────────┐
│  Layer 3  宿主层                                 │
│  class VideoRoomVC : BaseVC, LiveRoomMixin       │
│  class VoiceRoomVC : BaseVC, LiveRoomMixin       │
│  宿主类体内不写任何 RMP 逻辑,只声明 conformance   │
└─────────────────────────────────────────────────┘

六、RMP 模式的新实践:MVCM 聚合协议模式

在实践中,RMP 模式有了新的发展,根据功能复杂度采用不同的组织方式:

6.1 简单功能:独立能力单元

对于简单、单一职责的功能,直接创建一个独立的 RMP 能力单元:

适用场景:功能逻辑简单,不需要复杂的 UI、数据和操作分离。

示例 :BeautyProtocol.swift

  • 只负责美颜功能的核心逻辑

  • 包含美颜面板的管理、美颜参数的设置等

  • 宿主只需遵循此协议即可获得美颜能力

6.2 复杂功能:MVCM 聚合协议模式

对于复杂功能,采用 MVC 模式划分多个协议,再通过 Mixin 组合,形成 MVCM(Model-View-Controller-Mixin)结构:

核心思想:将复杂功能按照 MVC 职责划分为三个协议,每个协议负责一个维度,最后通过 Mixin 协议组合在一起,由 Mixin 层统合 MVC 三层的交互。

协议划分:

协议类型 职责 命名规范 示例
Model 协议 数据管理、状态存储 {功能名}MProtocol.swift VideoReadyMProtocol.swift
View 协议 UI 组件、布局、渲染 {功能名}VProtocol.swift VideoReadyVProtocol.swift
Controller 协议 简单方法声明、回调管理 {功能名}CProtocol.swift VideoReadyCProtocol.swift
Mixin 协议 统合 MVC 三层、处理跨层交互 {功能名}Mixin.swift VideoReadyMixin.swift

职责详解:

  1. Model 层:

    • 负责数据的存储和管理

    • 定义数据结构和状态属性

    • 提供数据操作方法

    • 不直接与视图层交互

  2. View 层:

    • 负责 UI 组件的创建和布局

    • 提供 UI 更新方法

    • 通过参数接收数据,不直接访问数据层

    • 不处理业务逻辑

  3. Controller 层:

    • 定义简单的方法接口

    • 管理回调闭包

    • 不包含复杂的业务逻辑

    • 不直接操作视图

  4. Mixin 层:

    • 统合 MVC 三层,处理跨层交互

    • 实现复杂的业务逻辑

    • 协调数据层和视图层的通信

    • 提供完整的功能实现

文件结构:

复制代码
├── VideoReadyMProtocol.swift  ← 数据层
├── VideoReadyVProtocol.swift  ← 视图层
├── VideoReadyCProtocol.swift  ← 控制层
└── VideoReadyMixin.swift      ← 混合层(统合 MVC 三层)

优点:

  1. 职责清晰:每个协议专注于自己的职责,符合单一职责原则
  2. 代码组织:复杂功能的代码被合理拆分,提高可读性和可维护性
  3. 易于测试:各层可以独立测试,提高测试覆盖率
  4. 灵活性:可以根据需要单独使用某一层的能力
  5. 逻辑集中:复杂的业务逻辑集中在 Mixin 层,便于维护

使用方式 :

宿主类只需遵循 Mixin 协议,即可获得完整的功能:

swift 复制代码
class VideoReadyController: BaseViewController, VideoReadyMixin {
    // 无需实现任何逻辑,只需声明遵循协议
}

核心实现特点:

  • 控制层轻量化:只保留简单的方法声明和回调管理,复杂逻辑移至 Mixin 层

  • 混合层统合:Mixin 层负责协调 MVC 三层的交互,处理复杂业务逻辑

  • 跨层通信:通过 Mixin 层实现数据层和视图层的通信,避免直接耦合

  • 功能完整:宿主类只需遵循 Mixin 协议,即可获得完整的功能

这种 MVCM 结构特别适合复杂的业务模块,能够在保持 RMP 模式优势的同时,提供更清晰的代码结构和职责划分。


七、RMP 能力单元内访问宿主对象

RMP 能力单元的方法有时需要访问宿主 Controller 的属性(如 view、navigationController 等)。

由于能力单元不知道自己会被谁遵循,用交叉类型条件转换来安全地取得完整上下文:

swift 复制代码
// Self 同时满足"宿主基类"和"当前能力单元"两个约束
func pk_start() {
    guard let self = self as? BaseController & PKProtocol else { return }
    // 现在可以同时访问 BaseController 的属性和 PKProtocol 的属性
    self.view.addSubview(scoreView)
    self.someMethod()
}

guard let self = self as? HostClass & ThisProtocol 是 RMP 能力单元内访问宿主的标准写法 ,

不要用 as! 强转,不要假设 self 的具体类型。


八、新建一个 RMP 能力单元的完整模板

8.1 简单能力单元模板

swift 复制代码
import Foundation
import UIKit

// MARK: - 协议声明(定义对外暴露的接口)
/// 美颜功能 RMP 能力单元
public protocol BeautyProtocol: BaseAssociatedProtocol {
    func beauty_setup()
    func beauty_showPanel()
    func beauty_hidePanel()
    func beauty_updateOptions(_ options: [String: Any])
}

// MARK: - 默认实现(所有逻辑内聚于此)
public extension BeautyProtocol {

    // ---- 属性(Runtime 绑定)----

    // 懒加载属性
    private var beautyControlView: BeautyControlView {
        lazyVarObject { BeautyControlView() }
    }

    // 值类型属性
    private var beautyOptions: [String: Any] {
        get { associatedValue(defaultValue: [:]) }
        set { setAssociatedValue(newValue) }
    }

    private var isBeautyEnabled: Bool {
        get { associatedValue(defaultValue: true) }
        set { setAssociatedValue(newValue) }
    }

    // ---- 方法 ----

    func beauty_setup() {
        guard let self = self as? UIViewController & BeautyProtocol else { return }
        
        // 添加美颜控制面板
        self.view.addSubview(beautyControlView)
        beautyControlView.isHidden = true
        
        // 初始化美颜参数
        beautyOptions = [
            "smoothness": 0.5,
            "whiteness": 0.5,
            "redness": 0.5
        ]
    }

    func beauty_showPanel() {
        beautyControlView.isHidden = false
        // 显示美颜面板逻辑
    }

    func beauty_hidePanel() {
        beautyControlView.isHidden = true
        // 隐藏美颜面板逻辑
    }

    func beauty_updateOptions(_ options: [String: Any]) {
        beautyOptions = options
        // 应用美颜参数逻辑
    }
}

8.2 MVCM 结构模板

Model 协议
swift 复制代码
/// 视频准备模块数据协议
public protocol VideoReadyMProtocol: BaseAssociatedProtocol {
    // 数据属性
    var topic: String { get set }
    var coverImage: UIImage? { get set }
    var coverImageURL: String { get set }
    var isNotificationEnabled: Bool { get set }
    
    // 方法
    func resetState()
}

public extension VideoReadyMProtocol {
    var topic: String {
        get { associatedValue(defaultValue: "") }
        set { setAssociatedValue(newValue) }
    }
    
    var coverImage: UIImage? {
        get { associatedValue(defaultValue: nil) }
        set { setAssociatedValue(newValue) }
    }
    
    var coverImageURL: String {
        get { associatedValue(defaultValue: "") }
        set { setAssociatedValue(newValue) }
    }
    
    var isNotificationEnabled: Bool {
        get { associatedValue(defaultValue: true) }
        set { setAssociatedValue(newValue) }
    }
    
    func resetState() {
        topic = ""
        coverImage = nil
        coverImageURL = ""
        isNotificationEnabled = true
    }
}
View 协议
swift 复制代码
/// 视频准备模块视图协议
public protocol VideoReadyVProtocol: AnyObject {
    // UI 组件
    var previewView: UIView { get }
    var topicTextField: UITextField { get }
    var coverImageView: UIImageView { get }
    var startButton: UIButton { get }
    var notificationButton: UIButton { get }
    
    // 方法
    func setupUI()
    func updateTopic(_ topic: String)
    func updateCoverImage(_ image: UIImage)
    func updateNotificationButton(_ enabled: Bool)
}

public extension VideoReadyVProtocol where Self: UIView {
    var previewView: UIView {
        lazyVarObject { 
            let view = UIView()
            view.backgroundColor = .black
            return view
        }
    }
    
    var topicTextField: UITextField {
        lazyVarObject { 
            let textField = UITextField()
            textField.placeholder = "输入直播标题"
            textField.textColor = .white
            return textField
        }
    }
    
    var coverImageView: UIImageView {
        lazyVarObject { 
            let imageView = UIImageView()
            imageView.image = UIImage(named: "default_cover")
            imageView.contentMode = .scaleAspectFill
            return imageView
        }
    }
    
    var startButton: UIButton {
        lazyVarObject { 
            let button = UIButton(type: .custom)
            button.setTitle("开始直播", for: .normal)
            button.setTitleColor(.white, for: .normal)
            return button
        }
    }
    
    var notificationButton: UIButton {
        lazyVarObject { 
            let button = UIButton(type: .custom)
            button.setTitle("通知粉丝", for: .normal)
            button.setTitleColor(.white, for: .normal)
            return button
        }
    }
    
    func setupUI() {
        // 布局 UI 组件
        addSubview(previewView)
        addSubview(topicTextField)
        addSubview(coverImageView)
        addSubview(startButton)
        addSubview(notificationButton)
        
        // 设置布局约束
        // ...
    }
    
    func updateTopic(_ topic: String) {
        topicTextField.text = topic
    }
    
    func updateCoverImage(_ image: UIImage) {
        coverImageView.image = image
    }
    
    func updateNotificationButton(_ enabled: Bool) {
        let image = enabled ? UIImage(named: "notification_on") : UIImage(named: "notification_off")
        notificationButton.setImage(image, for: .normal)
    }
}
Controller 协议
swift 复制代码
/// 视频准备模块控制协议
public protocol VideoReadyCProtocol: AnyObject {
    // 回调闭包
    var onBack: (() -> Void)? { get set }
    var onStart: (([String: Any]) -> Void)? { get set }
    var onSelectCover: (() -> Void)? { get set }
    
    // 方法
    func backAction()
    func startAction()
    func coverTapped()
    func notificationTapped()
}

public extension VideoReadyCProtocol where Self: UIView {
    var onBack: (() -> Void)? {
        get { associatedValue(defaultValue: nil) }
        set { setAssociatedValue(newValue) }
    }
    
    var onStart: (([String: Any]) -> Void)? {
        get { associatedValue(defaultValue: nil) }
        set { setAssociatedValue(newValue) }
    }
    
    var onSelectCover: (() -> Void)? {
        get { associatedValue(defaultValue: nil) }
        set { setAssociatedValue(newValue) }
    }
    
    func backAction() {
        onBack?()
    }
    
    func startAction() {
        // 具体实现移至 Mixin 层
    }
    
    func coverTapped() {
        onSelectCover?()
    }
    
    func notificationTapped() {
        // 具体实现移至 Mixin 层
    }
}
Mixin 协议
swift 复制代码
/// 视频准备模块混合协议
public protocol VideoReadyMixin: VideoReadyMProtocol, VideoReadyVProtocol, VideoReadyCProtocol {}

public extension VideoReadyMixin {
    func setupVideoReady() {
        // 初始化数据
        resetState()
        
        // 设置 UI
        setupUI()
        
        // 绑定事件
        bindEvents()
        
        // 启动预览
        startPreview()
    }
    
    func bindEvents() {
        // 封面点击
        let coverTap = UITapGestureRecognizer { [weak self] _ in
            self?.coverTapped()
        }
        coverImageView.isUserInteractionEnabled = true
        coverImageView.addGestureRecognizer(coverTap)
        
        // 开始按钮点击
        startButton.addTarget(self, action: #selector(startAction), for: .touchUpInside)
        
        // 通知按钮点击
        notificationButton.addTarget(self, action: #selector(notificationTapped), for: .touchUpInside)
    }
    
    func startPreview() {
        // 启动相机预览逻辑
    }
    
    // 重写控制层方法
    func startAction() {
        // 验证输入
        guard !topic.isEmpty else {
            showAlert("请输入直播标题")
            return
        }
        
        // 构建参数
        var params: [String: Any] = [
            "topic": topic,
            "coverImageURL": coverImageURL,
            "notificationEnabled": isNotificationEnabled
        ]
        
        // 调用回调
        onStart?(params)
    }
    
    func notificationTapped() {
        // 切换通知状态
        isNotificationEnabled = !isNotificationEnabled
        
        // 更新 UI
        updateNotificationButton(isNotificationEnabled)
    }
    
    private func showAlert(_ message: String) {
        // 显示提示
    }
}

九、生命周期多路接入问题

问题根源

Swift 协议扩展对同一方法只有一个实现会生效。当多个 RMP 能力单元都想响应 viewDidLoad,

就会产生静默冲突------编译通过,但只有一个能力单元的逻辑被执行:

swift 复制代码
extension PKProtocol      { func viewDidLoad() { /* 只有这个生效 */ } }
extension LinkMicProtocol { func viewDidLoad() { /* 被静默忽略 */ } }

根本原因 :viewDidLoad 是单入口,而我们有 N 个 RMP 能力单元需要接入它。

问题的本质是谁来做多路分发。


解法一:各 RMP 能力单元用独立命名钩子,由 Mixin 链式编排

适用于不依赖 RxSwift 的场景,或需要精确控制调用顺序的模块。

每个 RMP 能力单元不实现 viewDidLoad,改为实现自己命名的钩子,

Mixin 协议提供统一实现,负责按序调用所有钩子。

swift 复制代码
// ---- 每个 RMP 能力单元只定义自己命名的钩子,无冲突 ----
extension PKProtocol {
    func pk_viewDidLoad() { /* PK 初始化 */ }
    func pk_viewWillAppear(_ animated: Bool) { /* PK 刷新 */ }
}

extension LinkMicProtocol {
    func lm_viewDidLoad() { /* 连麦初始化 */ }
}

// ---- Mixin:统一编排调用顺序 ----
protocol LiveRoomMixin: PKProtocol, LinkMicProtocol {
    func setup()
    func viewWillAppear(_ animated: Bool)
}

extension LiveRoomMixin {
    func setup() {
        pk_viewDidLoad()    // PK 初始化
        lm_viewDidLoad()    // 连麦初始化
    }
    
    func viewWillAppear(_ animated: Bool) {
        pk_viewWillAppear(animated) // PK 刷新
    }
}

// ---- 宿主:调用 Mixin 方法 ----
class VideoRoomController: BaseViewController, LiveRoomMixin {
    override func viewDidLoad() {
        super.viewDidLoad()
        setup() // 调用 Mixin 方法
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.viewWillAppear(animated) // 调用 Mixin 方法
    }
}

解法二:使用事件发布-订阅模式

适用于使用 RxSwift 或其他响应式框架的项目。

swift 复制代码
// ---- 基础控制器 ----
class BaseViewController: UIViewController {
    let rx_viewDidLoad = PublishRelay<Void>()
    let rx_viewWillAppear = PublishRelay<Bool>()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        rx_viewDidLoad.accept(())
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        rx_viewWillAppear.accept(animated)
    }
}

// ---- RMP 能力单元 ----
extension PKProtocol {
    func pk_bindEvents() {
        guard let self = self as? BaseViewController & PKProtocol else { return }
        
        // 监听生命周期事件
        self.rx_viewDidLoad
            .subscribe(onNext: { [weak self] in
                self?.pk_setup()
            })
            .disposed(by: self.disposeBag)
        
        self.rx_viewWillAppear
            .subscribe(onNext: { [weak self] _ in
                self?.pk_refresh()
            })
            .disposed(by: self.disposeBag)
    }
}

// ---- Mixin:统一绑定 ----
extension LiveRoomMixin {
    func bindMixins() {
        pk_bindEvents()
        lm_bindEvents()
    }
}

// ---- 宿主 ----
class VideoRoomController: BaseViewController, LiveRoomMixin {
    override func viewDidLoad() {
        super.viewDidLoad()
        bindMixins() // 绑定所有 RMP 能力单元
    }
}

十、何时用 RMP 能力单元,何时用继承

场景 推荐方式
功能需要跨多个无关类复用 RMP 能力单元
多个功能需要自由组合 RMP 能力单元
向现有类追加功能,不想修改它 RMP 能力单元
严格的 is-a 关系(Dog is-a Animal) 继承
需要访问 super、重写生命周期 继承
功能仅一个类使用,不存在复用 直接写在类里

不要滥用 RMP 能力单元 :如果一个功能只有一个宿主,直接写在类里更清晰。

RMP 能力单元的价值在于复用和隔离,强行拆分只会增加间接层。


十一、命名规范

协议文件命名

{功能名}Protocol.swift(建议使用功能名作为前缀,Protocol 作为后缀)

方法 / 属性前缀

每个 RMP 能力单元的公开成员加功能名前缀,防止不同能力单元之间命名冲突:

RMP 能力单元 前缀 示例
PK 功能 pk_ pk_start(), pk_scoreView
连麦 lm_ lm_onMic(), lm_isOnMic
礼物动效 gift_ gift_play(), gift_queue
美颜 beauty_ beauty_setup(), beauty_options
视频房 video_ video_initUI(), video_previewView

十二、常见错误

错误 后果 正确做法
把逻辑写进宿主 Controller RMP 能力单元失去意义,基类重新膨胀 逻辑全写在 extension XxxProtocol 里
在宿主里 override 方法后调用 RMP 能力单元 形成重复调用层 RMP 能力单元自己在 setup 里完成初始化
多个 RMP 能力单元共享关联对象 key 属性互相覆盖,产生诡异 bug 为每个属性生成唯一 key
用 as! 强转宿主类型 运行时崩溃 用 guard let self = self as? Host & XxxProtocol
RMP 能力单元之间直接互相调用 能力单元耦合,破坏独立性 通过宿主或组合协议传递依赖
对只有一个宿主的功能也拆 RMP 能力单元 过度设计,增加无谓间接层 直接写在类里

十三、RMP 模式的优势

  1. 代码复用:能力单元可以被多个类复用,避免重复代码
  2. 解耦:能力单元之间相互独立,降低耦合度
  3. 灵活性:可以自由组合不同的能力单元
  4. 可测试性:每个能力单元可以独立测试
  5. 清晰的职责划分:每个能力单元专注于单一职责
  6. 易于维护:功能模块化,便于理解和修改

RMP 模式是 Swift 面向协议编程的强大实践,特别适合构建复杂的、需要高度复用的功能模块。通过合理的协议设计和运行时属性绑定,可以创建出既灵活又可维护的代码结构。

相关推荐
geovindu3 分钟前
rust: Abstract Factory pattern
开发语言·后端·设计模式·rust·抽象工厂模式
“AI国潮设计-小江”6 分钟前
《Python+SDXL实战:用ControlNet批量生成“英歌舞麻将糕”IP,附自动化脚本与商用思路》
开发语言·人工智能·python·prompt·aigc
Cc.Y33 分钟前
Java零基础入门:封装与继承 —— 从“裸奔“到“穿衣服“,从“重复造轮子“到“站在巨人肩膀上“
java·开发语言
Escalating_xu35 分钟前
【C 语言】深入理解指针(4):回调函数、qsort、void * 与泛型排序的模拟实现
java·c语言·开发语言
吠品1 小时前
GitHub 周榜拆解:决策小模型扎堆上位
c语言·开发语言·算法
见叶之秋1 小时前
C++ 泛型世界的两块拼图:容器适配器与仿函数
java·开发语言
逗脑IDE2 小时前
零基础学ESP32:RFID无线射频卡——让ESP32拥有“刷卡“能力!
开发语言·网络·人工智能·python·esp32·硬件开发
小师兄吃牛肉2 小时前
C语言篇:内联函数
c语言·开发语言
yuniko-n2 小时前
【Java】字符串相关坑点
java·开发语言·python
jimy13 小时前
虚函数vtable,链接时看到构造函数,linker把符号地址写入vtable(三)
开发语言·c++