subzero subzero - 4 months ago 14
Objective-C Question

swizzling NSObject's performSelector:withObject: gives EXC_BAD_ACCESS why?

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

CRD CRD
Answer

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

Comments