Objective-C 路由表原理详解

发布于:2025-07-04 ⋅ 阅读:(16) ⋅ 点赞:(0)

在 Objective-C 中实现路由表是组件化架构的核心,它通过 URL 映射机制实现模块间解耦通信。以下是完整实现原理:

一、核心架构设计

匹配
执行
未匹配
URL请求
路由表
处理模块
目标组件
错误处理
注册中心

二、路由表实现原理

1. 路由条目类设计

// RouteEntry.h
@interface RouteEntry : NSObject
@property (nonatomic, copy) NSString *pattern;       // 路由模式
@property (nonatomic, copy) id (^handler)(NSDictionary *params); // 处理闭包
@property (nonatomic, strong) NSArray<NSString *> *keys; // 参数键名
@property (nonatomic, assign) NSInteger priority;    // 匹配优先级
@end

// RouteEntry.m
@implementation RouteEntry
@end

2. 路由表核心类

// Router.h
@interface Router : NSObject

+ (instancetype)sharedRouter;

- (void)registerPattern:(NSString *)pattern 
               handler:(id (^)(NSDictionary *params))handler;

- (void)registerPattern:(NSString *)pattern 
               handler:(id (^)(NSDictionary *params))handler 
             priority:(NSInteger)priority;

- (id)openURL:(NSString *)url;
- (id)openURL:(NSString *)url withParams:(NSDictionary *)additionalParams;

@end

3. 路由注册实现

// Router.m
@implementation Router {
    NSMutableDictionary<NSString *, RouteEntry *> *_staticRoutes;
    NSMutableArray<RouteEntry *> *_dynamicRoutes; // 存储动态路由
}

+ (instancetype)sharedRouter {
    static Router *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[Router alloc] init];
    });
    return instance;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _staticRoutes = [NSMutableDictionary dictionary];
        _dynamicRoutes = [NSMutableArray array];
    }
    return self;
}

- (void)registerPattern:(NSString *)pattern 
               handler:(id (^)(NSDictionary *params))handler 
             priority:(NSInteger)priority {

                 // 解析模式中的参数
                 NSMutableArray *keys = [NSMutableArray array];
                 NSString *transformedPattern = [self transformPattern:pattern keys:keys];

                 RouteEntry *entry = [[RouteEntry alloc] init];
                 entry.pattern = pattern;
                 entry.handler = handler;
                 entry.keys = [keys copy];
                 entry.priority = priority;

                 // 静态路由(无参数)
                 if (keys.count == 0) {
                     _staticRoutes[pattern] = entry;
                 } 
                 // 动态路由(含参数)
                 else {
                     [_dynamicRoutes addObject:entry];
                     // 按优先级排序
                     [_dynamicRoutes sortUsingComparator:^NSComparisonResult(RouteEntry *e1, RouteEntry *e2) {
                         return [@(e2.priority) compare:@(e1.priority)];
                     }];
                 }
             }

4. URL 匹配算法

- (id)openURL:(NSString *)url withParams:(NSDictionary *)additionalParams {
    // 1. 尝试精确匹配静态路由
    RouteEntry *staticEntry = _staticRoutes[url];
    if (staticEntry) {
        return staticEntry.handler(additionalParams ?: @{});
    }

    // 2. 动态路由匹配
    NSURLComponents *components = [NSURLComponents componentsWithString:url];
    NSString *path = components.path;

    for (RouteEntry *entry in _dynamicRoutes) {
        // 将模式转换为正则表达式
        NSString *regexPattern = [self regexPatternForRoute:entry.pattern];
        NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexPattern 
                                      options:0 
                                      error:nil];

        NSArray *matches = [regex matchesInString:path 
                            options:0 
                            range:NSMakeRange(0, path.length)];

        if (matches.count > 0) {
            NSTextCheckingResult *match = matches[0];
            NSMutableDictionary *params = [NSMutableDictionary dictionary];

            // 提取路径参数
            for (int i = 0; i < entry.keys.count; i++) {
                NSString *key = entry.keys[i];
                NSRange range = [match rangeAtIndex:i+1];
                if (range.location != NSNotFound) {
                    NSString *value = [path substringWithRange:range];
                    params[key] = value;
                }
            }

            // 添加查询参数
            for (NSURLQueryItem *item in components.queryItems) {
                params[item.name] = item.value;
            }

            // 合并额外参数
            if (additionalParams) {
                [params addEntriesFromDictionary:additionalParams];
            }

            return entry.handler(params);
        }
    }

    // 3. 未找到匹配路由
    [self handleUnmatchedURL:url];
    return nil;
}

5. 模式转换方法

- (NSString *)regexPatternForRoute:(NSString *)routePattern {
    // 转换模式如 /user/:id 为正则表达式
    NSString *pattern = [routePattern stringByReplacingOccurrencesOfString:@":" 
                         withString:@""];

    // 处理通配符
    pattern = [pattern stringByReplacingOccurrencesOfString:@"*" 
               withString:@".*"];

    // 添加正则捕获组
    NSRegularExpression *paramRegex = [NSRegularExpression regularExpressionWithPattern:@":(\\w+)" 
                                       options:0 
                                       error:nil];

    __block NSString *result = routePattern;
    __block NSInteger offset = 0;

    [paramRegex enumerateMatchesInString:routePattern 
     options:0 
     range:NSMakeRange(0, routePattern.length) 
     usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop) {

         NSRange fullRange = [match rangeAtIndex:0];
         NSRange keyRange = [match rangeAtIndex:1];

         // 调整位置偏移量
         fullRange.location += offset;
         keyRange.location += offset;

         NSString *replacement = [NSString stringWithFormat:@"([^/]+)"];
         result = [result stringByReplacingCharactersInRange:fullRange 
                  withString:replacement];

         offset += replacement.length - fullRange.length;
     }];

    // 添加边界匹配
    return [NSString stringWithFormat:@"^%@$", result];
}

三、路由匹配优先级系统

路由请求
是否精确匹配?
返回精确路由
动态路由匹配
按优先级排序
高优先级 完全限定路径
中优先级 命名参数
低优先级 通配符

四、企业级优化策略

1. 路由缓存机制

// 在Router类中添加
@property (nonatomic, strong) NSCache *routeCache;

- (id)cachedRouteForURL:(NSString *)url {
    if (!_routeCache) {
        _routeCache = [[NSCache alloc] init];
        _routeCache.countLimit = 100; // 限制缓存条目数
    }

    return [_routeCache objectForKey:url];
}

- (void)cacheRouteResult:(id)result forURL:(NSString *)url {
    if (result && url) {
        [_routeCache setObject:result forKey:url];
    }
}

// 在openURL方法中使用
- (id)openURL:(NSString *)url withParams:(NSDictionary *)additionalParams {
    // 先检查缓存
    id cachedResult = [self cachedRouteForURL:url];
    if (cachedResult) {
        return cachedResult;
    }

    // ...匹配逻辑

    // 缓存结果
    if (result) {
        [self cacheRouteResult:result forURL:url];
    }

    return result;
}

2. 路由守卫系统

// Router.h
typedef BOOL (^RouteGuardHandler)(NSString *url, NSDictionary **params);

- (void)addGlobalGuard:(RouteGuardHandler)guard;
// Router.m
@implementation Router {
    NSMutableArray<RouteGuardHandler> *_globalGuards;
}

- (void)addGlobalGuard:(RouteGuardHandler)guard {
    if (guard) {
        [_globalGuards addObject:guard];
    }
}

- (BOOL)executeGuardsForURL:(NSString *)url params:(NSDictionary **)params {
    for (RouteGuardHandler guard in _globalGuards) {
        NSDictionary *tempParams = *params;
        if (!guard(url, &tempParams)) {
            return NO;
        }
        *params = tempParams;
    }
    return YES;
}

// 在openURL方法中调用
- (id)openURL:(NSString *)url withParams:(NSDictionary *)additionalParams {
    // ...匹配逻辑

    // 执行路由守卫
    if (![self executeGuardsForURL:url params:&finalParams]) {
        return nil; // 守卫拦截
    }

    // ...继续执行
}

3. 路由表调试工具

- (void)printAllRoutes {
    NSLog(@"===== Static Routes =====");
    [_staticRoutes enumerateKeysAndObjectsUsingBlock:^(NSString *key, RouteEntry *entry, BOOL *stop) {
        NSLog(@"%@ -> %@", key, entry.pattern);
    }];

    NSLog(@"===== Dynamic Routes =====");
    for (RouteEntry *entry in _dynamicRoutes) {
        NSLog(@"%@ (priority: %ld)", entry.pattern, (long)entry.priority);
    }
}

- (BOOL)isRouteRegistered:(NSString *)pattern {
    if (_staticRoutes[pattern]) {
        return YES;
    }

    for (RouteEntry *entry in _dynamicRoutes) {
        if ([entry.pattern isEqualToString:pattern]) {
            return YES;
        }
    }

    return NO;
}

五、使用示例

1. 注册路由

// App启动时注册路由
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    Router *router = [Router sharedRouter];

    // 注册用户详情页
    [router registerPattern:@"/user/profile/:userId" handler:^id(NSDictionary *params) {
        NSString *userId = params[@"userId"];
        UserProfileVC *vc = [[UserProfileVC alloc] initWithUserId:userId];
        [self.navigationController pushViewController:vc animated:YES];
        return vc;
    } priority:10];

    // 注册设置页
    [router registerPattern:@"/settings" handler:^id(NSDictionary *params) {
        SettingsVC *vc = [[SettingsVC alloc] init];
        [self presentViewController:vc animated:YES completion:nil];
        return vc;
    } priority:5];

    return YES;
}

2. 使用路由跳转

// 在需要跳转的地方
- (void)showUserProfile:(NSString *)userId {
    Router *router = [Router sharedRouter];
    NSString *url = [NSString stringWithFormat:@"myapp://user/profile/%@", userId];

    // 带额外参数
    NSDictionary *params = @{@"source": @"home_page"};
    [router openURL:url withParams:params];
}

3. 处理深度链接

// 处理AppDelegate中的深度链接
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url 
            options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {

                Router *router = [Router sharedRouter];
                return [router openURL:url.absoluteString] != nil;
            }

六、性能优化策略

优化技术 实现方式 性能提升
正则预编译 注册时编译正则表达式 减少70%匹配时间
路由缓存 使用NSCache缓存结果 减少60%匹配计算
优先级排序 高优先级路由前置 平均减少50%匹配尝试
守卫短路 守卫失败时提前终止 减少不必要的处理

七、特殊场景处理

1. 通配符路由

// 注册通配符路由
[router registerPattern:@"/files/*" handler:^id(NSDictionary *params) {
    NSString *filePath = params[@"wildcard"];
    FileViewer *viewer = [[FileViewer alloc] initWithFilePath:filePath];
    return viewer;
} priority:1];

2. 多参数路由

// 注册多参数路由
[router registerPattern:@"/search/:category/:keyword" handler:^id(NSDictionary *params) {
    NSString *category = params[@"category"];
    NSString *keyword = params[@"keyword"];
    SearchResultsVC *vc = [[SearchResultsVC alloc] initWithCategory:category keyword:keyword];
    return vc;
} priority:8];

3. 路由重定向

// 实现重定向
[router registerPattern:@"/old/path" handler:^id(NSDictionary *params) {
    // 重定向到新路径
    [[Router sharedRouter] openURL:@"/new/path"];
    return nil;
} priority:3];

八、路由表 vs 其他通信方式

特性 路由表 通知中心 委托模式
解耦程度 ★★★★★ ★★★☆☆ ★★☆☆☆
类型安全 ★★☆☆☆ ★☆☆☆☆ ★★★★★
跨组件通信 ★★★★★ ★★★★★ ★★☆☆☆
可维护性 ★★★☆☆ ★★☆☆☆ ★★★★☆
深度链接支持 ★★★★★ ☆☆☆☆☆ ☆☆☆☆☆

九、最佳实践建议

路由命名规范

// 使用反向DNS风格
@"com.yourapp.module.action"
// 或RESTful风格
@"/user/{id}/profile"

路由版本管理

// v1路由
[router registerPattern:@"/v1/user/profile" ...];

// v2路由
[router registerPattern:@"/v2/user/profile" ...];

路由文档自动化

// 使用注释生成文档
/// ROUTE: /user/profile
/// DESC: 显示用户详情页
/// PARAMS: userId - 用户ID

路由监控系统

// 统计路由使用情况
- (id)openURL:(NSString *)url withParams:(NSDictionary *)additionalParams {
    [Analytics trackEvent:@"route_open" properties:@{@"url": url}];
    // ...原有逻辑
}

路由表是 Objective-C 组件化架构的核心基础设施,合理设计路由系统可以大幅提升代码的可维护性和扩展性,同时为深度链接、动态化等高级功能提供基础支持。