IOS super keyword causes errors - ios

I'm updating the code in an app I didn't write while at the same time basically teaching myself objective C so am a beginner with this stuff.
I have a class called TableViewController and in it I have a method that is fired once a JSON feed successfully gets some data. This method is called:
- (void)JSONFetch:(MYJSONFetch *)fetch didDownloadCollection:(id)collection
{
//CODE HERE
}
I have a bunch of other classes that inherit from this class and in turn they call their own version of this method and add their own bits of functionality to the mix.
So for example I have a class called CategoryViewController that has this method defined in it:
- (void)JSONFetch:(MYJSONFetch *)fetch didDownloadCollection:(id)collection
{
[super didDownloadCollection:collection];
}
Notice the SUPER call there, ideally it should be able to access all the code in the parent's method and its own code as well.
In the .h file on the category controller I have this:
#import "MYTableViewController.h"
#interface MYCategoryViewController : MYTableViewController
{
//code here
}
So it should be able to 'see' the other code, but I get this error on the SUPER line:
Instance method '-didDownloadCollection' not found (return type defaults to 'id')
Which presumably means it actually can't see the parent method. It's not set as private as far as I can tell, the .h explicitly mentions the inheritance and other method calls happily bounce back and forth between the two.
So, something I've done has borken this good and I've no idea what. I've an inkling it must be in the MYJSONFetch code, but am not sure.
Anyone shed any light on this, hours spent trying to figure out why it can't see the parent method.

The method is
- (void)JSONFetch:(MYJSONFetch *)fetch didDownloadCollection:(id)collection
So you have to call
[super JSONFetch:fetch didDownloadCollection:collection];

Thats because the method is looks like this JSONFetch:didDownloadCollection: instead of this didDownloadCollection:.

Related

How to create categories dynamically in objective c?

I'm working on an objective-c library project ('MyLib').
Let's say this library is then included in 'TestApp'. Now, 'TestApp' also includes another library called 'Xlib'. This Xlib has a class C1 which has a method m1.
//C1.h is part of Xlib
#interface C1
- (void) m1;
#end
Now, every time m1 is called, MyLib should execute a piece of code.
My approach was, if I create a category in MyLib:
#interface C1 (mycat)
#end
#implementation C1 (mycat)
+ (void) load{
//code to swizzle method in class C1 from: #selector(m1) to: #selector(mycat_m1)
}
- (void) mycat_m1{
/*
insert code that I want to execute
*/
[self mycat_m1]; //calling the original m1 implementation
}
#end
Problem:
MyLib doesn't have the class C1. So I can't build MyLib as I'm trying to create a category on a class which doesn't exist. Hence, compilation errors.
So, then I tried to wrap the above code inside:
#if defined(__has_include)
#if __has_include("C1.h")
/* above category code */
#endif
#endif
MyLib compiles and builds fine now, but since C1.h is not present in MyLib, the mylib.framework wouldn't have this category.
Now, I have two options:
1. Create this category dynamically, so that once the library is included in the app, and then when the app runs, this category will be created depending on whether TestApp includes Xlib or not.
2. Remove the file which has this category code from compile sources, and then somehow expose that file in my framework to TestApp.
I'm not able to solve either of the options. Any ideas on the existing options? or any new options?
Edit: Added details since the question wasn't quite explanatory
This code doesn't need to be in a category.
Categories are often used when swizzling because most of the time, people are trying to "wrap" a function with their own behavior in a specific class. Swizzling is often done in the +load class method. The load class method on a Category is a nice place to put it organizationally, because you know the main class's +load method has just been called.
Swizzling is still quite possible in your case. Implement a class in your framework with a +load method. The swizzling code will go there like usual. You just need to lookup the Class you want to swizzle, instead of assuming the target reference is to self like it would be if this was in a Category. That is, if you are referencing a blog post or using a favorite technique to swizzle that references self, know that it likely won't be. Make sure you watch out for what class you are trying to swizzle to/from.
I've had to do something similar in the past. In my case, I had to look up the instance of the class that implemented the AppDelegate protocol from a Framework. In that case, the code that triggered the swizzling wasn't called from +load (since the AppDelegate class might not have been loaded, and the instance of the class for the AppDelgate definitely wasn't instantiated). In that case, I invoked the code from my class's -init method, and protected it so it couldn't be called twice. However, I was guaranteed to be able to instantiate my class from the app code, so the technique worked; I'm not 100% sure of your use case. It wouldn't hurt to post the code you've tried.
UPDATE: concrete example
Here is the method I keep handy for swizzling.
+ (void)swizzleSelector:(SEL)sel1 onClass:(Class)class1 withSelector:(SEL)sel2 fromClass:(Class)class2
{
Method method1 = class_getInstanceMethod(class1, sel1);
Method method2 = class_getInstanceMethod(class2, sel2);
// Make sure that both methods are on the target class (the one to swizzle likely already is).
class_addMethod(class1,
sel1,
method_getImplementation(method1),
method_getTypeEncoding(method1));
class_addMethod(class1, // The swizzling is 'on' the first class, so it's the target here, not class2.
sel2,
method_getImplementation(method2),
method_getTypeEncoding(method2));
// Once they are both added to the class, exchange the implementations of the methods.
method_exchangeImplementations(class_getInstanceMethod(class1,sel1),
class_getInstanceMethod(class1,sel2));
}
Like I said, you'll need to lookup the class of your target somehow, but assuming you are calling this from the class that has your replacement methods, and assuming m1: is the target selector you are trying to swizzle, you might invoke like this:
Class class = NSClassFromString(#"C1");
// Check that `class` is not `nil`
[self swizzleSelector:#selector(m1)
onClass:class
withSelector:#selector(my_m1)
fromClass:self];
I hope this helps to clarify what you can do with swizzling. 999 out of 1000, you probably don't need to swizzle. Your use case sounds like a possibility where you might have to.

How and where are the methods defined within the ViewController.m file in my XCode project called?

Started learning Objective-C and XCode yesterday. Coming from a Rails background, I expect class methods like the ones defined in the ViewController.m file to be called at some point. I assume this is also the case in Objective-C, but I can't find where these methods are used.
To be more specific: I am working with a Collection View. I have the following method that defines the amount of sections in my view.
-(NSInteger) numberOfSectionsInCollectionView:(UICollectionView*)collectionView{
return 1;
}
Firstly, I cannot find out where the method is called. How does numberOfSectionsInCollectionView get its UICollectionView argument?
Put another way - if this were Ruby and I defined the method like so:
Class ViewController
def numberOfSectionsInCollectionView(collectionView)
return 1
end
end
I would expect a view controller object (viewController = ViewController.new) to call the method somewhere (viewController.numberOfSectionsInCollectionView(argument)). I'm not seeing the objective-c equivalent here. Can anyone help me understand this?
As an aside, why would you pass in an argument to a method that's never used?

Put Class results in a second viewController

I have an UIViewController in wich I imported a class:
#import "differenza.h"
and then I created an instance of that class when a button is pressed:
- (IBAction)ok:(id)sender {
differenza *classeDifferenza;
classeDifferenza = [[differenza alloc] init];
[classeDifferenza metodo];
}
As you can see, I also called a method.... now I don't have enough space in that UIViewController for the results of that method, so I need a new ViewController to show all the results... but I don't know how to recall results from that new ViewController, as I can not use the
classeDifferenza.variabile
way ....
This is something I did not really get already. In the past I used (thanks to your help: Setting up class instances in a multi view app (Objective C)) a "common class" where to store all data and all methods... but I don't think It's always the proper way... I feel like I am abusing that solution... or It's the right way to do what I described?
Thanks!
You can pass data with prepareForSegue check this topic
How to pass prepareForSegue: an object
You can send variable results like that

Programmatical segue to new viewcontroller

In an if/else function depending on the conditions that're met I want to show some string as result to the user. I'm trying to do this by changing to a new viewController programatically. I have created a method 'showresult' that will be taking one variable 'result' as argument and then changing viewController and showing the content of 'result' on there.
For changing the viewController I now have:
Defined in .h as:
Then I'm trying to call this getData method in an if/else function:
Here I get 'no visible #interface for 'NSString' declares the selector 'getData'
I assume the error lies in my definition of the method, I just can't see why
--UPDATED Part--
I have now reached a point where the following way of changin viewController works for me:
[self performSegueWithIdentifier:#"gotoresult" sender:self];
Now I just need the last part, which is to pass on a variable to the new viewController. I have found other threads about passing something on, but just didn't understand how to apply this.
One suggestion I found was this one, but prepareforSegue isn't recognized in my Xcode
-(void)prepareforSegue:(UIStoryboardSegue *)segue sender:(id)sender{
GTImageView * viewController = segue.destinationViewController;
viewController.someData = 99;
}
I just need to be able to show some string on to that new viewController
The compiler is not complaining about self because the identifier is unknown but because the way you declare your method is wrong. In Objective-C you define methods like this:
- (returnType)doSomethingWithTheFirstParameter:(param1Type)param1 secondParameter:(param2Type)param2 {
// body
}
The name of your method would be -doSomethingWithFirstParameter:secondParamter:.
In the same manner you can add as many parameters as you like. So in your case the correct method definition would be:
- (NSString *)showResult:(NSString *)result {
// body
}
As this is very basic stuff I suggest you first read an introduction to Objective-C or do a tutorial in order to get familiar with the syntax, e.g. Programming with Objective-C.
Question has been edited. Answer to your new question:
The compiler tells you what's wrong with that code: In your if-branch you call the message getData on the object result which seems to be of class NSString. But you added the getData method to your view controller class. So when you want to call that method from inside the view controller you need to use
[self getData:result];
instead.
Furthermore you should not return 0 for a method of return type NSString. If you do not want the method to return anything change the method definition to:
- (void)showResult:(NSString *)result {
// body
}
and remove the line
return 0;
or return some NSString object.
Note: Please do not include screenshots of your code but copy the code and paste the text to your post instead. Besides other advantages you give other users the option to copy your code and try it out.

Theos instance method calling

I have searched for an answer to this and cannot find one.
How would I call an instance method in the same class I am hooking in my Theos tweak?
If I was using standard Xcode i would use the self method i.e.-
[self method:arg];
But in a theos tweak this says cannot find the method, even if i hook that method.
Example:
%hook classimhooking
-(void)methodimhooking
{
[classimhooking methodiwanttocall];
[self methodiwanttocall];
%orig;
}
-(void)methodiwanttocall
{
%orig;
}
%end
The methodiwanttocall is there and i can hook to it, just not call it.
I have tried adding a new method with %new and calling that but it is not found, i have tried the same with a delay, but it is not found.
I have tried defining a variable of my class and calling that but it doesn't work.
I can also see that you can grab iVars directly, but this doesn't work with methods.
Any ideas would be appreciated.
If you want to call a method on the object you just hooked, you can use performSelector (or performSelector:withObject: if it has an argument), which should be something like that:
[self performSelector:#selector(methodiwanttocall)];
Sorry to dig an old thread. I may have an idea about the issue here. In case anyone with the same issue comes across this.
The problem is that the compiler does not know there is such a method, even there is. The way to deal with it is to import the header at the top of the code.
#import <classimhooking>

Resources