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:
MyClickedIndexBlock is typedef to a NSInteger. Property created with the name "clickedIndexBlock" which is of type MyClickedIndexBlock (meaning that clickedIndexBlock can be a number).
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
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).
I strongly suggest you read the Blocks Programming Guide.
Blocks are not methods. I'm not going to paraphrase what's said in the Conceptual Overview, but just quote some parts:
Blocks represent typically small, self-contained pieces of code. [...]
They allow you to write code at the point of invocation that is executed later in the context of the method implementation.
It seems you're confused by the syntax.
typedef void (^MyClickedIndexBlock)(NSInteger index);
It's basically just defining a type named MyClickedIndexBlock representing a block that takes a single parameter of type NSInteger and returns nothing (void).
It's not a typedef to a NSInteger.
#property (nonatomic, strong) MyClickedIndexBlock clickedIndexBlock
is a declaration of a property that will contain a MyClickedIndexBlock.
It's not required to typedef blocks, it would be perfectly valid to write
#property (nonatomic, strong) void(^clickedIndexBlock)(NSInteger index);
But for the sake of clarity (or reuse), you may choose to typedef them. Notice that the property name is what follows the ^.
You're stating that blocks can be used as methods because it's possible to call self.clickedIndexBlock(buttonIndex) in your example. But in fact, it's because you've declared a property named clickedIndexBlock that you can call it like that.
There's a lot in your question, but a large part is due to confusion and misunderstanding. The 2 approaches you mention aren't really different. Blocks are objects and can be manipulated as parameters, local variables or ivars / properties just as you would do with NSString or other kinds of objects.
1) The block isn't typedef'd to an integer. It's typedef'd to return void and has an integer parameter. There is no advantage to ethos 1 or method 2; they both can have multiple parameters if declared.
2) No reason why that format has been chosen for either case. They both achieve the same result, but the first one is arguably better semantically.
3) No. You can declare blocks inline to a method. Look at the header for [NSArray enumerateObjectsUsingBlock:] for an example of inline block declaration.
4) You can just create multiple properties and call each distinct block when necessary.
Related
I have question when I override setter for a #property.
that is:
If I set a property like this :
#property (strong) NSString *name;
In 'MRC' it will auto-generate getter and setter, assume setter will implement like this :
- (void)setName:(NSString *)name
{
[_name release]; // Release previous.
_name = name;
[_name retain]; // Remain object.
}
When I override setter in 'MRC', I can manage object by follow 'strong' behavior like code above,
but when in 'ARC', what will setter implement like or how to manage object to make it behavior like 'strong' since it has no 'retain and release' in 'ARC' ?
Thanks for yor time!
Under ARC, the compiler generates this setter:
- (void)setName:(NSString *)name {
_name = name;
}
But since _name is declared __strong (because the property is strong), the assignment turns into a call to objc_storeStrong:
- (void)setName:(NSString *)name {
objc_storeStrong(&_name, name);
}
The objc_storeStrong function takes care of the retain and release, and does so more safely than yours:
id objc_storeStrong(id *object, id value) {
value = [value retain];
id oldValue = *object;
*object = value;
[oldValue release];
return value;
}
(Consider what happens in your setter if name == _name and its retain count is 1 at the start of the setter. The objc_storeStrong function is also carefully written to avoid race conditions when multiple threads try to set the property simultaneously.)
ARC doesn't really require that you do anything special or additional, except for explicit bridging to Core Foundation pointers (which the compiler can auto-fix). It mainly requires that you don't write memory management code (like retain/release calls) yourself. You really don't need to "learn ARC", you only need to learn the memory issues that ARC cannot handle for you, like retain cycles (bad) and the management of C pointers (i.e. Core Foundation types--except when called from Swift, in which case ARC can handle them as well). The whole point of ARC is automating a part of software development that is very tedious and error-prone; it's less for you to worry about, not more. As an analogy, you don't really need to know anything about SQL in order to use Core Data...the finer details are abstracted away for you.
You made the property strong by virtue of declaring it strong, there isn't anything special you have to do in the setter:
-(void)setName:(NSString*)name
{
_name = name;
}
Of course, that is a silly example because there is no reason to override a setter when all you're doing is the default behavior, anyway. But you get the point...
I have a block thats stored as an instance variable in a class
typedef void ((^didSelectWord)(NSString* word));
#property (nonatomic,strong) didSelectWord wordSelected;
and i want xcode to auto fillout the block like when you type [UIView animateWithDuration and xcode autocompletes a block for it.
When i autocomplete my block it just fills out
[self.suggestedSearchTermView setWordSelected:(didSelectWord)wordSelected
instead of
[self.suggestedSearchTermView setWordSelected:^(NSString *word) {
Is it possible to change something to make Xcode understand how to autocomplete this block?
Ok I did some testing.
Apparently you have two (far from perfect) options:
avoid the typedef and declare the property as
#property (nonatomic,strong) void (^wordSelected)(NSString * word);
As noted in the comments, this has the drawback of skipping the parameter name in the autocompletion.
explicitly add a setter declaration in the interface
typedef void ((^DidSelectWordBlock)(NSString* word));
#interface YourClass : NSObject
#property (nonatomic,strong) DidSelectWordBlock wordSelected;
- (void)setWordSelected:(DidSelectWordBlock)wordSelected;
#end
this will cause Xcode to resolve the type definition before the setter definition, giving you the nice autocompletion that you would expect. The obvious drawback is the extra setter declaration in the interface.
That said, you should fill in a bug report: http://openradar.appspot.com/
Declare your property without typedef, like this:
#property (nonatomic,strong) void (^wordSelected)(NSString *word);
With this definition Xcode would give you the expansion below:
MyClass *test = [MyClass new];
[test setWordSelected:(void (^)(NSString *))wordSelected];
In exacerbated frustration, I made a macro consolidating this gross process..
#define BlockProperty(SIGNATURE,TYPENAME,varname,Varname) typedef SIGNATURE; #property (nonatomic,copy) TYPENAME varname; - (void) set##Varname:(TYPENAME)_
Now what Previously would've required (for proper autocompletion)..
typedef void(^OnEvent)(BOOL ok,id result);
#property (nonatomic,copy) OnEvent varname;
- (void) setVarname:(OnEvent)_;
is simply
BlockProperty(void(^OnEvent)(BOOL ok, id result),OnEvent,varname,VarName);
QUITE a bit easier, less verbose, AND you get the benefit of the typedef AND and you don't have to create the unsightly, theoretically unneeded setter declaration!
If you WANT to reuse a "type" you'll need another one (which this time will only take THREE parameters (as the block type cannot be redeclared).
#define BlockProp(TYPENAME,varname,Varname) #property (nonatomic,copy) TYPENAME varname; - (void) set##Varname:(TYPENAME)_
BlockProp(OnEvent,anotherVar,AnotherVar);
You could just create a new block type (name) for each property even if their signatures match (using the first macro), but that's kind of gross. Enjoy!
To better illustrate the question, consider the following simplified form of block recursion:
__block void (^next)(int) = ^(int index) {
if (index == 3) {
return;
}
int i = index;
next(++i);
};
next(0);
XCode (ARC-enabled) warns that "Capturing 'next' strongly in this block is likely to lead to a retain cycle".
Agreed.
Question 1: Would the retain cycle be successfully broken by setting the block itself to nil, in this fashion:
__block void (^next)(int) = ^(int index) {
if (index == 3) {
next = nil; // break the retain cycle
return;
}
int i = index;
next(++i);
};
next(0);
(Note: you'd still get the same warning, but perhaps it is unwarranted)
Question 2: What would be a better implementation of block recursion?
Thanks.
To accomplish the retain-cycle-free recursive block execution, you need to use two block references - one weak and one strong. So for your case, this is what the code could look like:
__block __weak void (^weak_next)(int);
void (^next)(int);
weak_next = next = ^(int index) {
if (index == 3) {
return;
}
int i = index;
weak_next(++i);
};
next(0);
Note that the block captures the weak block reference (weak_next), and the external context captures the strong reference (next) to keep the block around. Both references point to the same block.
See https://stackoverflow.com/a/19905407/1956124 for another example of this pattern, which also uses block recursion. In addition, the discussion in the comments section of the following article is relevant here as well: http://ddeville.me/2011/10/recursive-blocks-objc/
I think #newacct is correct about #Matt Wilding's solution; it does seem that nothing will have a strong ref to the next block in that case and will result in a run time exception when run (at least it did for me).
I don't know how common it is to find recursively called blocks in the wild in objc. However, in a real world implementation (if actually required) on say, a view controller, one might define the block and then set up an internal interface property with a strong reference to said block:
typedef void(^PushButtonBlock)();
#interface ViewController ()
#property (strong, nonatomic) PushButtonBlock pushButton;
#end
#implementation ViewController
...
// (in viewDidLoad or some such)
__weak ViewController *weakSelf = self;
self.pushButton = ^() {
[weakSelf.button pushIt];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), weakSelf.pushButton);
};
self.pushButton();
...
#end
This runs fine for me and has no compiler warnings about retain cycles (and no leaks in instruments). But, I think I would probably steer clear of doing this (recursive block calls) in most cases in objc - it's smelly. But interesting in any case.
I'm trying to create a global block that can be used from any method. I want this block to access #properties of the class. But when I try to do this I get "use of undeclared identifier self" accessing the backing variables _myVar also doesn't work.
Why doesn't this work? And what work around would give me a block that I can access from any method? Thanks.
An example:
#interface myClass()
#property (nonatomic,assign) BOOL subjectSex;
#end
#implementation
// these returns will get: use of undeclared identifier
int (^myBlock) = ^{
if(self.subjectSex) return 1;
return (!_subjectSex);
}
#end
You must define the block inside an instance method. You can then have a static block pointer that all your methods can access. When you assign the block to the pointer you must copy it:
s_blockPtr = [block copy];
Alternatively, you can send the this pointer as argument to the block. It's simpler to understand but may be more typing.
It doesn't so much have to do with property access as that you are using self. (You can use properties of other objects that you have access to without problem.) self does not exist in that scope. self is an implicit parameter in methods. Since your block definition is not inside a method, there is no variable named self (unless you define a global variable named self, but that would probably be a bad idea).
Blocks are nothing it's just a function pointer.If you want to access property in block you have to explicitly set __block in the property i.e.
you have to tell the compiler this is a block type property.
Let's take a closure look on blocks:
suppose there are two threads T1 and T2
//T1 Thread
void fun(int (*funptr)(int a,int b))
{
funptr(2,3);
}
//T2 Thread
int add(int a,int b)
{
return a+b;
}
//Main Thread
fun(&add);
printf("Hello");
From the above code ,assume main thread address is 20004 and executing fun having address 20006 and that fun taking function pointer as an argument which pointing to another function of thread T2 at an address of 20064 which means context switching from Thread T1 and T2, while executing T2 thread,function don't know about the global variables which are in code segment thats why they can't access because they are in different thread and this is the reason blocks are running on different thread.
that's solve.
You can access any object in a block by declaring it block type
#property(nonatomic,retain) __block NSString *strName;
dispatch_async(dispatch_get_current_queue(), ^(void)
{
self.strName= #"XYZ";
});
I have a block property that looks like this:
#property (nonatomic, copy) void (^indexChangeBlock)(NSInteger index);
When I try to set the value for this property, Xcode autocomplete will omit the parameter name leaving me with something like this:
[self.segmentedControl3 setIndexChangeBlock:^(NSInteger) {
code
}];
Then Xcode shows a Parameter name omitted error. I'm aware that I can solve this by adding the parameter name manually to make it look like this:
[self.segmentedControl3 setIndexChangeBlock:^(NSInteger index) {
code
}];
My questions is, how can I make Xcode add the parameters names automatically. Or in other words, prevent it from removing them.
possible solution:
typedef void (^IndexChangeBlock)(NSInteger index);
and define your property with
#property (nonatomic, copy) IndexChangeBlock indexChangeBlock;
and if you add
- (void)setIndexChangeBlock:(IndexChangeBlock)indexChangeBlock;
everything should work
In exacerbated frustration, I made a macro consolidating this gross process..
#define BlockProperty(SIGNATURE,TYPENAME,varname,Varname) typedef SIGNATURE; #property (nonatomic,copy) TYPENAME varname; - (void) set##Varname:(TYPENAME)_
Now what Previously would've required (for proper autocompletion)..
typedef void(^OnEvent)(BOOL ok,id result);
#property (nonatomic,copy) OnEvent varname;
- (void) setVarname:(OnEvent)_;
is simply
BlockProperty(void(^OnEvent)(BOOL ok, id result),OnEvent,varname,VarName);
QUITE a bit easier, less verbose, AND you get the benefit of the typedef AND and you don't have to create the unsightly, theoretically unneeded setter declaration!
If you WANT to reuse a "type" you'll need another one (which this time will only take THREE parameters (as the block type cannot be redeclared).
#define BlockProp(TYPENAME,varname,Varname) #property (nonatomic,copy) TYPENAME varname; - (void) set##Varname:(TYPENAME)_
BlockProp(OnEvent,anotherVar,AnotherVar);
You could just create a new block type (name) for each property even if their signatures match (using the first macro), but that's kind of gross. Enjoy!