Question

I'm trying to swizzle NSObject's performSelector:withObject: rentzsch's swizzling library, here is the code:

#import "NSObject+xxxx.h"
#import "JRSwizzle.h"

@implementation NSObject (xxxx)
+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSError *error;
        BOOL result = [[self class] jr_swizzleMethod:@selector(performSelector:withObject:) withMethod:@selector(xxxx_performSelector:withObject:) error:&error];
        if (!result || error) {
            NSLog(@"Can't swizzle methods - %@", [error description]);
        }else{
            NSLog(@"done");
        }      
    });
}

- (id)xxxx_performSelector:(SEL)aSelector withObject:(id)object{
    NSLog(@"called %@ %@",NSStringFromSelector(aSelector), [object description]);
    return [self xxxx_performSelector:aSelector withObject:object]; // when running it, Xcode stops and highlights this line
}

@end

My question is, why it gives me EXC_BAD_ACCESS when I'm running it?.
Note, it always fails in the same point. And also, I'm just tracing the arguments (no other magic).

Here is what I get in the console:

2014-04-01 17:36:40.368 xxxApp debug[7322:90b] done
2014-04-01 17:36:44.704 xxxApp debug[7322:90b] called layoutSublayersOfLayer: <CALayer: 0xcd75650>
2014-04-01 17:36:45.536 xxxApp debug[7322:90b] called layoutSublayersOfLayer: <CALayer: 0xcd76240>
2014-04-01 17:36:45.955 xxxApp debug[7322:90b] called layoutSublayersOfLayer: <CALayer: 0xcd76550>
2014-04-01 17:36:46.170 xxxApp debug[7322:90b] called layoutSublayersOfLayer: <CALayer: 0xcd7bce0>
2014-04-01 17:36:46.470 xxxApp debug[7322:90b] called layoutSublayersOfLayer: <UIWindowLayer: 0xcd78140>

Here is the backtrace:

(lldb) bt
* thread #1: tid = 0x7bd49, 0x01e81de5 libobjc.A.dylib`objc_retain + 21, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x56e58965)
    frame #0: 0x01e81de5 libobjc.A.dylib`objc_retain + 21
  * frame #1: 0x000099f0 MyApp debug`-[NSObject(self=0x0f8432a0, _cmd=0x022bd138, aSelector=0x0083aae4, object=0x0f8424f0) xxxx_performSelector:withObject:] + 256 at NSObject+xxxx.m:44
    frame #2: 0x007e745a QuartzCore`-[CALayer layoutSublayers] + 148
    frame #3: 0x007db244 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 380
    frame #4: 0x007e73a5 QuartzCore`-[CALayer layoutIfNeeded] + 160
    frame #5: 0x00a55ae3 UIKit`-[UIViewController window:setupWithInterfaceOrientation:] + 304
    frame #6: 0x0096baa7 UIKit`-[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:isRotating:] + 5212
    frame #7: 0x0096a646 UIKit`-[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:] + 82
    frame #8: 0x0096a518 UIKit`-[UIWindow _setRotatableViewOrientation:updateStatusBar:duration:force:] + 117
    frame #9: 0x0096a5a0 UIKit`-[UIWindow _setRotatableViewOrientation:duration:force:] + 67
    frame #10: 0x0096963a UIKit`__57-[UIWindow _updateToInterfaceOrientation:duration:force:]_block_invoke + 120
    frame #11: 0x0096959c UIKit`-[UIWindow _updateToInterfaceOrientation:duration:force:] + 400
    frame #12: 0x0096a2f3 UIKit`-[UIWindow setAutorotates:forceUpdateInterfaceOrientation:] + 870
    frame #13: 0x0096d8e6 UIKit`-[UIWindow setDelegate:] + 449
    frame #14: 0x00a47b77 UIKit`-[UIViewController _tryBecomeRootViewControllerInWindow:] + 180
    frame #15: 0x00963474 UIKit`-[UIWindow addRootViewControllerViewIfPossible] + 591
    frame #16: 0x009635ef UIKit`-[UIWindow _setHidden:forced:] + 312
    frame #17: 0x0096386b UIKit`-[UIWindow _orderFrontWithoutMakingKey] + 49
    frame #18: 0x0096e3c8 UIKit`-[UIWindow makeKeyAndVisible] + 65
    frame #19: 0x0091ebc0 UIKit`-[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 2097
    frame #20: 0x00923667 UIKit`-[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 824
    frame #21: 0x00937f92 UIKit`-[UIApplication handleEvent:withNewEvent:] + 3517
    frame #22: 0x00938555 UIKit`-[UIApplication sendEvent:] + 85
    frame #23: 0x00925250 UIKit`_UIApplicationHandleEvent + 683
    frame #24: 0x0341af02 GraphicsServices`_PurpleEventCallback + 776
    frame #25: 0x0341aa0d GraphicsServices`PurpleEventCallback + 46
    frame #26: 0x02130ca5 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 53
    frame #27: 0x021309db CoreFoundation`__CFRunLoopDoSource1 + 523
    frame #28: 0x0215b68c CoreFoundation`__CFRunLoopRun + 2156
    frame #29: 0x0215a9d3 CoreFoundation`CFRunLoopRunSpecific + 467
    frame #30: 0x0215a7eb CoreFoundation`CFRunLoopRunInMode + 123
    frame #31: 0x00922d9c UIKit`-[UIApplication _run] + 840
    frame #32: 0x00924f9b UIKit`UIApplicationMain + 1225
    frame #33: 0x00002997 MyApp debug`main(argc=1, argv=0xbfffedac) + 135 at main.m:16
Was it helpful?

Solution

Short answer:

Your problem is ARC, you should be able to fix it by adding -fno-objc-arc to just this file in your project config (Build Phases > Compile Sources > Compiler Flags) Also make sure you set error to nil in load before you swizzle - that happens automatically under ARC.

Explanation:

ARC needs to know the return type of a method call, and the ownership of the returned value, so that it can manage the reference appropriately. This causes a problem with performSelector:withObject as selectors in Objective-C are untyped and so ARC cannot know the return type. The method is declared to return id, but with an appropriate selector may in fact return void - ARC will attempt to retain the return value, and doing that when nothing has in fact been returned doesn't work too well as the return value in that case is essentially garbage.

Turning off ARC for just this one file has no significant impact, your xxxx_performSelector:withObject: requires no manual memory management calls.

HTH

OTHER TIPS

Disabling ARC should indeed be a quick fix to your problem, but a better solution could be found by taking extra care of the value you want to return.

ARC tends to retain the return value of a swizzled function, which causes a EXC_BAD_ACCESS in case non-objC instances are returned (or sometimes even on void). See the second footnote of https://blog.newrelic.com/2014/04/16/right-way-to-swizzle/, that explains this issue in longer details, and that gives you a proper solution (basically, casting the return value so that the compiler knows it should not retain it).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top