鍍金池/ 問答/iOS/ 為什么會崩潰?

為什么會崩潰?

代碼如下:

+ (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [NSURLConnection class];
        SEL selector = @selector(initWithRequest:delegate:);
        SEL swizzledSelector = @selector(test_initWithRequest:delegate:);
        Method originalinitWithRequest = class_getInstanceMethod(class, selector);
        NSURLConnection* (^swizzleBlock)(NSURLConnection *,SEL,NSURLRequest*,id )= ^(NSURLConnection *slf,SEL swizzledSelector,NSURLRequest *request,id delegate) {
            return ((NSURLConnection*(*)(NSURLConnection*,SEL,NSURLRequest*,id))objc_msgSend)(slf,swizzledSelector,request,delegate);
            
        };
        
        IMP implementation = imp_implementationWithBlock(swizzleBlock);
        class_addMethod(class, swizzledSelector, implementation, method_getTypeEncoding(originalinitWithRequest));
        Method newConnectionWithRequestClassMethod = class_getInstanceMethod(class, swizzledSelector);
        method_exchangeImplementations(originalinitWithRequest, newConnectionWithRequestClassMethod);
    });
}

我看了半天沒發(fā)現(xiàn)什么問題,為什么這樣一交換之后運行就會崩潰。
在return 時候打斷點,信息如下:

(lldb) po slf
<NSURLConnection: 0x60000000dea0> { request: (null) }

(lldb) po swizzledSelector
<NSURLRequest: 0x60000000e020> { URL: http://httpstat.us/200 }

(lldb) po request
<ViewController: 0x7fdd4bf03fe0>

(lldb) po delegate
 nil

崩潰信息如下:

2017-12-19 15:09:25.923 HookNetwork2[4030:501650] *** NSForwarding: warning: selector (0x60000000e020) for message 'X?ü' does not match selector known to Objective C runtime (0x608000004340)-- abort
2017-12-19 15:09:25.924 HookNetwork2[4030:501650] (null): unrecognized selector sent to instance 0x60000000dea0
2017-12-19 15:09:25.928 HookNetwork2[4030:501650] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '(null): unrecognized selector sent to instance 0x60000000dea0'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010926fb0b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x0000000108cd4141 objc_exception_throw + 48
    2   CoreFoundation                      0x00000001092df134 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
    3   CoreFoundation                      0x00000001091f6840 ___forwarding___ + 1024
    4   CoreFoundation                      0x00000001091f63b8 _CF_forwarding_prep_0 + 120
    5   HookNetwork2                        0x00000001086fc379 __20+[RAObserverer load]_block_invoke_2 + 153
    6   HookNetwork2                        0x00000001086fd6a3 -[ViewController connectTest:] + 1731
    7   UIKit                               0x0000000109694d82 -[UIApplication sendAction:to:from:forEvent:] + 83
    8   UIKit                               0x00000001098195ac -[UIControl sendAction:to:forEvent:] + 67
    9   UIKit                               0x00000001098198c7 -[UIControl _sendActionsForEvents:withEvent:] + 450
    10  UIKit                               0x0000000109818802 -[UIControl touchesEnded:withEvent:] + 618
    11  UIKit                               0x00000001097027ea -[UIWindow _sendTouchesForEvent:] + 2707
    12  UIKit                               0x0000000109703f00 -[UIWindow sendEvent:] + 4114
    13  UIKit                               0x00000001096b0a84 -[UIApplication sendEvent:] + 352
    14  UIKit                               0x0000000109e945d4 __dispatchPreprocessedEventFromEventQueue + 2926
    15  UIKit                               0x0000000109e8c532 __handleEventQueue + 1122
    16  CoreFoundation                      0x0000000109215c01 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    17  CoreFoundation                      0x00000001091fb0cf __CFRunLoopDoSources0 + 527
    18  CoreFoundation                      0x00000001091fa5ff __CFRunLoopRun + 911
    19  CoreFoundation                      0x00000001091fa016 CFRunLoopRunSpecific + 406
    20  GraphicsServices                    0x000000010d044a24 GSEventRunModal + 62
    21  UIKit                               0x0000000109693134 UIApplicationMain + 159
    22  HookNetwork2                        0x00000001086ff2bf main + 111
    23  libdyld.dylib                       0x000000010c0d965d start + 1
    24  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

前面的代碼哪里有問題,為什么斷點信息是這樣的?
這里有一個crash demo,可以下載下來看一看。

回答
編輯回答
安淺陌

建議閱讀一下imp_implementationWithBlock的文檔。

Parameters

block
The block that implements this method. The signature of block should be method_return_type ^(id self, self, method_args …). The selector of the method is not available to block. block is copied with Block_copy().

你的代碼里面的block的類型里面是需要了一個seletor, 打斷點的得知,此時swizzle selector的值是一個NSURLRequest,明顯不是我們想要的selector,所以objc_msgSend就失敗了。

查閱文檔可知,block的簽名應該是method_return_type ^(id self, self, method_args …).是不需要SEL的,所以將上述的代碼改為:


    id swizzleBlock = ^(NSURLConnection *slf, NSURLRequest *request,id delegate) {
        return ((NSURLConnection*(*)(NSURLConnection*,SEL,NSURLRequest*,id))objc_msgSend)(slf,swizzledSelector,request,delegate);
    };

運行可以成功,點擊按鈕也OK了。

2017年10月5日 16:43
編輯回答
深記你

遇到問題找不到原因時可以多看看官方文檔,說不定會有思路。

2017年3月17日 18:03
編輯回答
朕略傻

那個RAObserverer是什么東西?代碼里沒有,奔潰調(diào)用棧里看起來跟上面代碼沒關系。

需要知道這兩句是干了什么,從調(diào)用??词?code>RAObserverer沒有load方法的實現(xiàn)

5   HookNetwork2                        0x00000001086fc379 __20+[RAObserverer load]_block_invoke_2 + 153
6   HookNetwork2                        0x00000001086fd6a3 -[ViewController connectTest:] + 1731

關鍵:imp_implementationWithBlock這個block的參數(shù)里是沒有SEL的

2017年5月26日 15:28