Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Please help me with block-based callbacks

I have an ill-understanding of block-based callbacks. There seems to be two approaches that I'm aware of and I don't know when I should be using one over the other so could someone please explain to me the differences between the two, correct me and give me some tips if I need any.

Some code I found off stackoverflow as well as a library from elsewhere so thanks to those who wrote this code.

typedef void (^MyClickedIndexBlock)(NSInteger index);
@interface YourInterface : YourSuperClass
@property (nonatomic, strong) MyClickedIndexBlock clickedIndexBlock

.m
//where you have to call the block
if (self.clickedIndexBlock != nil) {self.clickedIndexBlock(buttonIndex)};

// where you want to receive the callback
alert.clickedIndexBlock = ^(NSInteger index){NSLog(@"%d", index);};

my understanding with the above is that:

  1. MyClickedIndexBlock is typedef to a NSInteger. Property created with the name "clickedIndexBlock" which is of type MyClickedIndexBlock (meaning that clickedIndexBlock can be a number).

  2. Blocks can also be used as methods which is why I can call self.clickedIndexBlock(buttonIndex);

BUT something tells me that this approach as a @property only really supports one parameter eg. NSInteger.

WHEREAS the following approach allows for more than one parameter.

bluetoothMe.h

typedef void (^hardwareStatusBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);

- (void)hardwareResponse:(hardwareStatusBlock)block;

bluetoothMe.m

- (void)hardwareResponse:(hardwareStatusBlock)block {
privateBlock = [block copy]; 
}

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"Did connect to peripheral: %@", peripheral);

privateBlock(peripheral, BLUETOOTH_STATUS_CONNECTED, nil);

NSLog(@"Connected");
[peripheral setDelegate:self];
[peripheral discoverServices:nil];
 }

My understanding that creating a property which is strong and doing a [block copy] will retain the block around until the app terminates. So [block copy] and strong both retain. [block copy] is applied to the block to retain otherwise the block would have vanished when the method goes out of scope.

ViewController.m

[instance hardwareResponse:^(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error) {

    if (status == BLUETOOTH_STATUS_CONNECTED)
    {
        NSLog(@"connected!");
    }
    else if (status == BLUETOOTH_STATUS_FAIL_TO_CONNECT)
    {
        NSLog(@"fail to connect!");
    }
    else
    {
        NSLog(@"disconnected!");
    }

    NSLog(@"CBUUID: %@, ERROR: %@", (NSString *)peripheral.UUID, error.localizedDescription);
}];

So lets see what my questions were:

1) When would I choose the first approach over the second approach and vice versa?

2) First example, the block was a typedef to a property. Second example, the block was declared a method. Why couldn't the first example be declared a method and why couldn'tt the second example be typedef to a property?

3) Would I need to create a typedef for every type of delegate method that I want a block-based callback for?

4) At of date, ive only seen one delegate method supported. Could you show me an example on how one would implement each approach if I was to create block-based callbacks on multiple delegate methods which are not similar.

Appreciate your feedback. This is hard at times. Need as much help as I can get. Thanks,

Ben

like image 408
Ben Avatar asked May 23 '26 17:05

Ben


1 Answers

The questions

  • Whether to typedef a block or not,
  • whether to use a property for a block or not,
  • whether a block has a single or multiple arguments,

are completely independent (or orthogonal). All combinations are possible and allowed.

void (^myClickedIndexBlock)(NSInteger index);

declares a block variable myClickedIndexBlock taking an integer argument and returning void. You can use typedef if the same block type occurs repeatedly in your program:

// Define MyClickedIndexBlock as *type* of a block taking an integer argument and returning void:
typedef void (^MyClickedIndexBlock)(NSInteger index);
// Declare myClickedIndexBlock as a *variable* of that type:
MyClickedIndexBlock myClickedIndexBlock;

With multiple arguments:

void (^privateBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);

or

typedef void (^hardwareStatusBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);
hardwareStatusBlock privateBlock;

Instead of (instance) variables, you can use properties. In the first example:

@property (nonatomic, copy) void (^myClickedIndexBlock)(NSInteger index);

declares myClickedIndexBlock as a block property, and is equivalent to

typedef void (^MyClickedIndexBlock)(NSInteger index);
@property (nonatomic, copy) MyClickedIndexBlock clickedIndexBlock;

Contrary to your assumption, block properties are not restricted to blocks with a single argument. You can use a property also in the second example, with or without typedef:

@property (nonatomic, copy) void (^privateBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);

or

typedef void (^hardwareStatusBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);
@property (nonatomic, copy) privateBlock;

It is your choice whether to use instance variables or properties for blocks. I would use properties (with the "copy" attribute).

Whether to typedef or not is purely a matter of taste. It helps to avoid errors if the same block type occurs repeatedly in your program. On the other hand, the Xcode autocompletion seems to work better without typedef (in my experience).

like image 58
Martin R Avatar answered May 26 '26 06:05

Martin R



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!