I am new to Objective-C, I was following Apple's "Programming with Objective_C", I can't understand this code sample:
void (^(^complexBlock)(void (^)(void)))(void) = ^ (void (^aBlock)(void)) {
//some code
return ^{
//some code
};
};
I was expecting this pattern:
ReturnType ^(blockVariableIdentifier)(ParameterType1, ParameterType2, ...) = ^{
//some code
};
How was blockVariableIdentifier repleaced with (^complexBlock)(void (^)(void)) ?
isn't it supposed to have void as a return, how come that we have return { ... } ?
I find this code confusing, can you explain it ?
code source.
Update:
Given this typedef:
typedef void (^XYZSimpleBlock)(void);
I can simplify the declaration of complexBlock to:
void (^(^complexBlock)(XYZSimpleBlock))(void);
but I still can't figure out how this is equivalent to
XYZSimpleBlock (^betterBlock)(XYZSimpleBlock);
It is said right there at your link:
The complexBlock variable refers to a block that takes another block as an argument (aBlock) and returns yet another block.
This is just block that returns another block. There is also simplification of that code:
typedef void (^XYZSimpleBlock)(void);
XYZSimpleBlock (^betterBlock)(XYZSimpleBlock) = ^(XYZSimpleBlock aBlock) {
...
return ^{
...
};
};
Return type of betterBlock is XYZSimpleBlock so you return another block from there. void relates only for XYZSimpleBlock, look at its typedef - block that have no arguments and do not return anything
Also this awesome answer will be useful to you
According to this answer (found in #Ahmed Lotfy's comment above) this is just a matter of language/compiler design, but to visuailze how that happened I made this image:
The expected syntax (top) of a block that takes a block as argument and returns a block, that is not accepted by Xcode.
The desgined syntax (bottom).
Visually what happened is just cutting the boxed )(void) and putting it at the end.
we can see it as analogy to a functin returning a block:
void (^f())(void) {
return ^{ ... };
}
just by substituting f() with (^myBlock)(void (^) (void)) which is a block taking another block as a parameter with no return.
I hope we can live with that.
Related
Today I want to learn how to use typedef to define a block type. I read the related content from "IOS developer library". But I feel confused about some statement of the sentences.
As an example, you can define a type for a simple block with no arguments or return value, like this:
typedef void (^XYZSimpleBlock)(void);
You can then use your custom type for method parameters or when creating block variables:
XYZSimpleBlock anotherBlock = ^{
...
};
- (void)beginFetchWithCallbackBlock:(XYZSimpleBlock)callbackBlock {
...
callbackBlock();
}
Custom type definitions are particularly useful when dealing with blocks that return blocks or take other blocks as arguments. Consider the following example:
void (^(^complexBlock)(void (^)(void)))(void) = ^ (void (^aBlock)(void)) {
...
return ^{
...
};
};
The complexBlock variable refers to a block that takes another block as an argument (aBlock) and returns yet another block.
Rewriting the code to use a type definition makes this much more readable:
XYZSimpleBlock (^betterBlock)(XYZSimpleBlock) = ^ (XYZSimpleBlock aBlock) {
...
return ^{
...
};
}
The question I want to ask is that how to convert the expression from
void (^(^complexBlock)(void (^)(void)))(void)
to
XYZSimpleBlock (^betterBlock)(XYZSimpleBlock) = ^ (XYZSimpleBlock aBlock)
?
I have used blocks a few times but never with declaring a typedef first.
I am trying to setup this definitions on a class (lets call it ClassA)
typedef void (^myBlock)();
#property (nonatomic, copy) myBlock onCompletion;
Then I create an instance of the object this class represents and do this:
ClassA *obj = [[ClassA alloc] init];
obj.onCompletion = ^(){
// my code here
};
it is complaining "incompatible block pointer types assigning"...
can you guys explain?
Although you don't have to specify the return type of a block, you must specify void if your block doesn't take any parameters.
Check out this link for block reference: block syntax
Just guessing it might be because you left the block parameter type blank.
typedef void (^myBlock)(void); //Untested
At the request of the OP I am posting my original comment as an answer even though there are other answers stating the same thing:
You need to clearly specify that the block takes no parameters. Your typedef should be:
typedef void (^myBlock)(void);
The only difference in the declared block type myBlock and the type of the block literal might be the return type.
If you leave the return type out in a block literal the compiler figures it out automatically:
myBlock block1 = ^() { // everything is fine
return;
};
myBlock block2 = ^() { // does not work, types differ
return 1;
};
typedef void (^RequestProductsCompletionHandler)(BOOL success, NSArray * products);
I am having difficulty understanding what this line of code is doing in .h file.
Please explain in detail
typedef.
void (I know what void do, but whats the purpose here?).
(^RequestProductsCompletionHandler)(BOOL success, NSArray * products);
How to call it?
This is definition of objective-c block type with name RequestProductsCompletionHandler that takes 2 parameters (BOOL and NSArray) and does not have return value. You can call it the same way you would call c function, e.g.:
RequestProductsCompletionHandler handler = ^(BOOL success, NSArray * products){
if (success){
// Process results
}
else{
// Handle error
}
}
...
handler(YES, array);
Vladimir described it well. It defines a variable type which will represent a block that will pass two parameters, a boolean success and an array of products, but the block itself returns void. While you don't need to use the typedef, it makes the method declaration a tad more elegant (and avoids your having to engage in the complicated syntax of block variables).
To give you a practical example, one might infer from the name of the block type and its parameters that this defines a completion block (e.g. a block of code to be performed when some asynchronous operation, like a slow network request, completes). See Using a Block as a Method Argument.
For example, imagine that you had some InventoryManager class from which you could request product information, with a method with an interface defined like so, using your typedef:
- (void)requestProductsWithName:(NSString *)name completion:(RequestProductsCompletionHandler)completion;
And you might use the method like so:
[inventoryManager requestProductsWithName:name completion:^(BOOL success, NSArray * products) {
// when the request completes asynchronously (likely taking a bit of time), this is
// how we want to handle the response when it eventually comes in.
for (Product *product in products) {
NSLog(#"name = %#; qty = %#", product.name, product.quantity);
}
}];
// but this method carries on here while requestProductsWithName runs asynchronously
And, if you looked at the implementation of requestProductsWithName, it could conceivably look something like:
- (void)requestProductsWithName:(NSString *)name completion:(RequestProductsCompletionHandler)completion
{
// initiate some asynchronous request, e.g.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// now do some time consuming network request to get the products here
// when all done, we'll dispatch this back to the caller
dispatch_async(dispatch_get_main_queue(), {
if (products)
completion(YES, products); // success = YES, and return the array of products
else
completion(NO, nil); // success = NO, but no products to pass back
});
});
}
Clearly, this is unlikely to be precisely what your particular completion handler block is doing, but hopefully it illustrates the concept.
Mike Walker created a nice one page site that shows all possibilities to declare a block in Objective-C. This can be helpful to understand your problem as well:
http://fuckingblocksyntax.com
To quote his site, this is how you can define blocks:
As a local variable:
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};
As a property:
#property (nonatomic, copy) returnType (^blockName)(parameterTypes);
As a method parameter:
- (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName {...}
As an argument to a method call:
[someObject someMethodThatTakesABlock: ^returnType (parameters) {...}];
As a typedef:
typedef returnType (^TypeName)(parameterTypes);
TypeName blockName = ^(parameters) {...}
Assume such conditions:
Some operation does not provide possibility of returning the result.
This operation declared as callback
Using typedef not recommended
Some operation provide of returning the result.
This operation declared as callback
Using typedef not recommended
Assume such scenario:
void main() {
executeVoidOperation(methodNonVoid); // Must throw if method void?
executeNonVoidOperation(methodVoid); // Must throw if method non-void?
}
int methodNonVoid() {
return 0;
}
void methodVoid() {
}
void executeVoidOperation(void operation()) {
operation(); // Must throw if method non-void?
}
void executeNonVoidOperation(dynamic operation()) {
var result = operation(); // Must throw if method void?
print(result); // Result of void operation? (if such passed as argument)
}
Displayed results:
null
Questions (where I wrong?):
Null is object. From where this null appeared (as result) if void function cannot return result (even null)?
Functions with different return types in Dart assumed as the same (not conflicting) types?
How in Dart called this function transformations?
executeNonVoidOperation(methodVoid); works because the callback is defined as dynamic operation(). dynamic can be anything, including void. It's the same as if you just don't specify a type.
The null value stems from a simple rule in Dart. Quoted from the Dart Language Tour:
All functions return a value. If no return value is specified, the statement return null; is implicitly appended to the function body.
That means that every void method always returns null. If you try to return something else, you'll get a runtime error (in checked mode).
executeVoidOperation(methodNonVoid); is a bit more tricky - I'd expect it to throw a runtime error, but it seems the callback is treated as dynamic operation() instead of void operation(). Dart Editor's analyzer seems to think that, too. This may be either a bug or a design choice by the Dart team.
I want to call a c function from objective-c and pass objective-c function as a callback
the problem is this function has a callback as parameter, so I have to pass objective-c function as a call back to c function
here is the header of the c function
struct mg_context *mg_start(const struct mg_callbacks *callbacks,
void *user_data,
const char **configuration_options);
here is where I try to call it
- (void)serverstarted
{
NSLog(#"server started");
}
- (IBAction)startserver:(id)sender {
NSLog(#"server should start");
const char *options[] =
{
"document_root", "www",
"listening_ports", "8080",
NULL
};
mg_start(serverstarted(), NULL, options);
}
I have tried several ways to do it and searched the web to just get a clue how to do it but with not luck
here is the library I am incuding in my code
https://github.com/valenok/mongoose
Your chief problem is the first parameter to mg_start(), which is described in the declaration as const struct mg_callbacks *callbacks. You are trying pass a pointer to a function. (Actually you are trying to pass the result of a call to that function, which is even further from the mark.) That isn't what it says: it says a pointer to a struct (in particular, an mg_callbacks struct).
The example code at https://github.com/valenok/mongoose/blob/master/examples/hello.c shows you how to configure this struct. You have to create the struct and put the pointer to the callback function inside it. Then you pass the address of that struct.
Other problems with your code: your callback function itself is all wrong:
- (void)serverstarted
{
NSLog(#"server started");
}
What's wanted here is a C function declared like this: int begin_request_handler(struct mg_connection *conn), that is, it takes as parameter a pointer to an mg_connection struct. Your serverstarted not only doesn't take that parameter, it isn't even a C function! It's an Objective-C method, a totally different animal. Your use of the term "Objective-C function" in your title and your question is misleading; C has functions, Objective-C has methods. No Objective-C is going to be used in the code you'll be writing here.
What I suggest you do here is to copy the hello.c example slavishly at first. Then modify the content / names of things slowly and bit by bit to evolve it to your own code. Of course learning C would also help, but you can probably get by just by copying carefully.
As matt already said, you cannot pass an Objective-C method as callback where a C function
is expected. Objective-C methods are special functions, in particular the receiver ("self")
is implicitly passed as first argument to the function.
Therefore, to use an Objective-C method as request handler, you need an (intermediate) C function as handler and you have to pass self to that function, using the user_data argument. The C function can then call the Objective-C method:
// This is the Objective-C request handler method:
- (int)beginRequest:(struct mg_connection *)conn
{
// Your request handler ...
return 1;
}
// This is the intermediate C function:
static int begin_request_handler(struct mg_connection *conn) {
const struct mg_request_info *request_info = mg_get_request_info(conn);
// Cast the "user_data" back to an instance pointer of your class:
YourClass *mySelf = (__bridge YourClass *)request_info->user_data;
// Call instance method:
return [mySelf beginRequest:conn];
}
- (IBAction)startserver:(id)sender
{
struct mg_callbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = begin_request_handler;
const char *options[] =
{
"document_root", "www",
"listening_ports", "8080",
NULL
};
// Pass "self" as "user_data" argument:
mg_start(&callbacks, (__bridge void *)self, options);
}
Remarks:
If you don't use ARC (automatic reference counting) then you can omit the (__bridge ...)
casts.
You must ensure that the instance of your class ("self")
is not deallocated while the server is running. Otherwise the YourClass *mySelf
would be invalid when the request handler is called.