问题表现
在 Swift 2.x 版本中,注册 JLRoutes 时调用下边的方法,在 Route URL 时会崩溃:
+ (void)addRoute:(NSString *)routePattern handler:(BOOL (^__nullable)(NSDictionary<NSString *, NSString *> *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 <A> (Swift.AnyObject, A.Type) -> A + 385
frame #3: 0x000000010ce63b80 libswiftFoundation.dylib`static (extension in Foundation):Swift.Dictionary.(_forceBridgeFromObjectiveC (__ObjC.NSDictionary, result : inout Swift.Optional<Swift.Dictionary<A, B>>) -> ()).(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<Swift.Dictionary<A, B>>) -> ()).(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<Swift.Dictionary<A, B>>) -> () + 558
frame #8: 0x000000010ce4f87b libswiftFoundation.dylib`Foundation._convertNSDictionaryToDictionary <A, B where A: Swift.Hashable> (Swift.Optional<__ObjC.NSDictionary>) -> Swift.Dictionary<A, B> + 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
里的 NSDictionary
的 key
和 value
都被限定为了 NSString
类型。
而在真正执行 Route 操作时,方法
+ (BOOL)routeURL:(NSURL *)URL withController:(JLRoutes *)routesController parameters:(NSDictionary *)parameters executeBlock:(BOOL)executeBlock
中对参数的解析,却犯了两个错误:
-
kJLRouteURLKey
对应的value
解析成了NSURL
类型(代码是finalParameters[kJLRouteURLKey] = URL;
) -
kJLRouteNamespaceKey
对应的value
有可能会被解析成NSNull
,对应的代码是finalParameters[kJLRouteNamespaceKey] = strongParentRoutesController.namespaceKey ?: [NSNull null];
,
从而导致在 didRoute = route.block(finalParameters);
代码调用中, block
传入的 finalParameters
的类型与之前注册传入的 Swift 参数类型不一致,发生了崩溃。
解决方法
解决方法有两种,
- 将
handlerBlock
的字典类型的NSString
限制去掉,这种方法需要作者修改源码。GitHub 上已经有开源的修改了。 - 不修改源码的情况下,我们可以用 OC 语言添加一个中间层,将 1 中修改后的方法,作为中间层的类方法,该方法的实现里,直接调用 JLRoutes 的方法。然后在 Swift 代码中,用中间层来注册 Route。
建议可以先采用 2 的方式,等作者修改后,再去掉这个中间层即可。