In my code, if I use the block directly:
- (IBAction)checkPendingAction:(UIButton *)sender {
self.block(sender.titleLabel.text); // if the block is no realize, there will report EXC_BAD_ACCESS(code=x, address=0x11xx)
}
If I use delegate, I can use the below code to check my delegate and delegate method if is realize:
if ([self.delegate respondsToSelector:#selector(myDelegateMethod:)]) {
[self.delegate tabBarClickWriteButton:self];
}
So, can I check the block if is realize in iOS?
make your block a property with 'strong' reference. and use weak reference of self in it if needed.
before calling block just check
if(block) {
block(data);
}
You need check block as below
if (self.yourblock != nil) {
self.yourblock(self.titleLabel.text);
}
Related
I am confused about these delegate method calls.
Which one is the correct way of calling the delegate method?
#protocol XYZDelegate <NSObject>
#required
- (void)someMethod:(id)someObject;
#end
method 1:
- (void)someButtonAction:(UIButton *)sender {
if([self.delegate && [self.delegate respondsToSelector:#selector(someMethod:)]]) {
[self.delegate someMethod:sender];
}
}
method 2:
- (void)someButtonAction:(UIButton *)sender {
if([self.delegate && [self.delegate respondsToSelector:#selector(someMethod:)]]) {
[self.delegate performSelector:#selector(someMethod:) withObject:sender];
}
}
They are both pretty much the same. They will result in the same outcome.
The second is slightly less efficient.
What I would change is the line...
if([self.delegate && [self.delegate respondsToSelector:#selector(someMethod:)]]) {...
The method someMethod: is required by the protocol.
So you can remove it completely...
- (void)someButtonAction:(UIButton *)sender {
[self.delegate someMethod:sender];
}
And it will still work. You can send a message to nil and it just won't do anything. If the delegate is not nil then by definition it will respond to the selector.
If the delegate object does not conform to the method then you will get a compiler error (or maybe just a warning?).
Anyway, that should suffice.
Just as a side note. I personally prefer the first method and if there is more than one parameter then you would have to call it that way to be able to pass the parameters in corrcetly.
The difference is one calls the delegate method directly, while the other uses the runtime, through performSelector, to do so dynamically.
The latter is less efficient and pointless, but the results are the same.
I have declared the method as follows:
- (void)downloadCSVs:(void (^)(void))completion
Its body is:
- (void)downloadCSVs:(void (^)(void))completion
{
[[Singleton sharedData] downloadCSVFilesFromServer:<MY_URL>];
}
and calling this method as:
[self downloadCSVs:^
{
NSLog(#"Download Completed!");
}];
But its after download, it is not executing the NSLog.
Please let me know where I am wrong.
Your block isn't being called as there is no attempt to call it.
The following method accepts the block as a parameter and, in turn, calls [Singleton downloadCSVFilesFromServer:] but it does not pass the block to this method and does not call it itself:
- (void)downloadCSVs:(void (^)(void))completion
{
[[Singleton sharedData] downloadCSVFilesFromServer:<MY_URL>];
}
You need to extend the [Singleton downloadCSVFilesFromServer:] method to accept the block parameter and call it when it's complete.
I want to define the method which will contain the block as an argument but block should be run on the completion of the method.
For Example:
[picker dismissViewControllerAnimated:YES completion:^{
imageThumb = pickedImage;
imageViewThumb.image = imageThumb;
}];
Please have a look what i did yet.
I declared the method in .h file-
-(void)resizeImageForSmoothness: (int) imageSmoothness completion: (void (^)(void))completion;
I implemented it in .m file-
-(void)resizeImageForSmoothness:(int)imageSmoothness completion: (void (^)(void))completion
{
// Here i performed my image resizing activity
}
How can my code will know that method has been completed and then run the completion block?
How can we declare and define such method?
How to store the block depends on how you do your stuff. If it's a synchronous operation (that is, the method blocks until whole operation is complete) you simply call it like a function:
- (void)fooWithHandler:(void(^)())handler
{
// Do things.
handler();
}
If the operation is asynchronous, you might want to store the block in a variable or even a dictionary. In this case you need to copy the block. You can either do this via the low-level Block_copy and Block_release C functions, but you can also treat a block like an Objective-C object! (Xcode doesn't provide autocompletion for this, for some reason.)
#interface MyClass {
void (^myHandler)();
}
- (void)fooWithHandler:(void(^)())handler
#end
#implementation MyClass
- (void)fooWithHandler:(void(^)())handler
{
myHandler = [handler copy];
// Do things.
// Then, when you're done (this is probably in another method):
if (myHandler) {
myHandler();
myHandler = nil;
}
}
#end
You can do something like that and use the return type et parameter you might need :
- (void)doStuffAndExecute:(void (^)(void))handler
{
// do stuff
handler();
}
I have this if statement checking if my delegate has implemented a given method:
if ([[self delegate] respondsToSelector:#selector(didFinishSettingNotificationOnDialog:)])
{
[self.delegate didFinishSettingNotificationOnDialog:self withNotification:notification];
}
However my code is not getting executed within the If statement. I have other delegate calls working between these objects and if I remove the if statement and just call
[self.delegate didFinishSettingNotificationOnDialog:self withNotification:notification];
on its own it works!
My delegate does implement correctly:
- (void)didFinishSettingNotificationOnDialog:(NotificationDialogView *)dialogView withNotification:(NotificationContext *)setNotification{
NSLog(#"Notification is: %#", setNotification);
}
So what am I doing wrong?
The name of the method is wrong. It should be didFinishSettingNotificationOnDialog:withNotification:.
Try this:
if ([[self delegate] respondsToSelector:#selector(didFinishSettingNotificationOnDialog:withNotification:)]) {
[self.delegate didFinishSettingNotificationOnDialog:self withNotification:notification];
}
I want to create a completion handler for a certain class, instead of firing off the class's main code and waiting for a delegate callback. I've read through the Apple documentation and they don't seem to give a very good example of how to directly implement something like this.
You need to treat the completion block just like a variable. The method will accept a block as part of it's parameters, then store it for later.
- (void)myMethodWithCompletionHandler:(void (^)(id, NSError*))handler;
You can typedef that block type for easier reading:
typedef void (^CompletionBlock)(id, NSError*);
And then store your block as an instance variable:
In your #interface: CompletionBlock _block;
In the myMethod.. _block = [handler copy]
Then when you want the completion block to execute you just call it like a regular block:
_block(myData, error);
If it was for an asynchronous method you could do it like this
- (void)asynchronousTaskWithCompletion:(void (^)(void))completion;
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Some long running task you want on another thread
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion();
}
});
});
}
this would be invoked with
[self asynchronousTaskWithCompletion:^{
NSLog(#"It finished");
}];
Something to note is the guard to make sure that completion is pointing to something otherwise we will crash if we try to execute it.
Another way I often use blocks for completion handlers is when a viewController has finished and want's to be popped from a navigation stack.
#interface MyViewController : UIViewController
#property (nonatomic, copy) void (^onCompletion)(void);
#end
#implementation MyViewController
- (IBAction)doneTapped;
{
if (self.onCompletion) {
self.onCompletion();
}
}
#end
You would set the completion block when pushing this view onto the stack
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
{
MyViewController *myViewController = segue.destinationViewController;
myViewController.onCompletion = ^{
[self.navigationController popViewControllerAnimated:YES];
};
}
Heres an example for a method that takes a String and a completion handler as variables. The completion handler can also receive a String.
Swift 2.2 Syntax
Defintion:
func doSomething(input: String, completion: (result: String) -> Void {
print(input)
completion(result: "we are done!")
}
Calling the function:
doSomething("cool put string!") { (result) in
print(result)
}
Chris C's answer is correct (and was very helpful to me) with one caveat:
Placing the declaration CompletionBlock _block; in #interface is not thread safe.
Put CompletionBlock _block = [handler copy]; in myMethod… instead if there is any possibility that myMethod… will be called from multiple threads (or dispatch queues).
Thanks #Chris C.