JLRoutes 与 Swift 结合使用遇到的一个 Bug

问题表现

在 Swift 2.x 版本中,注册 JLRoutes 时调用下边的方法,在 Route URL 时会崩溃:

+ (void)addRoute:(NSString *)routePattern handler:(BOOL (^__nullable)(NSDictionary *parameters))handlerBlock;

在网上已经有人反馈这个问题了,issue 在这里。目前官方还没有解决这个问题。

原因分析

通过崩溃日志分析(引用 issue )

thread #1: tid = 0xbbf578, 0x000000010cb4855f libswiftCore.dylib`swift_bridgeNonVerbatimFromObjectiveC + 255, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
frame #0: 0x000000010cb4855f libswiftCore.dylib`swift_bridgeNonVerbatimFromObjectiveC + 255
frame #1: 0x000000010cb43f70 libswiftCore.dylib`deallocateBuffer value witness for Swift.LazyMapCollection + 16
frame #2: 0x000000010ce63da1 libswiftFoundation.dylib`Swift._forceBridgeFromObjectiveC  (Swift.AnyObject, A.Type) -> A + 385
frame #3: 0x000000010ce63b80 libswiftFoundation.dylib`static (extension in Foundation):Swift.Dictionary.(_forceBridgeFromObjectiveC (__ObjC.NSDictionary, result : inout Swift.Optional>) -> ()).(closure #1) + 144
frame #4: 0x000000010ce60624 libswiftFoundation.dylib`partial apply forwarder for static (extension in Foundation):Swift.Dictionary.(_forceBridgeFromObjectiveC (__ObjC.NSDictionary, result : inout Swift.Optional>) -> ()).(closure #1) + 100
frame #5: 0x000000010bd681e6 CoreFoundation`__65-[__NSDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:]_block_invoke + 102
frame #6: 0x000000010bd680af CoreFoundation`-[__NSDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:] + 159
frame #7: 0x000000010ce4fb4e libswiftFoundation.dylib`static (extension in Foundation):Swift.Dictionary._forceBridgeFromObjectiveC (__ObjC.NSDictionary, result : inout Swift.Optional>) -> () + 558
frame #8: 0x000000010ce4f87b libswiftFoundation.dylib`Foundation._convertNSDictionaryToDictionary  (Swift.Optional<__ObjC.NSDictionary>) -> Swift.Dictionary + 75
frame #9: 0x00000001046ba40d Imgur`thunk + 77 at Routing.swift:0
frame #10: 0x0000000108461954 JLRoutes`+[JLRoutes routeURL:withController:parameters:executeBlock:](self=JLRoutes, _cmd="routeURL:withController:parameters:executeBlock:", URL="aaaaaa -- mgur://imgur.com", routesController=0x00007f8b14d20ae0, parameters=0x0000000000000000, executeBlock=YES) + 2740 at JLRoutes.m:551

和调试可以发现,上文中注册方法的参数 handlerBlock 里的 NSDictionarykeyvalue 都被限定为了 NSString类型。
而在真正执行 Route 操作时,方法
+ (BOOL)routeURL:(NSURL *)URL withController:(JLRoutes *)routesController parameters:(NSDictionary *)parameters executeBlock:(BOOL)executeBlock
中对参数的解析,却犯了两个错误:

  1. kJLRouteURLKey 对应的 value 解析成了 NSURL 类型(代码是 finalParameters[kJLRouteURLKey] = URL;
  2. kJLRouteNamespaceKey 对应的 value 有可能会被解析成 NSNull,对应的代码是 finalParameters[kJLRouteNamespaceKey] = strongParentRoutesController.namespaceKey ?: [NSNull null];

从而导致在 didRoute = route.block(finalParameters); 代码调用中, block 传入的 finalParameters 的类型与之前注册传入的 Swift 参数类型不一致,发生了崩溃。

解决方法

解决方法有两种,

  1. handlerBlock 的字典类型的 NSString 限制去掉,这种方法需要作者修改源码。GitHub 上已经有开源的修改了。
  2. 不修改源码的情况下,我们可以用 OC 语言添加一个中间层,将 1 中修改后的方法,作为中间层的类方法,该方法的实现里,直接调用 JLRoutes 的方法。然后在 Swift 代码中,用中间层来注册 Route。

建议可以先采用 2 的方式,等作者修改后,再去掉这个中间层即可。

你可能感兴趣的:(JLRoutes 与 Swift 结合使用遇到的一个 Bug)