在block中执行push操作崩溃

本文介绍了一种在iOS开发过程中遇到的问题:当试图在View中通过一个block属性处理点击事件时,程序出现崩溃现象。文章分析了问题的原因,并给出了解决方案——将block的属性从assign改为strong,同时建议将push方法置于主线程执行。

情景如下:
view中有一个block属性,点击view上的按钮,调用block
对block的赋值在viewController中
结果,点击按钮的时候崩溃,无崩溃信息

原因,view中的block使用的assign属性,改为strong即可 且 最好push方法写在主线程中

<think>我们已经在之前的回答中介绍了如何在Qt项目中集成spdlog,并展示了基本用法。现在,用户的问题是:如何在Qt项目的多个文件中使用spdlog日志库。这意味着用户希望在整个项目的多个源文件中都能方便地使用同一个日志记录器(logger)进行日志记录,而不是仅限于main.cpp。 ### 解决方案思路 1. **全局日志记录器**:创建一个全局可访问的日志记录器,这样在项目的任何地方都可以使用它。 2. **封装日志类**:将spdlog的日志记录器封装在一个单例类中,确保整个项目只有一个实例,并提供静态方法供其他文件用。 3. **初始化与销毁**:在应用程序启动时初始化日志记录器,在退出时进行清理(如刷新缓冲区)。 4. **线程安全**:spdlog本身是线程安全的,所以我们只需要确保全局访问的方式是线程安全的(单例模式通常可以满足)。 ### 步骤1:创建全局日志管理器(单例模式) 我们将创建一个名为`QtLogger`的类,它管理一个全局的spdlog日志记录器。 **文件:qtlogger.h** ```cpp #ifndef QTLOGGER_H #define QTLOGGER_H #include <spdlog/spdlog.h> #include <spdlog/sinks/stdout_color_sinks.h> #include <spdlog/sinks/basic_file_sink.h> #include <memory> class QtLogger { public: // 获取单例实例 static QtLogger& instance(); // 初始化日志系统(可设置日志文件路径,默认为空则只输出到控制台) void init(const std::string& logFilePath = ""); // 获取日志记录器 std::shared_ptr<spdlog::logger> logger() const { return m_logger; } private: QtLogger() = default; // 私有构造函数 ~QtLogger(); // 禁止复制和赋值 QtLogger(const QtLogger&) = delete; QtLogger& operator=(const QtLogger&) = delete; std::shared_ptr<spdlog::logger> m_logger; }; // 全局宏,方便用 #define LOG_TRACE(...) QtLogger::instance().logger()->trace(__VA_ARGS__) #define LOG_DEBUG(...) QtLogger::instance().logger()->debug(__VA_ARGS__) #define LOG_INFO(...) QtLogger::instance().logger()->info(__VA_ARGS__) #define LOG_WARN(...) QtLogger::instance().logger()->warn(__VA_ARGS__) #define LOG_ERROR(...) QtLogger::instance().logger()->error(__VA_ARGS__) #define LOG_CRITICAL(...) QtLogger::instance().logger()->critical(__VA_ARGS__) #endif // QTLOGGER_H ``` **文件:qtlogger.cpp** ```cpp #include "qtlogger.h" QtLogger::~QtLogger() { if (m_logger) { spdlog::drop_all(); // 清理所有注册的logger,确保资源释放 } } QtLogger& QtLogger::instance() { static QtLogger instance; return instance; } void QtLogger::init(const std::string& logFilePath) { // 避免重复初始化 if (m_logger) { return; } try { // 创建sink集合 std::vector<spdlog::sink_ptr> sinks; // 控制台输出(带颜色) sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>()); // 文件输出(如果提供了文件路径) if (!logFilePath.empty()) { sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(logFilePath, true)); } // 创建日志记录器 m_logger = std::make_shared<spdlog::logger>("qt_logger", sinks.begin(), sinks.end()); m_logger->set_level(spdlog::level::trace); // 设置日志级别(最低级别,实际输出由sink控制) m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%s:%#] %v"); // 格式:时间、级别、源文件位置、消息 // 注册日志记录器,以便通过spdlog::get获取 spdlog::register_logger(m_logger); // 设置当遇到错误级别(error及以上)时立即刷新 m_logger->flush_on(spdlog::level::err); // 可选:设置全局日志记录器,这样可以直接使用spdlog::info()等(但建议使用我们自己的宏) spdlog::set_default_logger(m_logger); } catch (const spdlog::spdlog_ex& ex) { // 初始化失败,可以输出到stderr std::cerr << "Log initialization failed: " << ex.what() << std::endl; } } ``` ### 步骤2:在Qt项目中初始化日志系统 在`main.cpp`中初始化日志系统: ```cpp #include "qtlogger.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); // 初始化日志系统(输出到控制台和文件) QtLogger::instance().init("my_app.log"); LOG_INFO("Application started. Qt version: {}", qVersion()); // ... 其他初始化代码 return a.exec(); } ``` ### 步骤3:在其他文件中使用日志 在项目的任何源文件中,只需包含`qtlogger.h`,然后使用预定义的宏进行日志记录。 **示例:在某个窗口类中** ```cpp #include "qtlogger.h" #include "mywindow.h" MyWindow::MyWindow(QWidget *parent) : QMainWindow(parent) { LOG_INFO("MyWindow created"); // 示例:在某个方法中 connect(ui->pushButton, &QPushButton::clicked, this, [this]() { LOG_DEBUG("Button clicked in MyWindow"); // ... 其他逻辑 }); } ``` ### 关键点说明 1. **单例模式**:确保整个项目中只有一个日志记录器实例。 2. **宏定义**:使用宏简化用,并避免在每个文件中重复写`QtLogger::instance().logger()->info(...)`。 3. **日志格式**:在初始化时设置日志格式,这里包含了时间、日志级别、源文件路径和行号(`[%s:%#]`),便于定位日志来源。 4. **多文件支持**:由于我们提供了全局可访问的宏,所以在任何包含`qtlogger.h`的文件中都可以直接使用日志功能。 5. **线程安全**:spdlog的日志记录器是线程安全的,因此可以在多线程环境中使用(如Qt的多线程操作)。 ### 高级配置 - **异步日志**:如果担心日志写入影响性能(特别是文件写入),可以启用异步日志。修改`init`函数: ```cpp #include <spdlog/async.h> void QtLogger::init(const std::string& logFilePath) { // ... 前面的代码 // 创建异步日志记录器(替换原来的创建代码) spdlog::init_thread_pool(8192, 1); // 队列大小8192,1个后台线程 m_logger = std::make_shared<spdlog::async_logger>("qt_logger", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block); // ... 后续设置 } ``` - **日志级别控制**:可以添加一个方法来动态设置日志级别: ```cpp void QtLogger::setLevel(spdlog::level::level_enum level) { if (m_logger) { m_logger->set_level(level); } } ``` ### 常见问题解决 1. **编译错误:未找到spdlog头文件** 确保在.pro文件中正确包含spdlog头文件路径: ```qmake INCLUDEPATH += $$PWD/third_party/spdlog/include ``` 2. **日志文件无法写入** 检查应用程序对目标目录的写权限,并确保文件路径正确。 3. **日志记录器未初始化** 确保在用任何日志宏之前,已经用了`QtLogger::instance().init()`(如在main函数中)。 ### 相关问题 1. **如何实现日志级别的动态整?** 可以添加一个方法,在运行时修改日志级别,甚至可以通过网络接口或信号来触发。 2. **如何将日志同时输出到Qt的QTextEdit控件?** 参考之前自定义sink的方法,创建一个`QTextEditSink`并添加到日志记录器的sink列表中。 3. **如何实现日志文件的滚动和分割?** spdlog提供了`rotating_file_sink`(按大小分割)和`daily_file_sink`(按天分割),替换`basic_file_sink`即可。 4. **在多线程环境下使用日志是否安全?** 是的,spdlog的日志记录器是线程安全的,可以放心使用。 5. **如何避免日志记录对程序性能的影响?** 使用异步日志模式,并合理设置队列大小和后台线程数量。 [^1]: Qt中基于spdlog的高效日志管理方案 [^2]: spdlog入门教程 [^3]: spdlog的安装及介绍 [^4]: spdlog的配置 [^5]: 手动创建日志记录器</think>### 在Qt项目中全局配置spdlog实现多文件日志记录 #### 全局日志管理器实现(单例模式) ```cpp // LoggerManager.h #pragma once #include <spdlog/spdlog.h> #include <spdlog/sinks/basic_file_sink.h> #include <spdlog/sinks/stdout_color_sinks.h> #include <memory> class LoggerManager { public: static LoggerManager& instance() { static LoggerManager instance; return instance; } std::shared_ptr<spdlog::logger> getLogger() { return logger; } private: LoggerManager() { try { // 创建组合sink:控制台+文件 auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("app.log", true); std::vector<spdlog::sink_ptr> sinks{console_sink, file_sink}; logger = std::make_shared<spdlog::logger>("global_logger", sinks.begin(), sinks.end()); // 设置日志格式 [时间] [级别] [文件:行号] 消息 logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%s:%#] %v"); logger->set_level(spdlog::level::debug); // 设置默认日志级别 spdlog::register_logger(logger); // 注册全局logger } catch (const spdlog::spdlog_ex& ex) { qFatal("Logger init failed: %s", ex.what()); } } std::shared_ptr<spdlog::logger> logger; }; // 全局访问宏 #define LOG_TRACE(...) LoggerManager::instance().getLogger()->trace(__VA_ARGS__) #define LOG_DEBUG(...) LoggerManager::instance().getLogger()->debug(__VA_ARGS__) #define LOG_INFO(...) LoggerManager::instance().getLogger()->info(__VA_ARGS__) #define LOG_WARN(...) LoggerManager::instance().getLogger()->warn(__VA_ARGS__) #define LOG_ERROR(...) LoggerManager::instance().getLogger()->error(__VA_ARGS__) #define LOG_CRITICAL(...) LoggerManager::instance().getLogger()->critical(__VA_ARGS__) ``` #### 项目配置步骤(.pro文件) ```qmake # 添加spdlog头文件路径 INCLUDEPATH += $$PWD/third_party/spdlog/include # 启用C++11/14 CONFIG += c++14 # 开启异常处理(spdlog需要) CONFIG += exceptions ``` #### 在main.cpp中初始化 ```cpp #include "LoggerManager.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); // 初始化日志系统(自动执行) LoggerManager::instance(); LOG_INFO("====== 应用启动 ======"); LOG_INFO("Qt版本: {} | 编译时间: {}", qVersion(), __TIMESTAMP__); // ... 其他初始化代码 return app.exec(); } ``` #### 在其他文件中使用日志 ```cpp // MyClass.cpp #include "LoggerManager.h" void MyClass::processData() { LOG_DEBUG("开始处理数据"); try { // ... 业务逻辑 LOG_INFO("数据处理完成,结果: {}", result); } catch (const std::exception& e) { LOG_ERROR("数据处理异常: {}", e.what()); LOG_FLUSH(); // 立即刷新日志 } } // NetworkManager.cpp #include "LoggerManager.h" void NetworkManager::onResponseReceived(QNetworkReply* reply) { if(reply->error() != QNetworkReply::NoError) { LOG_WARN("网络请求错误: {} | URL: {}", reply->errorString().toStdString(), reply->url().toString().toStdString()); } else { LOG_DEBUG("收到响应: {}字节", reply->size()); } } ``` #### 关键特性说明 1. **全局访问** 通过单例模式确保所有文件访问同一日志实例[^1] 2. **详细日志格式** 包含时间戳、日志级别、源文件位置和行号: ```text [2023-08-15 14:23:45.678] [info] [MyClass.cpp:25] 数据处理完成 ``` 3. **多Sink支持** 同时输出到控制台(带颜色)和文件[^4] 4. **性能优化** 使用宏封装避免重复用: ```cpp // 原始用 LoggerManager::instance().getLogger()->info("message"); // 宏简化用 LOG_INFO("message"); ``` 5. **异常安全** 在构造函数中捕获初始化异常,防止日志系统崩溃导致应用退出[^3] #### 高级配置选项 ```cpp // 动态修改日志级别 LoggerManager::instance().getLogger()->set_level(spdlog::level::trace); // 添加自定义sink(如网络传输) class NetworkSink : public spdlog::sinks::base_sink<std::mutex> { protected: void sink_it_(const spdlog::details::log_msg& msg) override { // 发送日志到远程服务器 } void flush_() override {} }; auto network_sink = std::make_shared<NetworkSink>(); LoggerManager::instance().getLogger()->sinks().push_back(network_sink); // 启用异步日志(8MB队列,1个后台线程) spdlog::init_thread_pool(8192, 1); auto async_logger = std::make_shared<spdlog::async_logger>("async_log", begin(sinks), end(sinks), spdlog::thread_pool()); ``` #### 常见问题解决 1. **日志文件权限问题** 使用Qt标准路径: ```cpp QString logPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/app.log"; auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(logPath.toStdString()); ``` 2. **跨平台换行符** 统一使用`\n`,spdlog会自动处理平台差异 3. **日志文件过大** 启用滚动日志: ```cpp #include <spdlog/sinks/rotating_file_sink.h> auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>( "app.log", 1024*1024*5, 3); // 5MB大小,保留3个备份 ``` 4. **Qt信号日志** 安全记录信号参数: ```cpp connect(button, &QPushButton::clicked, [](bool checked) { LOG_DEBUG("按钮点击状态: {}", checked); }); ``` --- ### 相关问题 1. **如何实现按模块分级的日志过滤?** 2. **spdlog异步模式在Qt中如何避免内存泄漏?** 3. **如何将qDebug()输出重定向到spdlog?** 4. **在多进程Qt应用中如何共享日志文件?** 5. **如何实现日志的实时远程监控?** [^1]: Qt中基于spdlog的高效日志管理方案 [^2]: spdlog入门教程 [^3]: spdlog的安装及介绍 [^4]: spdlog的配置 [^5]: 手动创建日志记录器
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值