How to get a selector of a class method? - ios

I have the next code where I am getting a pointer to instance method:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface TestClass : NSObject
#end
#implementation TestClass
- (void)someMethod { // This is instance method, it's okay
NSLog(#"Hello from some method!");
}
#end
int main(int argc, const char * argv[]) {
typedef void (*MethodWithoutParams)();
MethodWithoutParams someMethodImplementation =
class_getMethodImplementation([TestClass class], #selector(someMethod));
someMethodImplementation();
return 0;
}
It works pretty good. But if I'd like to get a pointer to class method, it doesn't work:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface TestClass : NSObject
#end
#implementation TestClass
+ (void)someMethod { // This is class method, it doesn't work
NSLog(#"Hello from some method!");
}
#end
int main(int argc, const char * argv[]) {
typedef void (*MethodWithoutParams)();
MethodWithoutParams someMethodImplementation =
class_getMethodImplementation([TestClass class], #selector(someMethod));
someMethodImplementation();
return 0;
}
It doesn't work because it can not look up the method implementation.
I am sure it must work, because it works if I am getting the implementation in such way:
MethodWithoutParams someMethodImplementation =
[TestClass methodForSelector:#selector(someMethod)];
So I looked into NSObject implementation and see the next code:
+ (IMP)methodForSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
return object_getMethodImplementation((id)self, sel);
}
- (IMP)methodForSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
return object_getMethodImplementation(self, sel);
}
Also object_getMethodImplementation() function has the next implementation:
IMP object_getMethodImplementation(id obj, SEL name)
{
Class cls = (obj ? obj->getIsa() : nil);
return class_getMethodImplementation(cls, name);
}
So the implementation is same for both class method lookup and instance method lookup.
But it doesn't work and I have no idea why. I guess that any type of methods (both class and instance) gonna be located in the dispatch table, and I can get the pointer to any of this methods. But as you see I can't do it.

You need to query the class's metaclass. A class method is an instance method on its metaclass.
Class meta = objc_getMetaClass("TestClass");
SEL sel = #selector(someMethod);
typedef void (*MyMethodImplementation)(Class, SEL);
MyMethodImplementation someMethodImplementation = (MyMethodImplementation)class_getMethodImplementation(meta, sel);
someMethodImplementation([TestClass class], sel);
Note, a method (instance or class) is never "without parameters". It must always be called with at least the receiver (instance or class) and the selector.

I have absolutely no idea about Objective-C runtime. But here is my rant:
object_getMethodImplementation for class methods:
// returns a class method implementation, can't find instance methods
class_getMethodImplementation([TestClass class]->getIsa(),
#selector(classMethod));
which is equivalent to
class_getMethodImplementation(objc_getMetaClass("TestClass"),
#selector(classMethod));
object_getMethodImplementation for instance methods:
// returns an instance method implementation, can't find class methods
class_getMethodImplementation((<TestClass instance>)->getIsa(),
#selector(someMethod));
which is equivalent to:
class_getMethodImplementation([TestClass class],
#selector(someMethod))
So it seems that <TestClass instance>->getIsa() returns a pointer that is equal to what [TestClass class] returns. And I guess now you know what's the difference between a self in a class method and a self in an instance method.
So you should use meta class as #KenThomases suggests.

Related

No visible #interface for class

My Car.m implementation file is as follows. Here I have 1 private method engineStarting.
Here I have used class extension concept to introduce
private method and I have also used Car+Maintainance as a category.
#import "Car.h"
//private methods
#interface Car()
-(BOOL) engineStarting;
#end
#implementation Car
#synthesize model=_model;
-(void)drive{
NSLog(#"%# is driving",_model);
}
-(void)stop{
NSLog(#"%# has stopped now",_model);
}
-(void)turnleft{
NSLog(#"%# is turning left",_model);
}
-(void)turnright{
NSLog(#"%# is turning right",_model);
}
-(BOOL) engineStarting{
return true;
}
My main.m file looks like
#import <Foundation/Foundation.h>
#import "Car.h"
#import "Car+Maintanance.h"
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
Car *car=[[Car alloc]init];
car.model=#"maruti";
//using car its own methods
// i am getting error in this line(no visible #interface......)
[car engineStarting];
[car drive];
[car stop];
[car turnleft];
[car turnright];
//using maintainanace
if([car neddOilChange]){
[car changeOil];
}
}
return 0;
}
As you said you've created private method -(BOOL) engineStarting which means you can access it just within car class.
If you want that method to be visible in your main.m file move the declaration to car.h file it will make it public.
You say that you want to keep -engineStarting privately, but use it outside the implementation? That's contradictory, because private means: "Unvisible outside the implementation."
Probably you are messed up with the terms. And you are right in a way: There is no black and white private/public pair, but a greyscale: Something can be visible to an implementation only (usually called private), to an implementation and subclass implementations (usually called protected), to an implementation and subclass implementations and friend classes (in a framework, usually called package) and public at all. Beside this you can have the desire to make a method public only to some subsystems (modules) and so on. So it is very complex.
What you can do for it, is to create a separate category and import the header of that category file to the area, where it should be visible.
//Car_PublicInFrameworkAddition.h
#interface Car(PublicInFrameworkAddition)
-(BOOL) engineStarting;
#end
//Car_PublicInFrameworkAddition.m
#implementation Car(PublicInFrameworkAddition)
-(BOOL) engineStarting
{
…
}
#end
Somewhere.m
// Somewhere should see the addition
#import "Car_PublicInFrameworkAddition.h"

objective c - Access private var from public function

Newbie to objective C...
NOTE: This is a conceptual problem, as I'm trying to translate "public and private" from what I know about other languages.
How can I access the "stringB" ivar through the "public" method?
myClass.h
#interface myClass : UIViewController {
}
#property (nonatomic, retain) NSString *stringA;
#property (nonatomic, retain) NSString *stringB;
- (void)dealWithStringA;
+ (void)dealWithStringB;
myClass.m
#import "myClass.h"
#interface myClass () {
}
#end
#implementation myClass
// My "private" function
- (void)dealWithStringA
{
return _stringA;
}
// My "public" function
+ (void)dealWithStringB
{
// Errors with: Instance variable "stringB" accessed in class method
return _stringB;
}
The method starting with a + is called a class method in objective C where a method starting with - is an instance method. An instance method can be performed on an instance of that class only.
Also the return type for your method would be an NSString since you are expecting to get a string object from that method.
For a class method, you'll need to create an autoreleasing instance of that class and then perform operations on that instance.
For eg.
+ (NSString*)dealWithStringB
{
MyClass *myClass = [[[MyClass alloc] init] autorelease];
myClass.stringB = #"Its String B";//It's an absurd example
return myClass.stringB;
}
You are wrong with understanding "+", "-" - it's not about private / public.
To have a private function you should implement that in your .m file:
#interface YourClass ()
- (id) privateMethod;
#end
Everything you declare in .h file will be public:
#interface YourClass : NSObject
- (id)someMethod //public
#end
"+" is used for static functions so you can call them without having an instance of a class.
For example in your case:
[myClass dealWithStringB];
and for "-" function you need instance.
[[[myClass alloc] init] dealWithStringA];
The static functions can be used when you don't need any properties from a class or to they are pretty often used to create instances of classes.
The "+" prefix means class method, not public. A "-" stands for instance method, not private.
Both public and private methods can access the private state of the class or instance.
myClass.h (Similar to yours)
#interface myClass : UIViewController
{
}
#property (nonatomic, retain) NSString *stringA;
#property (nonatomic, retain) NSString *stringB;
- (void)dealWithStringA;
+ (void)dealWithStringB;
#end
myClass.m
#implementation myClass
#synthesize stringA ;
#synthesize stringB ;
static myClass* instance = nil;
+(void) dealWithStringB
{
if(instance==nil)
{
instance=[myClass alloc]init];
}
else
{
//Access the field this way
printf("#"The string content is %#",instance.stringB);
}
}
Hope its Clear!!!

NSQueueOperation weird behavior

I am adding multiple instances of subclass of NSOperation in a for loop:
NSMutableArray * operations = [[NSMutableArray alloc]initWithCapacity:0];
for(int i =1; i<81;i++){
[operations addObject:[[PDFGenerator alloc]initWithPageNumber:i andPDFParser:pdf]];
}
[_queue addOperations:operations waitUntilFinished: NO];
in PDFGenerator I have a variable that stores the current page number for the operation.
#implementation PDFGenerator
int pageCounter;
In the main method of the PDFGenerator I am logging the current page number and it prints
80 for ALL the operations!
I already fixed by using #property for the current page count,
but I am trying to understand why it's happening. Any ideas?
thanks!
When you just use:
int pageCounter;
You are creating a global variable. Presuming you set this at each iteration, then refer to it in your PDFGenerator methods, it will always use the last value it was set to.
Example:
// Bar.h
#interface Bar : NSObject
FOUNDATION_EXPORT int someThing;
#end
// Bar.m
#implementation Bar
int someThing;
#end
// Foo.m
#import "Foo.h"
#import "Bar.h"
#implementation Foo
- (void)doSomething
{
++someThing;
}
#end
That's totally valid code, and calls to [Foo doSomething] increment someThing.
If you wanted an instance variable, it would look like this:
#interface Bar()
{
int someThing;
}
#end
#implementation Bar
- (void)doSomething
{
++someThing;
}
#end
In this case someThing is defined as an instance variable (not a global variable). It is an accessible part of objects of the Bar.

iOS ARC: unexpected 'dealloc' call

I have a problem with an Objective-C class, when ARC is enabled.
My classes looks like these:
#interface ParentClass : NSObject {
}
-(void)decodeMethod;
#end
#implementation ParentClass
-(void)decodeMethod{
}
#end
#interface ChilldClass : ParentClass{
int *buffer;
}
#end
#implementation ChildClass
-(id)init{
self = [super init];
if(self != nil){
buffer = (int *)malloc(20*sizeof(int));
}
return self;
}
-(void)dealloc{
free(buffer);
}
#end
I have another class like this one:
#interface OtherClass : NSObject{
ParentClass *c;
}
#end
#implementation OtherClass
[...]
-(void)decode{
c = [[ChildClass alloc] init];
[c decodeMethod];
}
[...]
#end
As you can see, a ChildClass object is created and stored as an attribute in OtherClass. As long as the OtherClass object is living, the ChildClass object pointed by c should be also living, isn't it? Well, I have a BAD_ACCESS error, because after the ChildClass initialization, and before the decodeMethod is called, the dealloc method in ChildClass is automatically executed.
Why? ARC is enabled, so the dealloc method should be called automatically when the ChildClass object is released, but it shouldn't happen in this moment, because is still pointed with c.
Any help?
Thank you very much!
#interface ChilldClass : ParentClass{
It's possible your issue is caused by a spelling error in ChilldClass (typo?)

How to call method from one class in another (iOS)

This a very basic question but I've searched all over and been unable to find an answer that explains well enough for me to get my head around it.
What I want to do is create a method in one class of my iOS app and then call that method from other classes in my app. Could someone explain exactly what I need to do to achieve this? Any help would be greatly appreciated as all my attempts so far have failed!
Thanks.
Objective-C:
You have to import the header of the class that contains the method you want to use (ClassYouWantToUse.h) into the class you want to use it at (TargetClass).
Inside the TargetClass.h or TargetClass.m (depending on the scope you want to give it):
#import "ClassYouWantToUse.h"
Then create an instance of the class you want to use inside the target class either as a property like this:
#property (nonatomic,strong) ClassYouWantToUse *classObject;
Or as an instance variable like this:
ClassYouWantToUse *classObject;
Make sure you initialize it! (usually inside ViewDidLoad):
classObject = [[ClassYouWantToUse alloc] init];
Now you can call any public methods from that class like this:
[classObject theClassMethodWithParam:param1 andSecondParam:param2];
Note: The ClassYouWantToUse class must have the methods that you want to make accessible to others by declaring them in the header file:
- (void)theClassMethodWithParam:(UIImage*)someImage andSecondParam:(NSString*)someText;
Otherwise you won't be able to see these methods.
Swift:
Theres really nothing special about it in swift, just adding this as a reference.
In swift you simply create an instance of the class you want to use:
let classObject = ClassYouWantToUse()
And use it directly:
classObject.theClassMethodWithParam(param1, andSecondParam:param2)
You have two basic options. You can either create or pass-in an instance of the first class to the second class, or you can add a static method to the first class and call it directly using the class object.
For instance, say you have:
#interface ClassA : NSObject {
}
//instance methods
- (int) addNumber:(int)num1 withNumber:(int)num2;
//static/class methods
+ (int) add:(int)num1 with:(int)num2;
#end
#implementation ClassA
- (int) addNumber:(int)num1 withNumber:(int)num2 {
return num1 + num2;
}
+ (int) add:(int)num1 with:(int)num2 {
return num1 + num2;
}
#end
Then you can do:
#import "ClassA.h"
#interface ClassB : NSObject {
ClassA* adder;
}
//constructors
- (id) init; //creates a new instance of ClassA to use
- (id) initWithAdder:(ClassA*)theAdder; //uses the provided instance of ClassA
//instance methods
- (int) add2To:(int)num;
//static/class methods
+ (int) add3To:(int)num;
#end
#implementation ClassB
- (id) init {
if (self = [super init]) {
adder = [[ClassA alloc] init];
}
return self;
}
- (id) initWithAdder:(ClassA*)theAdder {
if (self = [super init]) {
adder = theAdder;
}
return self;
}
- (int) add2To:(int)num {
return [adder addNumber:2 withNumber:num];
}
+ (int) add3To:(int)num {
return [ClassA add:3 with:num];
}
#end
Note that in most cases, you would use instance methods rather than static methods.
You have to use the concept of delegation.
https://developer.apple.com/library/ios/#documentation/General/Conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html

Resources