David David - 1 year ago 59
Objective-C Question

Type of self and delegate for casting to fix objc_msgSend wrong number of arguments error?

I have an iPad app that I wrote a few years ago using Greg Coombs’ IntelligentSplitViewController which allows one to produce a layout similar to the rotatable UITabBarController in an iPhone app. The recent release of iOS 10 broke this in a way that has previously been described for an earlier version on this list: objc_msgSend gives an error message that there are a finite number of arguments whereas it is expecting 0.

The answer to this by @rintaro was to perform a type cast to another method (typed_msgSend) to use instead of objc_msgSend. In response to the other poster’s code with two arguments this was:

id (*typed_msgSend)(id, SEL) = (void *)objc_msgSend;

Greg’s code is a little more complex, and given my weakness in objective-C I am having difficulty doing something similar to his code. (Greg, unfortunately, has been ill and has ceased supporting this.) The details are as follows:

- (void)willRotate:(NSNotification*)notification
if (UIInterfaceOrientationIsPortrait(toOrientation))
UIPopoverController *popover = [super valueForKey:@"_hiddenPopoverController"];
objc_msgSend(theDelegate, @selector(splitViewController:willHideViewController:withBarButtonItem:forPopoverController:), self, master, button, popover);

So the question is what are the data types of the six arguments? My attempts were:

  1. theDelegate — unsure — tried ‘id’ or ‘id<UISplitViewControllerDelegate>’ (from its declaration)

  2. @selector... — SEL (by analogy with answer)

  3. self — really don’t know — tried ‘id’

  4. master — UIViewController* (from declaration)

  5. button — UIBarButtonItem* (from declaration)

  6. popover — UIPopoverController* (from declaration)

This builds, but crashes in main after the splash screen. It may, of course, be crashing for some other reason, but unless I am sure that I have done the type cast right, I can’t know. (How I wish this were Java.)

Answer Source
void (*my_objc_msgSend)(id, SEL, id, id, id, id) = (__typeof(my_objc_msgSend))objc_msgSend;  
my_objc_msgSend(theDelegate, @selector(splitViewController:willHideViewController:withBarButtonItem:forPopoverController:), self, master, button, popover);

in objc, id is a point of struct. any object (which class inherit from NSObject) can use id instead.

please post the log if still crash.

After iOS 9, splitViewController:willHideViewController:withBarButtonItem:forPopoverController: is deprecated, you should use splitViewController:willChangeToDisplayMode: instead.