I have a dylib which has a object of class "mConWifi". I have the main app which loads this dylib and executes following code
Class klass = objc_getClass("mConWifi");
SEL sel = sel_getUid("ListAllWifi:");
if ( [klass respondsToSelector:sel] )
objc_msgSend(klass, sel);
When above code is called, object of class mConWifi is already created in Memory.
My objective is to get object based on class name and then invoke a method. With above code I am not able to as respondsToSelector fails. I have already tried "ListAllWifi" and "ListAllWifi:"
Any ideas how to get object of a class based on class name?
Thanks in advance.
I think your problem is that you are trying to test a method of class (which are declared with +), but in fact you have an instance method, declared with -.
Try this:
Class klass = objc_getClass("mConWifi");
SEL sel = sel_getUid("ListAllWifi:");
if ( [klass instancesRespondToSelector:sel] ) {
id object = [[klass alloc] init];
objc_msgSend(object, sel);
}
Related
This question already has answers here:
Error: The instance member ... can't be accessed in an initializer
(4 answers)
Closed 6 months ago.
I would like a member of a class to received a listner from its owner class. So, something like this:
class Member {
Member({required this.listener});
final VoidCallback listener;
}
class Owner {
final member = Member(listener: saySomething); // <- error here
void saySomething() {
debugPrint('hello');
}
}
But I get the error on listener: saySomething that reads instance member can't be accessed in an initializer. My understanding is that it's because the compiler builds the Member instance first and then the Owner instance, so doesn't have the locations in memory yet.
I know I can do this in two steps. E.g., instantiate member and then assign its listener in my constructor or wherever, but it would be really nice if I could assign listener when member is instantiated.
I'm pretty sure that's not possible but am hoping to be proven wrong?
You can either pass the function directly into the constructor, or you can make saySomething into a static method, rather than an instance method:
// Directly passing the function
class Owner {
final member = Member(listener: () {
debugPrint('hello');
});
}
// Using a static method
class Owner {
final member = Member(listener: saySomething);
static void saySomething() {
debugPrint('hello');
}
}
I have a simple School class which defines a init method:
#implementation School
- (id) init {
self = [super init];
if (self) {
// call class method of MyHelper class
if ([MyHelper isWeekend]) {
[MyHelper doSomething];
}
}
}
#end
(MyHelper is a class contains only class methods, the isWeekend is a class method returns a boolean value)
I use OCMock to unit test this simple init method:
- (void)testInit {
// mock a school instance
id schoolMock = [OCMockObject partialMockForObject:[[School alloc] init]];
// mock class MyHelper
id MyHelperMock = OCMStrictClassMock([MyHelper class]);
// stub class method 'isWeekend()' to return true
OCMExpect([MyHelperMock isWeekend]).andReturn(true);
// run init method
[schoolMock init];
// verify
OCMVerify([MyHelperMock isWeekend]);
}
But when run it, I get error:
OCMockObject(MyHelper): Method isWeekend was not invoked. why?
You've created a mock for the MyHelper class, but this isn't going to be used within the implementation of your School object. You'd only get the mocked response if you wrote [MyHelperMock isWeekend], which you can't do inside the initialiser without rewriting it for tests.
To make your School class more testable you should be passing in any dependencies on initialisation. For example, you could pass in the isWeekend value as part of the initialiser, instead of obtaining it inside the method, or pass in the class object (MyHelper or MyHelperMock).
It's worth noting that finding certain classes or methods difficult to test because of things like this is often a good indicator that your code isn't structured very well.
I know there are countless resources on method swizzling. However is it possible to swizzle a method from a private API? The problem is that there are no header files. I would like to swizzle a method from a private class in a PrivateFramework such as (random example) Message.framework methods
This is for personal testing, I understand that it will get rejected to oblivion by Apple.
You can use NSClassFromString to get Class and use runtime library to perform method swizzling. No header files required. You just need to know class name and method signature.
sel_getUid can be used when #selector(somePrivateMethod) give your error about somePrivateMethod is not valid selector (because header is not available)
Code taken from my Xcode plugin
SEL sel = sel_getUid("codeDiagnosticsAtLocation:withCurrentFileContentDictionary:forIndex:");
Class IDEIndexClangQueryProviderClass = NSClassFromString(#"IDEIndexClangQueryProvider");
Method method = class_getInstanceMethod(IDEIndexClangQueryProviderClass, sel);
IMP originalImp = method_getImplementation(method);
IMP imp = imp_implementationWithBlock(^id(id me, id loc, id dict, IDEIndex *idx) {
id ret = ((id (*)(id,SEL,id,id,id))originalImp)(me, sel, loc, dict, idx);
// do work
return ret;
});
method_setImplementation(method, imp);
Create a category on the class and add the declaration for the method you want to call. Then you can just instantiate an instance of the class and call the method.
This also works for unit testing private methods in your code.
I'm currently trying to learn how to use private frameworks, just to gain some deeper understanding of these kinds of things.
So, while experimenting with ToneLibrary.framework (See classdump), I noticed that I am able to work with existing instances, but am unable to instantiate a class object from a private framework like I normally would myself.
For example, from the framework mentioned above, I have imported and TLToneManager.h and TLITunesTone.h.
I can do:
NSArray *tones = [[TLToneManager sharedRingtoneManager] installedTones];
for (id object in tones) {
TLITunesTone *tone = (TLITunesTone *)object;
NSLog(#"Tone: %#", [tone name]);
NSLog(#"Tone: %#", [tone filePath]);
}
But I can't do:
TLITunesTone *newTone = [[TLITunesTone alloc] init];
[newTone setName:#"TestTone"];
as it will result in "the symbol for TLITunesTone cannot be found for architecture arm7v".
If I add Class TLITunesTone = NSClassFromString(#"TLITunesTone"); before that code, it will complain that newTone is an undeclared identifier. I tried forward declaring the class, with the same result.
This however will work:
Class TLITunesTone = NSClassFromString(#"TLITunesTone");
id newTone = [[TLITunesTone alloc] init];
[newTone setName:#"TestTone"];
And I can now continue use newTone normally.
So my question is: Why can't I use a static type for an instance of a class which is part of a private framework?
in cpp:
void Character::jump(CCLayer *layer){
if (this->isAnimationPlaying) return;
up_or_down = UP;
body->runAction(CCSequence::actions(
CCMoveBy::actionWithDuration(0.5, ccp(0, 50)),
CCCallFuncND::actionWithTarget(body, callfuncND_selector(Character::upDownDone), this),
// CCCallFuncN::actionWithTarget(body, callfuncN_selector(Character::upDownDone)),
NULL));
this->isAnimationPlaying = true;
}
void Character::upDownDone(CCNode *node, CCObject *ob){
this->isAnimationPlaying = false; // *this is different from the this(class instance) in jump method, seems this in upDownDone is a new created instance*
}
So How can I get the class instance in a callback method? And can I make the this same for the main class instance and the callback's class instance?
EDIT:
Character is a class which has no parent class, and body is a member variable which is an instance of CCSprite.
Thanks.
because you are using body to call the function Character::upDownDone.
you should use this to call it.
CCCallFuncND* callFunc = CCCallFuncND::actionWithTarget(first_arg, secend_arg, third_arg);
body->runAction(callFunc);
assume your secend_arg is callfuncND_selector(Character::upDownDone)
then,
the first_arg is the caller, ie. the class instance who calls this function, in your code is body. but actually it should be this, or any instance of Charactor class
the CCNode* node (the first para that is been passed to your calling function) is the action runner, ie. body in your code. because you are using body->runAction()
the CCObject* obj (the second para that is been passed to your calling function) is a void pointer which is exactly the same with third_arg.
another way is use
void Character::upDownDone(CCNode *node, void *ob){
(Character*)ob->isAnimationPlaying = false;
}
Seems like you call the Character::upDownDone method using the instance "body" instead of this .May be you want this:
CCCallFuncND::actionWithTarget(this, callfuncND_selector(Character::upDownDone), body),