iOS. Passing block to block as parameter - ios

I am going around blocks and try to discover the ways that they can be used.
So I am wondering is it possible to pass block to block like parameter?
Here is some sample code:
//declaration
static id (^someBlock)(id) = ^(id someClass) {
// do some stuff to obtain class some class instance
// check if class instance respond to #selector
// if yes - perform selector
}
//usage
+ (instancetype)someMethod {
someBlock(SomeClass.class);
// do additional work and return some instance type
}
This works fine, but is not good enough, because we obligate caller to respond to selector if caller want to do some additional stuff when someBlock is completed.
So my question is how I can invoke someBlock block with parameter block which I want to be executed when someBlock is completed.
Some like:
//declaration
static id (^someBlock)(id, <b>^otherBlock</b>) = ^(id someClass, <b>????</b>) {
// do some stuff to obtain class some class instance
otherBlock();
}
Any advice?
PS: Please note that the question is not about passing block to method as parameter.
Thanks,
Venelin

Is this what you are looking for?
static id (^someBlock)(id, void (^otherBlock)()) = ^id (id someClass, void (^otherBlock)()) {
otherBlock();
return nil; // just because you declares a `id` return type
};
And call it like
someBlock(someClass, ^() {
NSLog(#"other stuff");
});

Related

Dynamic Method Resolution

i have known runtime gives the message a chance to be executed when If the implementation of the method is not found in the class or in its superclasses. It will start by sending the message + (BOOL)resolveInstanceMethod:(SEL)name to the class of the object: this method allows you to add the method at runtime to the class: if this message returns YES, it means it can redispatch the message.i try to return NO from + (BOOL)resolveInstanceMethod:(SEL)name, i thought that will make -(id)forwardingTargetForSelector:(SEL)aSelector be invoked(and dynamicMethodIMP will not be invoked), but still dynamicMethodIMP is invoked as same as the return value is YES. the Apple Doc said
If the method returns NO, the Objective-C runtime will pass control to the method forwarding mechanism(https://developer.apple.com/library/mac/releasenotes/Foundation/RN-FoundationOlderNotes/)
what is the different between return YES and return No from
+ (BOOL)resolveInstanceMethod:(SEL)name.
a piece of sample code like this :
`void dynamicMethodIMP(id self, SEL _cmd) {
NSLog(#"%# has added", NSStringFromSelector(_cmd));
}
+(BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == #selector(mustHas)) {
class_addMethod([self class], sel, (IMP) dynamicMethodIMP, "v#:");
return NO;
}
return [super resolveInstanceMethod:sel];
}`
[obj mustHas];
The docs you're reading are from the 10.5 release notes, but that specific statement is not part of the current documentation.
The implementation of class_resolveInstanceMethod() actually ignores your return value. It just checks if you've implemented +resolveInstanceMethod, calls it if you have, and then it looks up the original selector (just to cache the result). The only time your return value matters is when debugging the runtime.
There's no need to guess at how message forwarding works. It's all open source. Here's the function that calls +resolveInstanceMethod (runtime/objc-class.mm):
/***********************************************************************
* _class_resolveInstanceMethod
* Call +resolveInstanceMethod, looking for a method to be added to class cls.
* cls may be a metaclass or a non-meta class.
* Does not check if the method already exists.
**********************************************************************/
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// Resolver not implemented.
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
BOOL resolved = msg(cls, SEL_resolveInstanceMethod, sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}

How does filter method work?

-(instancetype)filter:(BOOL (^)(id value))block {
NSCParameterAssert(block != nil);
Class class = self.class;
return [[self flattenMap:^ id (id value) {
if (block(value)) {
return [class return:value];
} else {
return class.empty;
}
}] setNameWithFormat:#"[%#] -filter:", self.name];
}
This is the implementation of filter of ReactiveCocoa.I don't know what this code means.Also I can't get any reference to the second return method.
return [class return:value];
Also, what does this instancetype mean? Suppose the value is a string and I check whether its length is greater than 2. What will be returned by using filter method?
The filter method invokes the class method of the current class to get a RACStream subclass using that method. Using return: will give a signal that sends the value passed and then completes. Using empty gives a signal that immediately sends completed without sending a next value, which removes the value filtered value from the stream thanks to flattenMap: switching out the signal with the one being created.

Use an NSString to programatically access or create a method in Objective-C

I am trying to use an array of strings dynamically access methods at runtime within my class. For now the methods are already there, eventually I want to create them.
Is this possible?
For example:
bool nextLevel=NO;
for(NSString * match in gameLevels)
{
if([match isEqualToString:self.level])
{
nextLevel=YES;
}
else if(nextLevel==YES)
{
self.level=match;
nextLevel=NO;
}
}
//access method named self.level
Thank you in advance!
I use:
NSSelectorFromString(selectorString)
In your case, the selectorString would be:
NSString * selectorString = #"setLevel:";
This is 'setLevel' instead of 'level' because the Objective-C runtime will automatically expand dot properties to these selector names when assignment occurs.
To access a method based on a string, check the other answer.
To add a method in the runtime you need to create a IMP function or block.
If using a function, could be something like:
void myMethodIMP(id self, SEL _cmd)
{
// implementation ....
}
You could also use a block like this:
IMP blockImplementation=imp_implementationWithBlock(^(id _self, ...){
//Your Code here
}
Then you need to add the method, like this:
class_addMethod(yourClass, #selector(selectorName), (IMP) blockImplementation, encoding);
The encoding part is a special runtime encoding to describe the type of parameters your method receives. You can find that on the Objective-C runtime reference.
If you receive dynamic arguments on your generated methods, you need to use the va_list to read the values.

Need assistance regarding understanding typedefs and blocks

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) {...}

class instance changed in callback method comparing to the main instance

in cpp:
void Character::jump(CCLayer *layer){
if (this->isAnimationPlaying) return;
up_or_down = UP;
body->runAction(CCSequence::actions(
CCMoveBy::actionWithDuration(0.5, ccp(0, 50)),
CCCallFuncND::actionWithTarget(body, callfuncND_selector(Character::upDownDone), this),
// CCCallFuncN::actionWithTarget(body, callfuncN_selector(Character::upDownDone)),
NULL));
this->isAnimationPlaying = true;
}
void Character::upDownDone(CCNode *node, CCObject *ob){
this->isAnimationPlaying = false; // *this is different from the this(class instance) in jump method, seems this in upDownDone is a new created instance*
}
So How can I get the class instance in a callback method? And can I make the this same for the main class instance and the callback's class instance?
EDIT:
Character is a class which has no parent class, and body is a member variable which is an instance of CCSprite.
Thanks.
because you are using body to call the function Character::upDownDone.
you should use this to call it.
CCCallFuncND* callFunc = CCCallFuncND::actionWithTarget(first_arg, secend_arg, third_arg);
body->runAction(callFunc);
assume your secend_arg is callfuncND_selector(Character::upDownDone)
then,
the first_arg is the caller, ie. the class instance who calls this function, in your code is body. but actually it should be this, or any instance of Charactor class
the CCNode* node (the first para that is been passed to your calling function) is the action runner, ie. body in your code. because you are using body->runAction()
the CCObject* obj (the second para that is been passed to your calling function) is a void pointer which is exactly the same with third_arg.
another way is use
void Character::upDownDone(CCNode *node, void *ob){
(Character*)ob->isAnimationPlaying = false;
}
Seems like you call the Character::upDownDone method using the instance "body" instead of this .May be you want this:
CCCallFuncND::actionWithTarget(this, callfuncND_selector(Character::upDownDone), body),

Resources