I'm new to iPhone application development, but I seem to have somewhat managed so far (learning as I go).. however, I've run in to an issue I can't figure out. Here's the scenario:
I have an extension class called CoreAPI which I have my network functions inside. I have a function called "Login" inside CoreAPI which makes a request to a server, gets the 0 or 1 response and returns true or false. I had this working perfectly with Synchronous requests, but now I need to make everything asynchronous.
So, below is what I'm trying to do.. this is not working with the error along the lines of "Object of type ID has no method loginCallback"
loginViewController.m
- (void) login {
CoreAPI APIObject = [[CoreAPI alloc] init];
[APIObject login:self];
}
- (void) loginCallback {
NSLog(#"It called back.");
}
CoreAPI.m
- (void)login:(id)sender {
[sender loginCallback];
}
Thanks, and please let me know if I have missed any details needed to help me.
I'm seeing a couple problems. First, I'm guessing you don't provide visibility of the loginCallback function to CoreAPI.m. You should be able to send any message to an id type, so long as it's defined for a known type.
However, I'd recommend using a completion block in this case. Here's what that could look like:
(in CoreAPI.h)
- (void)login:(void (^)(void))completed;
(in CoreAPI.m)
- (void)login:(void (^)(void))completed {
// Login code
if (completed) completed();
}
Calling it would look like:
CoreAPI APIObject = [[CoreAPI alloc] init];
[APIObject login:^{
NSLog(#"It called back.");
}];
They have really goofy syntax, but blocks are really nice for this sort of thing. Hope this helped! Let me know if I didn't explain something clearly.
this should do the trick for you:
first, import loginViewController header inside CoreApi.m
#import "loginViewController.h"
Then, change login method to this:
- (void)login:(id)sender {
[(loginViewController*)sender loginCallback];
}
Or this:
- (void)login:(loginViewController*)sender {
[sender loginCallback];
}
Explanation: notice that your login method is receiving by parameter one object of type id . In objective C, id type means a reference to any Objective-C of unknow class. So, inside your login method, the compiler doesn't know that the sender is a instance of your loginViewController class, so it won't recognize loginViewController's methods.
To informations about this, please read: Objective-C: difference between id and void *
Notice that I only focused in remove your actual error. You should have to do more things in order to accomplish your code to run asynchronous.
In order to perform a better callback, please look for delegates or blocks (like in oltman's answer).
To run things in background, look for CDG : http://developer.apple.com/library/ios/#DOCUMENTATION/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html
Related
I have been doing some research online and have found that using the ObjectiveC package in Objective C you can get a list of all the methods on a class using class_copyMethodList(), and I see you can get the implementation (IMP) of a method using instanceMethodForSelector:. The Apple documentation here has been helpful so far but I'm stuck and not sure what I'm really looking to find.
I want a list of the methods/functions called in a given method's implementation so I can build a call tree.
Any suggestions? Thanks in advance!
This solution is kind of hard way, and will cause a line of code in every method You can also make use of sqlite and save the tracked methods..
MethodTracker.h
#interface MethodTracker : NSObject
#property (nonatomic) NSMutableArray *methodTrackArr;
+ (MethodTracker *)sharedVariables;
#end
MethodTracker.m
#implementation MethodTracker
static id _instance = nil;
+ (MethodTracker *)sharedVariables
{
if (!_instance)
_instance = [[super allocWithZone:nil] init];
return _instance;
}
// optional
- (void)addMethod:(NSString *)stringedMethod
{
// or maybe filter by: -containObject to avoid reoccurance
[self.methodTrackArr addObject:stringedMethod];
NSLog("current called methods: %#", methodTrackArr);
}
#end
and using it like:
OtherClass.m
- (void)voidDidLoad
{
[super viewDidLoad];
[[MethodTracker sharedVariables] addMethod:[NSString stringWithUTF8String:__FUNCTION__]];
// or directly
[[MethodTracker sharedVariables].methodTrackArr addObject:[NSString stringWithUTF8String:__FUNCTION__]];
}
- (void)someOtherMethod
{
// and you need to add this in every method you have (-_-)..
[[MethodTracker sharedVariables] addMethod:[NSString stringWithUTF8String:__FUNCTION__]];
}
i suggest you import that MethodTracker.h inside [ProjectName]-Prefix.pch file.
Sorry, for the double answer, i deleted the other one and i have no idea how did that happen..
Hope this have helped you or at least gave you an idea.. Happy coding,
Cheers!
I think in the runtime track method is possible, but function not.
I have been build a tool DaiMethodTracing for trace all methods activity in single class for some of my need. This is based on objective-c method swizzling. So, there is an idea to do this
List all Classes in your application.
swizze all the methods in each class.
filter the method you want to trace.
finally, you may got the method call path.
I have spent quite a bit of time reading about blocks, but for some reason it's very difficult for me to fully understand them. It's worth mentioning I'm a newbie developer, but blocks are the first real 'block' I've come across, so maybe you can help me.
I have a case where an object (instance of a ViewController) is talking to a class (via a class method - it's a utility class I've set up to request/get data). I want to implement a method with a callback using a block. Here are my current 'understandings' that I based my code on:
A block can be passed as a method argument;
A block can be called like a function.
OK, so here's my (simplified) method in my utility class:
+ (void)getDataWithCompletion:(void (^)(BOOL))completion {
//Some code...
completion(YES);
}
And this is how I call the method from my viewController:
[ClassName getDataWithCompletion:^(BOOL gotData) {
if (gotData) {
NSLog(#"Called");
}
}];
Naturally, I was expecting that NSLog to be printed on my console, but alas... I know I am doing something wrong here, could someone please point that out? Any rookie-level explanation would be highly appreciated.
The mistake is really simple. You forgot a parameter name.
Cahnge your method to this.
(BOOL gotData) this is what you have missed.
+ (void)getDataWithCompletion:(void (^)(BOOL gotData))completion {
//Some code...
completion(YES);
}
Here is link to apple documentation apple block documentation
I am trying to setup a OCMock to be verified.
I have a protocol, TaskManagerDelegate, that contains the following method,
- (void) addTasks:(NSArray * ) tasksToAdd;
After setting up my mock object like this,
id mockTaskManagerDelegate = [OCMockObject mockForProtocol:#protocol(TaskManagerDelegate)];
I assign the object to the class under test like this,
taskManager.Whatever = mockTaskManagerDelegate;
I call a method on my taskManager and then want to verify the addTasks method was called on the TaskManagerDelegate and that the array that was passed to it contains exactly one object.
So far I have used the OCMArg class to detect if a parameter is being passed in, but I am struggling to understand how to check that specific types are sent are sent to the mocks, or that the objects sent to mock pass certain tests (have a .count of exactly one for a example). I come from a C# background and would normally use Moq, where you can use lamda functions to do specific checks on parameters being sent to the mocked object.
Does any one know how to do this with OCMock or if for some conceptual reason it is not possible to do?
Thanks,
The features description on the OCMock site has this: ;-)
"If Objective-C blocks are available it is possible to check the argument with a block as follows:
[[mock expect] someMethod:[OCMArg checkWithBlock:^(id value) { /* return YES if value is ok */ }]];
Would that work for you? Are you in an environment where blocks aren't available?
I had the same requirement and so created a category for it:
#implementation OCMArg (IsOfClass)
+ (id)isOfClass:(Class)aClass
{
BOOL (^classCheck)(id) = ^BOOL(id obj) {
return [obj isKindOfClass:aClass];
};
return [[OCMBlockConstraint alloc] initWithConstraintBlock:classCheck];
}
#end
So I have this custom class with just a test method that does nslog. I am going to reuse this method many times in my app. The interface looks like this.
#interface TestViewController: UIViewController { CMImageMover * imageMover }
Then in the view did load I:
imageMover = [[CmImageMover alloc] init];
If I do:
[imageMover testMethod];
Right after the alloc and init it works in the viewDidLoad function but if I call it again from another function in the view controller nothing works and the class method does not get called.
What am I doing wrong here. Every other var I declare like NSArray/NSTimer, I do the say way and I am able to access and use it throughout my controller.
When you say "if I call it again from another function in the view controller nothing works" then first thing to check is what you are sending the testMethod. It could be nil, in which case nothing will happen. In objective C sending a message to nil does nothing. Add an NSLog to find out, e.g.
NSLog(#"imageMover object is: %#", imageOver);
[imageMover testMethod];
If the NSLog shows it is nil - or something crazy - then follow up what you are doing with the imageMover ivar.
You mention a class method in your question, but don't refer to it in your code snippets.
If you have defined testMethod as a class method it will, of course, fail if you send that message to an instance. (And it will fail noisily.) A class method would be introduced like this:
+ (void) testMethod
{
NSLog(#"CMImageMover testMethod called on Class");
}
An instance method would be introduced like this:
- (void) testMethod
{
NSLog(#"testMethod called on an instance of CMImageMover");
}
Apologies if this is all screamingly obvious to you and missing the point of the question. It's not that clear from your question where the issue lies.
Stick with me. I'm visually impaired, have never used this site before, and will probably not post this in precisely the format that you are all used to. I apologize for any unintentional faux pas's herein.
Using Objective-C in an iOS project…
I have a singleton class, set up in what appears to be the usual way for Objective-C. It is, in the main, a series of methods which accept NSString values, interprets them, and return something else. In the code below, I'm simplifying things to the barest minimum, to emphasize the problem I am having.
From the singleton class:
- (NSUInteger) assignControlState:(NSString *)state {
// excerpted for clarity...
return UIControlStateNormal; // an example of what might be returned
}
Now, an instance of another class tries to use this method like so:
- (void) buttonSetup:(UIButton*)button {
[button setTitle:#"something" forState:[[SingletonClass accessToInstance] assignControlState:#"normal"]];
}
This code actually works. HOwever, when the system goes to draw the UI which includes the button whose title was set in this way, an EXC_BAD_ACCESS error occurs.
If the assignControlState method is moved into the same class as the buttonSetup method, no error is generated.
I'm guessing this is something about Apple's memory management that I'm not fully understanding, and how things go in and out of scope, but for the life of me, I can't figure out where I'm going wrong.
HOpe someone can help. Thanks.
The problem is in your accessToInstance method. I'll bet you are under-retaining. The implementation should be more like this:
static SingletonClass *sSingletonClass = nil;
#implementation
+ (id)accessToInstance {
if (sSingletonClass == nil) {
sSingletonClass = [[[self class] alloc] init];
}
return sSingletonClass;
}
#end
Now, if your program is following normal memory management rules, the singleton will stay around. You can check by writing:
- (void)dealloc {
[super dealloc]; // <-- set a breakpoint here.
}
If the debugger ever stops at this breakpoint, you know something in your program has over-released the singleton.
You know that bit you excerpted for clarity? I think you need to show us what it is because there's probably an over release in it somewhere.
Specifically, I think you release an autoreleased object. If you do that and don't use the object again, everything will carry on normally until the autorelease pool gets drained. The autorelease pool gets drained automatically at the end of the event at about the same time as the drawing normally occurs.
That would also explain the delayed crash following the NSLogs.