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
Related
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
We had a bug, and it destroys the looks of our UI, some of the UI elements overlap, or has been added to the subview twice. the bug is hardly reproduced so its hard to fix it. Now I thought of the reason, and probably the method that changes the UI are being called twice at the same time. And I was correct, I tried to create the bug programatically.
We have a bug which is caused by a method being accessed by different threads at the same time. To emulate this problem, and better understand it. see codes posted below.
When I do this, updatePresence Method call, my program works perfectly
ViewController.m
-(void)loadConversationScreen{
[conversationController updatePresence];
}
But when I do this, something goes wrong with my program
ViewController.m
-(void)loadConversationScreen{
[conversationController performSelectorInBackground:#selector(updatePresence) withObject:nil];
[conversationController updatePresence];
}
This is because the method is being accessed at the same time and and the instance of my UIView is being accessed/changed also at the same time.
How do I PROPERLY stop 2 threads from using a method at the same time?
How do I properly handle it in IOS(if there is no proper way, what are the work arounds), are there built in locks or somekind?
My app should support ios 4.0 and up
Advance thanks to all for your help.
The best thread lock for you is #sycnhronized(object) {}. This means only one thread can enter the code at a time. The object passed in is used to perform the lock; only one thread can enter a block protected by a particular object's synchronized at a time. The others will wait. This can be any Objective-C object, even a NSString.
Typically, you'd use whatever object you're trying to protect from multiple threads. You probably want #synchronized(self) {}:
-(void)updateVariables {
#synchronized(self) {
_foo = 1;
_bar = 2;
}
}
#sycnhronized is re-entrant in the sense that the same thread can call #sycnhronized as deeply as it wants, for instance:
- (void)a {
#synchronized(self) {
// entered "immediately" if called from b, where the #synchronized has
// already been called
_foo = _foo + 1;
}
}
- (void)b {
#synchronized(self) {
[self a];
}
}
For posterity and because I already typed it before reading your clarification, if you really cared only about updating the UI, you'd want to force your call over to the main thread instead like this:
- (void)someTask {
dispatch_async( dispatch_get_main_queue(), ^{
[self updateUI];
});
}
- (void)updateUI {
NSAssert( [NSThread isMainThread], #"called from non-main thread" );
// do UI updates here
}
As warrenm said you shouldn't update your UIView from a different thread than the Main thread (UI thread). Still, you asked if there is any workaround for what's going on. To be honest, you should try to, instead of blocking the access of the second thread to your method, understand why the methods is called twice. This is more a logical problem than anything else and you should try to fix that, instead of trying a shortcut.
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.
I have two similar classes, MultiSlotBlock and SingleSlotBlock. They have started to share a lot of common code so I have decided to do some refactoring and pull some of the methods up to a new superclass, let's call it Block.
Now one of the methods that I pull up, simplified for the example, looks like this:
// (Block.mm)
- (void)doACommonBehaviour
{
// .. does some stuff
[self doAUniqueBehaviour];
}
The problem here is that [self doAUniqueBehaviour] is showing a warning because of course my superclass doesn't implement this method anywhere.
The two solutions I thought of don't sound great to me. One is to use a protocol (the way I am currently doing it) like so:
// (Block.mm)
- (void)doACommonBehaviour
{
// .. does some stuff
if ([self conformsToProtocol:#protocol(UniqueBehaviourProtocol)])
{
id<UniqueBehaviourProtocol> block = (id<UniqueBehaviourProtocol>)self;
[block doAUniqueBehaviour];
}
}
The other is to have a blank method body in my superclass (in this case there would be a lot) and just return doesNotRespondToSelector.
Something is tingling at the back of my mind that I should be using the Strategy Pattern, but I might be way off, and I haven't thought through how that would be implemented.
Any ideas? Thanks.
EDIT: I know for a fact that doAUniqueBehaviour will be implemented in all subclasses, it is just the implementation that will differ.
The superclass should not know about its subclasses. You should implement the
- (void)doACommonBehaviour method in every subclass and there:
- (void)doACommonBehaviour
{
[super doACommonBehaviour];
[self doAUniqueBehaviour];
}
EDIT - clarification:
If all the subclasses are going to implement -doAUniqueBehaviour then it should be implemented in the superclass (even empty) and each subclass will override it to its needs.
If subclass1 implements -doAUniqueBehaviour1, subclass2 implements -doAUniqueBehaviour2 etc then do what I propose above; eg. in subclass1:
- (void)doACommonBehaviour
{
[super doACommonBehaviour];
[self doAUniqueBehaviour1];
}
There is not such concept as abstract class in Objective-C. In order to avoid the warning, you have to provide a default implementation in your base class. Usually, this implementation will throw a doesNotRespondToSelector error at runtime:
- (id)someMethod:(SomeObject*)blah
[self doesNotRecognizeSelector:_cmd];
return nil;
}
Note: the _cmd argument is the invoked selector.
#Dimitri's suggestion will work, but instead of forcing each subclass to implement the same method, you can declare it once in Block, and just above that method (in the implementation file, not header) declare the unique method like so:
- (void) doUniqueBehaviour { }
- (void) doCommonBehaviour {
// any common code you need
[self doUniqueBehaviour];
}
This will prevent any compiler warnings, and you can override -doUniqueBehaviour in subclasses as you like. It also avoids code duplication and reduces the potential for changing the code in one subclass but not another. Plus, you don't need a separate protocol, and dynamic typing is preserved.