美文网首页
iOS 路由方案演进:实现去中心化的自动注册机制

iOS 路由方案演进:实现去中心化的自动注册机制

作者: 林君毅小号_001 | 来源:发表于2025-04-21 17:42 被阅读0次

在日常开发中,我们常常需要为每个功能页面分配独立的路由。传统做法通常有两种方案:

  1. 集中式管理路由:实现一个统一的路由类(如 URLHandler),在其中集中注册所有路由。新功能上线时,需要手动向该类中添加路由逻辑。
  2. 模块化管理路由:为每个功能实现一个对应的路由类(如 FunNewURLHandler),再将该类名添加到 plist 或模块静态数组中,统一加载。

第一种方案随着功能数量增长,URLHandler 会变得臃肿且耦合度高,维护难度大。第二种方式虽然更合理,但仍然存在一些问题:

  • 管理中心化,需要开发者主动注册;
  • 会增加类的数量,影响启动速度;
  • 注册步骤繁琐,若遗漏注册路径(如 plist 或模块静态数组),可能导致路由不生效。

那么是否有一种“去中心化”的方案,使得每个功能页面只需定义自己的路由及处理逻辑,无需额外注册操作,App 运行时可自动完成注册?

这正是本文将介绍的实现方式。


利用 Mach-O 特性实现自动注册

通过如下方式,我们可以在编译阶段将数据写入 Mach-O 文件的指定段中:

__attribute__((used, section("__DATA,__urlhandle")))
  • used:防止 Release 环境下被链接器优化掉;
  • section:指定写入位置,这里是__DATA 段下的 __urlhandle 区域。

1. 路由结构体定义

typedef void(*UrlHandleImp)(NSURL*, UINavigationController*, NSDictionary*);

struct URL_HANDLE {
    const char *url;
    UrlHandleImp handle;
};

url:存储路由路径;
handle:指向路由触发时调用的函数指针。

通过 __attribute__ 声明,将路由所对应的 static struct URL_HANDLE写入指定 section。App 运行时,使用 dyld 懒加载读取,构建路由表,并添加至 FBURLRouter中。

2. 路由注册宏定义

为了方便注册路由,定义了一套宏:

#define URL_EXPORT(...) \
    URL_EXPORT_WITH_LINE_CODE(__LINE__, __VA_ARGS__)

#define URL_EXPORT_WITH_LINE_CODE(line, ...) \
    static void URL_EXPORT_IMPL_NAME(line)(NSURL *, UINavigationController *, NSDictionary *); \
    url_macro_concat(URL_EXPORT_, url_macro_argcount(__VA_ARGS__))(line, __VA_ARGS__) \
    static void URL_EXPORT_IMPL_NAME(line)(NSURL *url, UINavigationController *navi, NSDictionary *parameters)

多参数展开示例(最多支持10个参数):

#define URL_EXPORT_10(line, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
    URL_EXPORT_9(line, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
    URL_EXPORT_EACH(line, _10, 10)

每个url被展开为:

#define URL_EXPORT_EACH(line, router, suffix) \
    __attribute__((used, section(FB_URL_EXPORT_SEGMENT "," URL_EXPORT_SECTION))) \
    static const struct URL_HANDLE URL_EXPORT_VAR_NAME(line, suffix) = { \
        (const char *)((char[sizeof(format_to_c_conststring(router))]){format_to_c_conststring(router)}), \
        URL_EXPORT_IMPL_NAME(line), \
    };

为了同时支持 NSString* 和 const char*:

#define format_to_c_conststring(a) \
(__builtin_choose_expr( \
    __builtin_types_compatible_p(__typeof__(a), NSString*), \
    #a, \
    a \
))

format_to_c_conststring("c")     //展开为 "c"
format_to_c_conststring(@"c")    //展开为 "@/"c/""

//  对于NSString转化的c字符串,读取时需要进行处理
NSString *url =  [NSString stringWithUTF8String:entry.url];
if ([url hasPrefix:@"@\""] && [url hasSuffix:@"\""] && url.length >= 3) {
    url = [url substringWithRange:NSMakeRange(2, url.length-3)];
}

URL_EXPORT 最多支持10个参数,宏使用__LINE__自动生成唯一函数和变量名,所以在同一源文件中可多次调用 URL_EXPORT

3. 路由加载流程

  • 使用URL_EXPORT在编译期间将路由注入到 __urlhandlesection中
  • 在 App 启动时将AutoRegURLHandler注册到 FBURLRouter
  • 为了避免影响启动速度,AutoRegURLHandler不立即构建路由,延迟至调用 +patterns 时;
  • 构建路由时,使用 dyld 读取 __urlhandle section中所有的URL_HANDLE结构体,构建对应的 _InnerURLHandler并加入 _RegisterHandlers,通知添加路由至 _RegisterURLs
  • 我们忽略了系统的动态库,仅处理 .app 下的动态库,同时支持通过修改 EXCLUDE_IMAGE_FOR_URL_HANDLE 忽略不必要模块(目前默认跳过 SDK 与 dylib)。

使用示例

单路由注册
URL_EXPORT(@"/setting") {
    SettingViewController *settingVC = [[SettingViewController alloc] init];
    [navi pushViewController:settingVC animated:YES];
}
带参数的路由注册
URL_EXPORT("/{courseId}/paramPage") {
    NSString *courseId = parameters[@"courseId"];
    NSString *param1 = parameters[@"param1"];
    NSString *param2 = parameters[@"param2"];
    ParamViewController *vc = [[ParamViewController alloc] initWithCourseId:courseId param1:param1 param2:param2];
    vc.hidesBottomBarWhenPushed = YES;
    [navi pushViewController:vc animated:YES];
}
多路由注册
#define FuncPageURL_1 @"/func1"
#define FuncPageURL_2 "/func2"
#define FuncPageURL_3 "/func3"

URL_EXPORT(FuncPageURL_1, FuncPageURL_2) {
    if ([url.absoluteString containsString:FuncPageURL_1]) {
        // 转到 func 页面 1
    } else if ([url.absoluteString containsString:FuncPageURL_2]) {
        // 转到 func 页面 2
    }
}
URL_EXPORT(FuncPageURL_3) {
    // 转到 func 页面 3
}

路由校验机制

AutoRegURLHandler 提供校验方法,用于检查:

  • 路由是否注册成功;
  • 是否存在重复路由;
Debug 环境下使用:
#ifdef DEBUG
    NSString *errorString = nil;
    BOOL urlValid = [AutoRegURLHandler checkValid:&errorString];
    NSAssert(urlValid, errorString);
#endif

📎 项目地址

完整实现与 Demo 示例:
👉 https://github.com/linjunyi/URLHandlerDemo

相关文章

  • gateway 路由

    配置 引入相关依赖 配置路由策略 根据注册中心eureka的serviceId实现自动路由策略 enabled 是...

  • 基于AST的组件化自动插桩方案

    本文将带你实现一个一百多行代码实现的自动化插桩方案,解决组件化子模块的初始化和路由器的自动注册,支持多种类型的插桩...

  • iOS - fastlane自动化打包

    iOS自动化打包,我推荐fastlane 方案。fastlane 的原理又是如何?实现自动化打包,又有什么流程? ...

  • iOS系统架构

    1: 滴滴出行iOS客户端架构演进之路 2: iOS应用架构谈 组件化方案 3:iOS组件化方案调研 4: 饿了么...

  • VUE去中心化的路由实现

    在做VUE时,很多时候把路由写在同一个路由文件里,这样在项目越来越大时,该文件需要多次修改,这样很不方便。现在需要...

  • 路由中间件koa-router的使用

    注册 实例化 添加路由 加载路由中间件 allowedMethods()的作用 自动设置ctx.status,以及...

  • Spring Cloud-1.服务治理

    服务治理 服务治理用来实现各个微服务示例的自动化注册与发现。 服务治理围绕“服务注册”和“服务发现”机制来完成对微...

  • iOS 自动化测试 AirTest+wdaproxy(或按键精灵

    iOS 自动化测试 AirTest+wdaproxy(或按键精灵?!) iOS自动化测试的方案有很多,但是很多都要...

  • iOS创建自己的pod库

    一、背景 目前iOS组件化常用的方案就是Pod+路由——持续集成,今天我们来创建一个Pod库。 二、注册用户名和邮...

  • iOS-创建自己的pod库

    一、背景 目前iOS组件化常用的方案就是Pod+路由——持续集成,今天我们来创建一个Pod库。 二、注册用户名和邮...

网友评论

      本文标题:iOS 路由方案演进:实现去中心化的自动注册机制

      本文链接:https://www.haomeiwen.com/subject/pbvvbjtx.html