iOS 中 attribute((constructor)) 修饰的函数
开发环境声明:此文描述的 attribute((constructor)) 特指使用 Objective-C 开发 iOS、MacOS,Swift 语言不支持这种属性修饰符。
初识 attribute((constructor))
在 Objective-C 开发中,attribute((constructor))
是一个 GCC 和 Clang 编译器特性,允许开发者在程序启动时自动执行一些函数。使用这个属性修饰的函数会在 main
函数之前执行,通常用于初始化一些全局状态或者执行一些在程序开始时就需要完成的操作。
使用 attribute((constructor))
这种属性的使用方法很简单,只需要在函数定义前加上 __attribute__((constructor))
修饰符即可。
#import <Foundation/Foundation.h>__attribute__((constructor))
void myCustomInitializer(void) {NSLog(@"This function is called before main");
}int main(int argc, const char * argv[]) {@autoreleasepool {NSLog(@"Hello, World!");}return 0;
}
在上面的代码中,myCustomInitializer
函数会在 main
函数之前执行。当你运行这个程序时,你会看到控制台输出:
This function is called before main
Hello, World!
应用场景
- 全局状态初始化:可以用于初始化一些全局变量或状态,这些变量在程序开始时就需要使用。
- 日志初始化:如果有全局的日志系统,可以在程序启动时进行初始化。
- 注册插件或模块:在应用启动时自动注册一些插件或模块,使其在整个应用生命周期中可用。
注意事项
- 执行顺序:如果有多个使用
attribute((constructor))
修饰的函数,执行顺序是不确定的,因此不要依赖于特定的执行顺序。 - 执行时间:这些函数会在
main
函数之前执行,因此会增加应用的启动时间,要谨慎使用,尽量避免进行耗时操作。 - Objective-C 的使用:虽然可以在 Objective-C 中使用,但要注意和 Objective-C runtime 的初始化顺序相互配合,避免在 Objective-C runtime 尚未完全初始化时进行依赖 Objective-C 特性的操作。
更复杂的例子
下面是一个更复杂的例子,展示了如何使用 attribute((constructor))
初始化一个全局对象:
#import <Foundation/Foundation.h>@interface MyGlobalManager : NSObject
+ (void)initializeManager;
@end@implementation MyGlobalManager+ (void)initializeManager {NSLog(@"MyGlobalManager is initialized");// 全局初始化代码
}@end__attribute__((constructor))
void initializeGlobalManager(void) {[MyGlobalManager initializeManager];
}int main(int argc, const char * argv[]) {@autoreleasepool {NSLog(@"Application is starting");// 应用的其他代码}return 0;
}
运行这个程序,你会看到:
MyGlobalManager is initialized
Application is starting
What about attribute((constructor))
in Swift ?
在 Swift 中,无法直接使用 __attribute__((constructor))
这种 GCC/Clang 特性,因为 Swift 语言不支持这种属性修饰符 (已经在开头声明)。不过,我们可以通过其他方法实现类似的效果,例如使用 @UIApplicationMain
、全局变量的初始化方法、或使用 Objective-C 的方法结合 Swift 来达到在程序启动时执行某些代码的目的。
使用 Swift 实现类似效果
1. 全局变量的初始化
Swift 中全局变量在应用启动时会初始化,可以利用这一特性来执行一些初始化代码。
import Foundationlet initialize: Void = {print("This code runs before main")
}()@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {var window: UIWindow?func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {print("Application did finish launching")return true}
}
在这个例子中,全局变量 initialize
在应用启动时会被初始化,因此其闭包中的代码会在 main
函数之前执行。
2. 使用 Objective-C 桥接
如果需要使用 __attribute__((constructor))
特性,可以在 Objective-C 文件中定义,然后在 Swift 中调用。
Objective-C 部分
创建一个 Objective-C 文件,例如 Initializer.m
:
// Initializer.m
#import <Foundation/Foundation.h>__attribute__((constructor))
static void myConstructor() {NSLog(@"Objective-C constructor is called before main");
}
创建一个桥接头文件,例如 YourProject-Bridging-Header.h
,并在其中导入 Initializer.h
:
// YourProject-Bridging-Header.h
#import "Initializer.h"
Swift 部分
确保在 Xcode 项目的设置中配置了桥接头文件,然后正常使用 Swift 编写应用:
import UIKit@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {var window: UIWindow?func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {print("Application did finish launching")return true}
}
运行这个项目时,控制台会输出:
Objective-C constructor is called before main
Application did finish launching
3. 使用 UIApplicationMain
在 Swift 中,@UIApplicationMain
会自动生成一个 main
函数并处理应用的启动过程,实际上你很少需要显式编写初始化代码。通过在 AppDelegate
中的 application(_:didFinishLaunchingWithOptions:)
方法中进行初始化,也能实现类似效果:
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {var window: UIWindow?func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {// 初始化代码print("Application did finish launching")return true}
}
通过 attribute((constructor)) 实现组件化
通过 __attribute__((constructor))
实现组件化的主要思路是利用这一特性来自动注册或初始化组件,使得每个组件在应用启动时都能自动执行其初始化代码。这在模块化或插件化系统中非常有用,因为它可以自动地将组件注册到系统中,而不需要在应用启动时显式地调用每个组件的初始化代码。
下面详细说明如何通过 __attribute__((constructor))
实现组件化,并提供一个具体的例子。
组件化的基本思路
- 定义一个组件协议:每个组件都需要实现这个协议。
- 组件注册器:一个全局的组件注册器,用来管理所有注册的组件。
- 使用
__attribute__((constructor))
:每个组件通过__attribute__((constructor))
注册到全局注册器中。
具体步骤
1. 定义组件协议
首先,定义一个组件协议(例如 Component
),所有组件都需要实现这个协议:
// Component.h
#import <Foundation/Foundation.h>@protocol Component <NSObject>
@required
- (void)initializeComponent;
@end
2. 组件注册器
创建一个全局的组件注册器,用来管理和调用所有注册的组件:
// ComponentRegistry.h
#import <Foundation/Foundation.h>@interface ComponentRegistry : NSObject
+ (instancetype)sharedInstance;
- (void)registerComponent:(id<Component>)component;
- (void)initializeAllComponents;
@end// ComponentRegistry.m
#import "ComponentRegistry.h"@interface ComponentRegistry ()
@property (nonatomic, strong) NSMutableArray<id<Component>> *components;
@end@implementation ComponentRegistry+ (instancetype)sharedInstance {static ComponentRegistry *sharedInstance = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{sharedInstance = [[self alloc] init];});return sharedInstance;
}- (instancetype)init {if (self = [super init]) {_components = [NSMutableArray array];}return self;
}- (void)registerComponent:(id<Component>)component {[self.components addObject:component];
}- (void)initializeAllComponents {for (id<Component> component in self.components) {[component initializeComponent];}
}@end
3. 定义组件并使用 __attribute__((constructor))
进行注册
每个组件通过 __attribute__((constructor))
将自己注册到组件注册器中:
// MyComponent.h
#import <Foundation/Foundation.h>
#import "Component.h"@interface MyComponent : NSObject <Component>
@end// MyComponent.m
#import "MyComponent.h"
#import "ComponentRegistry.h"@implementation MyComponent- (void)initializeComponent {NSLog(@"MyComponent is initialized");
}__attribute__((constructor))
static void registerMyComponent(void) {MyComponent *component = [[MyComponent alloc] init];[[ComponentRegistry sharedInstance] registerComponent:component];
}@end
4. 在应用启动时初始化所有组件
在 AppDelegate
的 application:didFinishLaunchingWithOptions:
方法中,调用组件注册器的初始化方法:
// AppDelegate.m
#import "AppDelegate.h"
#import "ComponentRegistry.h"@interface AppDelegate ()
@end@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {// Initialize all registered components[[ComponentRegistry sharedInstance] initializeAllComponents];return YES;
}@end
完整的示例
- Component.h
#import <Foundation/Foundation.h>@protocol Component <NSObject>
@required
- (void)initializeComponent;
@end
- ComponentRegistry.h
#import <Foundation/Foundation.h>
#import "Component.h"@interface ComponentRegistry : NSObject
+ (instancetype)sharedInstance;
- (void)registerComponent:(id<Component>)component;
- (void)initializeAllComponents;
@end
- ComponentRegistry.m
#import "ComponentRegistry.h"@interface ComponentRegistry ()
@property (nonatomic, strong) NSMutableArray<id<Component>> *components;
@end@implementation ComponentRegistry+ (instancetype)sharedInstance {static ComponentRegistry *sharedInstance = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{sharedInstance = [[self alloc] init];});return sharedInstance;
}- (instancetype)init {if (self = [super init]) {_components = [NSMutableArray array];}return self;
}- (void)registerComponent:(id<Component>)component {[self.components addObject:component];
}- (void)initializeAllComponents {for (id<Component> component in self.components) {[component initializeComponent];}
}@end
- MyComponent.h
#import <Foundation/Foundation.h>
#import "Component.h"@interface MyComponent : NSObject <Component>
@end
- MyComponent.m
#import "MyComponent.h"
#import "ComponentRegistry.h"@implementation MyComponent- (void)initializeComponent {NSLog(@"MyComponent is initialized");
}__attribute__((constructor))
static void registerMyComponent(void) {MyComponent *component = [[MyComponent alloc] init];[[ComponentRegistry sharedInstance] registerComponent:component];
}@end
- AppDelegate.m
#import "AppDelegate.h"
#import "ComponentRegistry.h"@interface AppDelegate ()
@end@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {// Initialize all registered components[[ComponentRegistry sharedInstance] initializeAllComponents];return YES;
}@end
要点总结
__attribute__((constructor))
是一个非常有用的特性,允许开发者在程序启动时自动执行一些初始化操作。不过在使用时要注意性能影响和执行顺序,避免对应用启动时间产生负面影响。它适用于需要在应用启动时进行一些全局初始化工作的场景,但要避免在这些函数中进行耗时的操作。
虽然 Swift 本身不支持 __attribute__((constructor))
,但我们可以通过全局变量初始化、使用 Objective-C 桥接以及在 AppDelegate
中进行初始化来实现类似的效果。这些方法在实际开发中都非常实用,并且可以满足绝大多数初始化需求。
通过 __attribute__((constructor))
特性,可以在应用启动时自动注册和初始化各个组件,从而实现模块化和插件化的效果。这种方法简化了组件的初始化流程,使代码更具模块化和可扩展性。不过,在使用这种技术时,要注意性能影响和组件的初始化顺序问题,以确保应用能稳定、高效地启动。
相关文章:
iOS 中 attribute((constructor)) 修饰的函数
开发环境声明:此文描述的 attribute((constructor)) 特指使用 Objective-C 开发 iOS、MacOS,Swift 语言不支持这种属性修饰符。 初识 attribute((constructor)) 在 Objective-C 开发中,attribute((constructor)) 是一个 GCC 和 Clang 编译器…...

原生js实现图片预览控件,支持丝滑拖拽,滚轮放缩,放缩聚焦
手撸源代码如下:注释应该很详细了,拿去直用 可以放到在线编辑器测试,记得修改图片路径 菜鸟教程在线编辑器 <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" conten…...

C语言入门课程学习笔记9:指针
C语言入门课程学习笔记9 第41课 - 指针:一种特殊的变量实验-指针的使用小结 第42课 - 深入理解指针与地址实验-指针的类型实验实验小结 第43课 - 指针与数组(上)实验小结 第44课 - 指针与数组(下)实验实验小结 第45课 …...
借助 Cloudflare D1 和 Drizzle 在 Astro 上实现全栈
使用 Cloudflare D1 和 Drizzle ORM 将后端添加到 Astro 项目的分步指南 文章目录 安装 Astro添加 Cloudflare 适配器部署到 Pages安装 wrangler 并登录创建 D1 数据库创建 wrangler.toml 文件将 .wrangler 添加到 .gitignore更新 astro.config.ts安装 Drizzle 依赖项创建 driz…...

SUSE linux 15的网络管理
1 手工配置网络 wicked提供了一种新的网络配置框架。自SUSE 12起,SUSE使用了新的网络管理工具wicked,这个是区别与其他常见发行版的。常见的发行版目前大多使用的是NetworkManager服务进行网络管理。 1.1 wicked网络配置 传统网络接口管理面临的挑战之…...

海康威视-下载的录像视频浏览器播放问题
目录 1、播放异常比对 2、视频编码检查 2.1、正常视频解析 2.2、海康视频解析 2.3、比对工具 3、转码 3.1、maven依赖 3.2、实现代码 4、验证 在前面的文章(海康威视-按时间下载录像文件_海康威视 sdk 下载录像 大小0-CSDN博客)中,通…...

养殖自动化管理系统:开启智慧养殖新篇章
在现代农业的快速演进中,养殖业正经历一场前所未有的技术革命。养殖自动化管理系统,作为这场变革的前沿科技,正逐步成为推动行业高效、环保、可持续发展的关键力量。本文将深入探讨自动化养殖系统如何通过精准管理、智能监控、数据驱动决策&a…...

SmartEDA革新来袭:融合Multisim与Proteus精髓,引领电子设计新纪元!
在电子设计领域,每一次技术的革新都如同春风化雨,滋润着设计师们的心田。今天,我们迎来了一个划时代的电子设计自动化(EDA)工具——SmartEDA,它不仅融合了业界知名的Multisim和Proteus的精华,更…...
【FFmpeg】AVFormatContext结构体
【FFmpeg】AVFormatContext结构体 1.AVFormatContext结构体1.2 const struct AVInputFormat *iformat1.3 const struct AVOutputFormat *oformat 参考: FFMPEG结构体分析:AVFormatContext 示例工程: 【FFmpeg】调用ffmpeg库实现264软编 【FF…...

【SpringSecurity】认证与鉴权框架SpringSecurity——授权
目录 权限系统的必要性常见的权限管理框架SpringSecurity授权基本流程准备脚本限制访问资源所需权限菜单实体类和Mapper封装权限信息封装认证/鉴权失败处理认证失败封装鉴权失败封装配置SpringSecurity 过滤器跨域处理接口添加鉴权hasAuthority/hasAnyAuthorityhasRole/ hasA…...
深入解析FTP:原理、架构与搭建方式
在当今互联网世界中,文件传输是日常工作和生活中不可或缺的一部分。FTP(File Transfer Protocol,文件传输协议)作为一种老而弥坚的协议,一直在文件传输领域发挥着重要作用。本文将从技术人的角度,详细分析F…...
Springboot与RestTemplate
RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。 一、使用Get进行访问 1、获取json格式 使用 getForEntity() API 发起 GET 请求: RestTemplate restTemplate…...
端口发布与暴露
端口发布与暴露 目录 发布端口发布到临时端口发布所有端口试一试 使用 Docker CLI使用 Docker Compose 如果你一直在跟随本指南,你应该理解容器为应用程序的每个组件提供了隔离的进程。每个组件 - 如 React 前端、Python API 和 Postgres 数据库 - 都运行在自己的…...
Unity:使用Texture2D动态创建的图像无法正常显示 / 修改图像后未生效
开发中遇到需要动态绘制图像的需求,前后文代码如下所示: Texture2D newImageTexture new Texture2D(width, height); Color32[] newImagePixels new Color32[height * width];for (int y 0; y < height ; y) {for (int x 0; x < width; x){if…...
【LinuxC语言】详解TCP/IP
文章目录 前言TCP与UDP协议的介绍TCP协议流式传输TCP的三次握手连接TCP的四次挥手连接断开总结前言 在我们的日常生活中,无论是浏览网页,还是发送电子邮件,甚至是在线视频聊天,都离不开网络通信。而在网络通信中,TCP和UDP协议起着至关重要的作用。本文将以通俗易懂的语言…...
数字化转型下的企业人力资源信息系统研究
随着数字化转型的加速,企业人力资源管理面临着全新的挑战和机遇。传统的人力资源信息系统(HRIS)在新时代的要求下必须进行深刻的革新和升级,以更好地支持企业的发展战略和员工的需求。 数据驱动的决策支持 在当今这个信息化迅猛发…...
docker camunda 8.5 部署步骤
Camunda Platform 8 环境准备 Docker 版本要求 Docker 20.10.16 is required; docker compose version 1.27.0.;github 开源地址:https://github.com/camunda/camunda-platformcamunda7 文档地址:https://docs.camunda.org/manual/7.21/user-guide/process-engine/社区地址: …...
学懂C#编程:常用高级技术——委托(Delegate)应用场景——委托与Lambda表达式的结合使用详解
在C#中,委托与Lambda表达式的结合使用是现代编程实践中的一个重要且强大的特性,它极大地提高了代码的简洁性和可读性。下面将详细讲解这两个概念如何协同工作,以及如何在实际编程中有效利用它们。 委托基础 委托是C#中的一种引用类型&#x…...

05-java基础——循环习题
循环的选择:知道循环的次数或者知道循环的范围就使用for循环,其次再使用while循环 猜数字 程序自动生成一个1-100之间的随机数,在代码中使用键盘录入去猜出这个数字是多少? 要求:使用循环猜,一直猜中为止…...

网络安全等级保护测评
网络安全等级保护 《GB17859 计算机信息系统安全保护等级划分准则》 规定计算机信息系统安全保护等级共分五级 《中华人民共和国网络安全法》 “国家实行网络安全等级保护制度。 等级测评 测评机构依据国家网络安全等级保护制度规定,按照有关 管理规范和…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...