objective-c how to get private Method lists - ios

as the title,I want to know the private method list of an objective-c class. such as "_resetViewController" for UIviewcontroller.

Look at objc/runtime.h, in particular the class_copyMethodList() function and the method_* functions that go with it.
Or if you're looking for a developer tool and not trying to write one, search for class-dump.
Note: Do not ship applications that call private methods, stick to the ones that are documented. Many of these methods may just be a convenience for the developer implementing the public methods, and they will change them when they make additions to the public code. They may remove the methods entirely, or just change the type of a parameter, and if your app calls them, it will suddenly crash, or corrupt memory and make something else crash 5 hours later and you'll have no idea why it is crashing.

I try to use runtime log the method list as follow:
- (void)testPrivateMethod {
Class clz = [self class];
unsigned int methodCount = 0;
Method *methods = class_copyMethodList(clz, &methodCount);
printf("Found %d methods on '%s'\n", methodCount,
class_getName(clz));
for (unsigned int i = 0; i < methodCount; i++) {
Method method = methods[i];
printf("\t'%s' has method named '%s' of encoding '%s'\n",
class_getName(clz),
sel_getName(method_getName(method)),
method_getTypeEncoding(method));
}
free(methods);
}
but,I just got three method as follow:
Found 3 methods on 'HGMThirdViewController'
'HGMThirdViewController' has method named 'testPrivateMethod' of encoding 'v16#0:8'
'HGMThirdViewController' has method named 'viewDidLoad' of encoding 'v16#0:8'
'HGMThirdViewController' has method named 'didReceiveMemoryWarning' of encoding 'v16#0:8'

There are a few regularly updated repositories containing the headers - here's a good one (for academic purposes only of course 😉): https://github.com/nst/iOS-Runtime-Headers
As #uliwitness mentioned, it is dangerous using private APIs in production code.

Related

No known class method for selector 'simplePingWithHostName: SimplePing iOS Objective C

Hello I am having trouble using SimplePing I receive the following error when copying over the SimplePing.h and SimplePing.m files I downloaded from Apple SimplePing
No known class method for selector 'simplePingWithHostName:'
From this method in my SimplePingClient.m class:
-(void)pingHostname:(NSString*)hostName andResultCallBlock:(void(^)(NSString* latency))result
{
_resultCallback = result;
_pingClient = [SimplePing simplePingWithHostName:hostName]; // No known class method for selector 'simplePingWithHostName:'
_pingClient.delegate = self;
[_pingClient start];
}
I'm calling this method from another file like this, where there are no errors:
-(void)testNetworkLatency {
[SimplePingClient pingHostname:#"www.apple.com"
andResultCallback:^(NSString *latency) {
NSLog(#"your latency is: %#", latency ? latency : #"unknown");
}];
}
I tried changing [SimplePing simplePingWithHostName... in SimplePingClient.m to variations of pingHostname, initWithHostName, sendPingWithData but nothing has worked so far. Did something change with SimplePing or am I going wrong somewhere?
Link to a screenshot of all the methods available in SimplePing.h as you can see, there is no simplePingWithHostName
it is wise to read the class definitions before coping and pasting but once done you can take them for granted because thats what they are for. You will have much more fun when following them. But keep on trying as this is a classic beginner mistake in objC when taking over language concepts from elsewhere but not objC.
You will want to set a global variable to keep your simple ping object around.
#implementation YourClass {
SimplePing *_pingClient;
}
alternative in
#interface YourClass : YourInheritingClass <NeverGiveUpProtocol>
#property (nonatomic) SimplePing *pingClient;
#end
and in your implementation method allocation and initiation of your ping object is done like
_pingClient = [[SimplePing alloc] initWithHostName:hostName];
Also you will have to be careful with the definition of block parameters that are doing nothing and are set up to accept NSStrings. You can read about block definitions in methods here
https://riptutorial.com/objective-c/example/1761/blocks-as-method-parameters
You can tell the compiler that you don't need a specific parameter like so ..
if you cant avoid to define it.
-(void)yourmethod:(NSString*)hostname resultBlock:(void(^)())result {
#pragma unused(result)
// your stuff
}
Hint: SimplePing and SimplePingClient seem to be total different classes where the last one contains pingHostname: as external method. (Means you can call it without explicit initiation.) Ending up in a method definition like..
+(void)pingHostname:(NSString *)hostname; which is why you would need to call it directly on the ClassName.
The Apple example does not contain a class named SimplePingClient.
Thats probably result of your creative process. Yeah maybe you want to ping from the app you create to the same app on a different device - well, why not. Cheers

Different result for objective-c syntax and C syntax

I have the following method section
- (NSString*) GetPathForFolder:(int)folder inDomains:(int) domains {
id cls = objc_getClass("NSFileManager");
SEL defaultManagerSelector = #selector(defaultManager);
SEL urlsForDirectoryInDomainsSelector = #selector(URLsForDirectory:inDomains:);
id fileMangerHandle = objc_msgSend(cls, defaultManagerSelector);
//NSArray<NSURL *>* notUsedArray = [fileMangerHandle URLsForDirectory:folder inDomains:domains];
NSArray<NSURL *>* resultArray = (NSArray<NSURL *>*) objc_msgSend(fileMangerHandle, urlsForDirectoryInDomainsSelector, folder, domains);
return [resultArray lastObject].absoluteString;
}
Calling this method with [self GetPathForFolder:5 inDomains:1]
returns file:///Applications/ which is wrong
The moment I uncomment the NSArray<NSURL *>* notUsedArray.. line I get a different and correct value which just happens to be the same as the one from
What does the objective-c version of the call do that I'm not doing in my C version?
Update:
I'm using objc_msgSend because this method will eventually be called from C# but it's just easier to first try it in objective-c and than start worrying about the interop part.
I was using sel_registerName because when running this inside of C#, I'm going to have to do my own registration.
More about Interop between C# and objective-c here. And also a java version of what I'm trying to understand is here.
You can’t use objc_msgSend like this, it is not a variadic function, even though it’s prototype suggests it. The compiler needs to use the same calling convention that the method is expecting. For this it needs to know the exact types of all parameters. You can tell it by casting objc_msgSend to a function pointer type with the correct parameters.
So for this case you would use something like this:
typedef NSArray<NSURL *> *(*UrlsForDirectoryType)(id, SEL, NSUInteger, NSUIteger);
NSArray<NSURL *>* resultArray = ((UrlsForDirectoryType)objc_msgSend)(fileMangerHandle, urlsForDirectoryInDomainsSelector, folder, domains);
The typedef is optional, of course, but then that makes the whole thing even harder to read.
To expose this to a different programming language you could also write regular C functions in Objective-C and call those. This is much easier to do than dealing with the runtime directly.

Access to iOS SDK constant via name (reflection)

Inside of iOS SDK, lots of constants defined by Apple can be found looking something like this:
extern const CFStringRef kSomeReallyNiceConstant
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_8_0);
If I check for presence of this constant standard way:
if (NULL == &kSomeReallyNiceConstant)
I am pretty much referencing it and in order for my code to compile properly, I need at least iOS SDK 8.0 or higher in this case.
When it comes to objects and methods, reflection approach works nicely with usage of NSClassFromString, respondsToSelector and performSelector.
Is there a chance to use some kind of reflection (access to string constant by name) in attempt to get it's value if it exists (or none if it doesn't)?
I know that I can use macros to check for iOS version and execute different code paths based on that information, but I don't want to use that approach.
I managed to do this with pointer:
#include <dlfcn.h>
// ...
int *pointer = dlsym(RTLD_SELF, "kSomeReallyNiceConstant");
if (pointer) {
NSLog(#"Thing exists!");
} else {
NSLog(#"Nope, doesn't exist!");
}
but I am not sure if this is something that would cause app rejection. Do you maybe know?
Regardless of this pointer approach, I'm curious to hear if there's any other way to achieve this?
Nothing better than suggested solution found on this topic.
#include <dlfcn.h>
// ...
int *pointer = dlsym(RTLD_SELF, "kSomeReallyNiceConstant");
if (pointer) {
NSLog(#"Thing exists!");
} else {
NSLog(#"Nope, doesn't exist!");
}

Dynamically implementing a delegate during runtime

In my class, I have a reference on an UIViewController and want to implement a delegate on this ViewController during runtime. The delegate has only one method (with two parameters) and when the delegate-method on the ViewController is invoked, my class should handle the call.
I am quite sure this is possible with some kind of method swizzling, etc. but I don't know how to accomplish this.
What you want is possible, but it's not method swizzling, since you don't want to switch to methods but add a new one. It can be done, thanks to Objective-C's dynamic nature, but it's still a dirty hack so also file a feature request with the library vendor.
What you want is class_addMethod() and a C function with the actual implementation for that. One more thing, Objective-C methods are C methods, but with two implicit parameters, self and _cmd, which have to keep in mind (both when creating your C method and when telling class_addMethod your methods signature. And here is an SSCE of how to pull something like that off:
#import <Foundation/Foundation.h>
#import <objc/runtime.h> // Required for class_addMethod()
#interface MyClass : NSObject
#end
#implementation MyClass
#end
#protocol MyProtocol <NSObject>
- (void)printString:(NSString *)string;
#end
// Note the method signature containing the
// two implicit parameters self and _cmd!
void MyClassPrinStringIMP(id self, SEL _cmd, NSString *string)
{
NSLog(#"Hi I'm %#:%s and this is the string: %#", self, sel_getName(_cmd), string);
}
void PimpMyClass()
{
// The last argument is the signature. First character is the return type, in our case void
// Then comes self and _cmd, followed by the NSString. You can use #encode() to find out how your
// type is encoded. Best is to build this string at runtime, since the encoding can change with architectures
class_addMethod([MyClass class], #selector(printString:), (IMP)MyClassPrinStringIMP, "v#:#");
}
int main(int argc, const char * argv[])
{
#autoreleasepool
{
PimpMyClass();
id foo = [[MyClass alloc] init]; // id, to silence the compiler!
[foo printString:#"Hello World"];
}
return 0;
}
Example output:
Hi I'm <MyClass: 0x100101810>:printString: and this is the string: Hello World
Edit: Something that you may find is that the passed object is checked at runtime wether it conforms to a protocol or not using conformsToProtocol:. Since this code just adds the method implementation, it would still fail, but you can tell the runtime that you totally do implement that protocol with this one function call:
class_addProtocol([MyClass class], #protocol(MyProtocol));
Alternative: proxies
Objective-Cs dynamism and message forwarding is already praised by #JasperBlues, however, there is one particular class in Objective-C that is designed to do just that: NSProxy. It is designed to intercept sent messages and dispatching them dynamically to the relevant target, and does use the high-level NSInvocation approach. If you can pass a proxied object in some way as the delegate (depending on what your code allows for and what not), creating a NSProxy subclass might be the cleanest way to go.
However, note though that you then end up with a shim object that wraps over your other object, which comes with its own bag of pain and will break when you try to directly access variables via -> syntax. It's not a perfectly invisible proxy, but good enough for most cases.
Firstly, some comments indicate that what you're asking is instantly "a bad thing to do" or a "dirty hack". I disagree here. Most modern Object Oriented languages support these features, and they are used to good effect by numerous system-level frameworks. Of course it is human-nature to perhaps use these dynamic features where they're not really required (for fun or practice), even when a simpler approach would work fine. Beware of this.
Objective-C is admirable in that its somewhat of a legacy language and close to the "bare metal", and yet features a surprising level of dynamism, making it relatively easy to support these requirements without any external libraries or frameworks.
Besides using the class_addMethod guide that another answer correctly indicates, some other approaches are:
Message Forwarding: (recommended)
All NSObject sub-classes have the ability to forward a method that they're not able to respond to, to another target object. This is similar to the lower-level concept of trampolines. Apple publishes a guide on using this approach.
The advantages of using forward invocation is that it uses the NSInvocation level of abstraction, instead of directly calling the C ObjC runtime API. This abstracts the following details away:
Structs and primitives will be box/unboxed automatically
Dispatching to methods with a dynamic/unknown number of arguments becomes easy. Until arm64, this could be done using va_args, however on arm64 va_args can be copied directly to registers, and not popped off the stack.
Resolve Instance Method:
Instance methods are created by by registering a C function as the implementation to respond to a given message. This can be done neatly with blocks, using IMP_ImplementationWithBlock:
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
IMP imp = imp_implementationWithBlock((__bridge id) objc_unretainedPointer(
^(id me, BOOL firstParam, NSString* secondParam)
{
//Implementation goes in here
return something; //something of type 'id'
}));
class_addMethod(self, sel, imp, "##:");
return YES;
}
return NO;
}
Using libffi:
Libffi can also do this kind of thing, though it should not be necessary if you're using pure Objective-C, as the runtime already has these features baked in.

Initializing object with non property methods in Typhoon

I'm new to DI and Typhoon in particular. I'm wondering if its possible to initialize an object with methods other than init methods and properties.
I have a class called ObjectMapper, an ObjectMapper can have N number of ObjectMaps. Before using typhoon, I would create the maps like so:
ObjectMap *map1 = [ObjectMap new];
[map1 mapProperty:#"prop1" toName:#"name1"];
[map1 mapProperty:#"prop2" toName:#"name2"];
ObjectMap *map2 = [ObjectMap new];
[map2 mapProperty:#"prop3" toName:#"name3"];
mapper.maps = #[map1, map2];
The maps and the mapper object never change throughout the lifetime of the application. I would like to create the ObjectMapper and the ObjectMaps in Typhoon.
Update: It seems that a TyphoonFactoryProvider might help but I can't figure out how to place object created by the factory into the 'maps' array.
If you are ready for risk, you can try in-development version of Typhoon which support method injections. (Still undocumented yet, but seems to work)
-(id) mappedComponent
{
return [TyphoonDefinition withClass:[ObjectMap class] injections:^(TyphoonDefinition *definition) {
[definition injectMethod:#selector(mapProperty:toName:) withParameters:^(TyphoonMethod *method) {
[method injectParameterWith:#"property"];
[method injectParameterWith:#"name"];
}];
}];
}
TyphoonFactoryProvider is not going to help you here - this (advanced) class just provides a clean way to obtain an instance where some of the initializer-arguments or properties aren't known until run-time. . normally here you'd either:
Obtain the instance and then configure it with the runtime known args in two separate steps
Create a custom factory
TyphoonFactoryProvider just writes the custom factory code for you, as well as handles some memory management details. (lazy dependencies). Its useful for eg: transitioning from one view controller to another.
If I understand you, what you're trying to do is not directly possible with Typhoon. However, you could always inject an object instance (configuration info) along with an afterPropertyInjection callback to finish off. Example:
-(id) mappedComponent
{
return [TyphoonDefinition withClass:[MyType class] properties:^(TyphoonDefinition* definition)
{
// Any object. This time an NSDictionary using Objc literals shorthand
[definition injectProperty:#selector(mappings) withObjectInstance:#{
#"prop1" : #"name1",
#"prop2" : #"name2",
#"prop3" : #"name3"
}];
//This can be a category method if you don't "own" the class in question. The method puts the object in the required state, using the config data provided.
definition.afterPropertyInject = #selector(myConfigMethod)];
}];
}

Resources