5 Replies
      Latest reply on Aug 26, 2019 8:26 AM by iphonegamedeveloper
      iphonegamedeveloper Level 1 Level 1 (0 points)

        Is there a way to access a method with 4 parameters which is not declared in header file?

        • Re: Accessing private methods
          QuinceyMorris Level 8 Level 8 (6,040 points)

          The "normal" way of handling cases like these (when, for example, there's no performSelector variant to help you) is to use NSInvocation to construct an object you can use to invoke the method:

           

          https://developer.apple.com/documentation/foundation/nsinvocation

           

          Using NSInvocation is not especially easy. You need a pretty good understanding of the Obj-C runtime (general principles, at least), and a lot of patience to chip away at the problem to get it right. With luck you might find a tutorial out there on the internetz somewhere.

           

          Or, you could, you know, declare the method in a header file.

           

          What's the use-case here?

            • Re: Accessing private methods
              eskimo Apple Staff Apple Staff (11,835 points)

              The "normal" way of handling cases like these … is to use NSInvocation

              Personally, I try to avoid NSInvocation wherever possible.  Instead I do this by defining a function type for the method’s signature (including its two hidden parameters) and then casting objc_msgSend to that function type and calling that.  There’s an example of this in the Cast Objective-C Messages in Proper Form section of Managing Functions and Function Pointers.

              There is a gotcha here, namely ‘abnormal’ return types.  If the method returns a structure or a floating point value, you have to use the appropriate objc_msgSend variant (objc_msgSend_stret, objc_msgSend_fpret).


              Oh, wait, if you’re trying to call a limited number of method selectors, you can just define those methods in a category on the class and call them directly.


              IMPORTANT Do not do any of this for Apple classes in product code.  That puts you on the path to binary compatibility problems in the future.

              Share and Enjoy

              Quinn “The Eskimo!”
              Apple Developer Relations, Developer Technical Support, Core OS/Hardware
              let myEmail = "eskimo" + "1" + "@apple.com"

                • Re: Accessing private methods
                  iphonegamedeveloper Level 1 Level 1 (0 points)

                  1. Will Apple reject if NSInvocation is used?

                  2. I tried Managing Functions and Function Pointers but I couldn't use as one of the parameter is a block for call back.

                   

                      typedef void (^Handler)(NSDictionary *info, NSError *error);

                       id target = self;

                      SEL selector = @selector(test:string2:string3:);

                      typedef void (*MethodType)(id, SEL, id, id, id);

                       Handler handler;

                      MethodType methodToCall = (MethodType)[target methodForSelector:selector];

                      methodToCall(target, selector, @(1), @(2), handler);

                   

                   

                  - (void)test:(NSString*)string1 string2:(NSString*)string2 handler:(Handler)handler{

                     handler(nil, nil);

                  }

                   

                  3) I tried category as well. It didn't work as the category method is not declared in header file.

                    • Re: Accessing private methods
                      eskimo Apple Staff Apple Staff (11,835 points)

                      1. Will Apple reject if NSInvocation is used?

                      NSInvocation is a public API, and using it is not grounds for rejection in and of itself.  However, using it to call private methods in Apple frameworks is definitely grounds for rejection.  And even if you’re not shipping via the App Store, using private methods can lead to binary compatibility problems in the future.  Don’t do this.

                      WARNING Do not do any of this for Apple classes in product code.

                      2. I tried Managing Functions and Function Pointers but I couldn't use as one of the parameter is a block for call back.

                      It’s hard to say what’s going on here without more context.  Pasted in below is a full program that implements my take on your code:

                      @import Foundation;
                      @import ObjectiveC;
                      
                      @interface Main : NSObject
                      
                      @end
                      
                      @implementation Main
                      
                      typedef void (^Handler)(NSDictionary *info, NSError *error);
                      
                      - (void)run {
                          typedef void (*MethodType)(id, SEL, NSString *, NSString *, Handler);
                          MethodType methodToCall = (MethodType) objc_msgSend;
                          methodToCall(
                              self,
                              @selector(testWithString1:string2:handler:),
                              @"s1",
                              @"s2",
                              ^(NSDictionary *info, NSError *error) {
                                  NSLog(@"handler called, info: %@, error: %@", info, error);
                              }
                          );
                      }
                      
                      - (void)testWithString1:(NSString *)string1 string2:(NSString *)string2 handler:(Handler)handler {
                          handler(@{ @"a": string1, @"b": string2 }, nil);
                      }
                      
                      @end
                      
                      int main(int argc, char **argv) {
                          #pragma unused(argc)
                          #pragma unused(argv)
                          @autoreleasepool {
                              [[[Main alloc] init] run];
                          }
                          return EXIT_SUCCESS;
                      }

                      Running it here (macOS 10.14.6, Xcode 10.3) it prints:

                      2019-08-23 09:48:30.561572+0100 xxot[86465:7744413] handler called, info: {
                          a = s1;
                          b = s2;
                      }, error: (null)

                      3. I tried category as well. It didn't work as the category method is not declared in header file.

                      I’m not sure what you mean by this.  The purpose of the category is to declare methods, so if it doesn’t declare a method then you can solve that by adding a method to the category.

                      Share and Enjoy

                      Quinn “The Eskimo!”
                      Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                      let myEmail = "eskimo" + "1" + "@apple.com"

                        • Re: Accessing private methods
                          iphonegamedeveloper Level 1 Level 1 (0 points)

                          Defining a function type for the method’s signature solved the problem. Thanks!  In regards to adding a method to the category, it didn't work as I didn't delcare the method as public (in .h file) i.e method testWithString1:string2:handler: is not declared in .h of the implementation class(.m), so I couldn't call the private methed declared in Category Class.