I have:
[self schedule:#selector(tickhealth)];
And tickHealth method only has one line of code:
-(void)tickHealth
{
[hm decreaseBars:0.5];
}
is it possible to use block objects in place of a selector. for example something like:
[self schedule:^{
[hm decreaseBars:0.5];
}];
As Caleb & bbum correctly pointed out you cannot simply pass a block to your existing (and unchanged) - (void)schedule:(SEL)selector; method.
You can however do this:
Define block type:
typedef void(^ScheduleBlock)();
Change schedule: method to be defined similar to this:
- (void)schedule:(ScheduleBlock)block {
//blocks get created on the stack, thus we need to declare ownership explicitly:
ScheduleBlock myBlock = [[block copy] autorelease];
//...
myBlock();
}
Then call it like this:
[self schedule:^{
[hm decreaseBars:0.5];
}];
Further Objective-C block goodness compiled by Mike Ash that will get you kickstarted with blocks:
http://mikeash.com/pyblog/friday-qa-2008-12-26.html
http://mikeash.com/pyblog//friday-qa-2009-08-14-practical-blocks.html
http://mikeash.com/pyblog/friday-qa-2011-06-03-objective-c-blocks-vs-c0x-lambdas-fight.html
You can't just pass a block in place of a selector because those two things have different types. However, if you have control over the -schedule: method, you can easily modify it to accept and use a block in place of a selector.
Related
I'm in a bit of a pickle. I know that calling [self methodName] from within a block will lead to a retain cycle.
However in this class due to multithreading I cannot allow execution of the method that the block is accessing from anywhere else other than the block, as it would potentially lead to serious problems.
Current code:
if (_getRunning==NO){
__weak SyncArrayMT *_weak_self = self;
_get_t = ^void (void){
_weak_self->_getRunning = YES;
NSArray *objects = [_weak_self get:getQuery
usingClassCtor:ctor
withAuthBlock:authBlock];
if (_weak_self.getBlockCb)
_weak_self.getBlockCb(objects);
_weak_self->_getRunning = NO;
};
}
Does exactly that, it calls [self getmethod]. While its ok for the dispatched block to run this method, I do not want anything outside this class calling this method.
So, would it be ok to override this inherited method as such:
- (NSArray *) get:(NSString *)getQuery usingClassCtor:(initBlock)initCb withAuthBlock:(authenticate)authBlock
{
NSLog(#"Direct call to get is not allowed - use the threaded method");
return nil;
}
And then change the block to this:
_get_t = ^void (void){
_weak_self->_getRunning = YES;
NSArray *objects = [super get:getQuery
usingClassCtor:ctor
withAuthBlock:authBlock];
if (_weak_self.getBlockCb)
_weak_self.getBlockCb(objects);
_weak_self->_getRunning = NO;
};
I have tried it and it works without doing a call to the [self getMethod], but will super be retained, properly released, etc? Yes I am using ARC. Would calling super within a block lead to any problem ? Is there a hack to get a __weak to super instead ?
Alternatively, how can I disallow direct calls to [self getMethod] (which is inherited) and only use it internally ?
I know that Objective-C doesn't exactly implement this, but I know there are tricks, such as declaring and implementing a method in the implementation file only.
EDIT#1:
I have tried with SEL & IMP and function pointers. Problem is that IMP and function pointers require as a parameter an instance, and this renders the hole point mute:
NSString * (*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[super methodForSelector:#selector(sendObjectsPassingTest:withAuthBlock:)];
NSString *reply = getFuncPtr(_weak_self,#selector(sendObjectsPassingTest:withAuthBlock:),predicate,authBlock);
This simply calls the inherited method. Trying to use it with super simply gives an error. At this point I will go ahead and simply use super within the block, and try and profile to see if it leads to any retain cycle.
EDIT#2:
Based on newacct's answer, this is what I ended up doing:
typedef NSArray * (* getFuncPtr)(id,SEL,id,id,id);
...
...
__weak SyncArrayMT *_weak_self = self;
_getMethod = (NSArray * (*)(id,SEL,id,id,id))[[[self class] superclass] instanceMethodForSelector:#selector(get:usingClassCtor:withAuthBlock:)];
_get_t = ^void (void){
NSArray *objects = _weak_self->_getMethod(_weak_self,#selector(get:usingClassCtor:withAuthBlock:),getQuery,ctor,authBlock);
}
I am hoping this should avoid any retain cycles, although I haven't actually profiled it yet.
I know that calling [self methodName] from within a block will lead to
a retain cycle.
That is not true in general. The block will retain self, yes. But there will only be a "retain cycle" if self somehow retains the block. In this case, it does.
but will super be retained
Yes, self will be retained (super is a call on self with a different method lookup pathway).
I have tried with SEL & IMP and function pointers. Problem is that IMP
and function pointers require as a parameter an instance, and this
renders the hole point mute:
NSString * (*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[super methodForSelector:#selector(sendObjectsPassingTest:withAuthBlock:)];
NSString *reply = getFuncPtr(_weak_self,#selector(sendObjectsPassingTest:withAuthBlock:),predicate,authBlock);
This simply calls the inherited method. Trying to use it with super simply gives an error. At this point I will go ahead and simply use super within the block, and try and profile to see if it leads to any retain cycle.
There are many wrong points here. First, as said above, super is a call on self (there is no such thing as a super object), so it would be sufficient to get the IMP for the method in the superclass, and call it on self.
BUT, [super methodForSelector:... does not get the method in the superclass. It actually gets the method in this class. The super in [super methodForSelector:... affects which methodForSelector: method is called. However, no class ever overrides methodForSelector:, so there is actually no difference between [super methodForSelector:... and [self methodForSelector:.... As said above, super calls the method on self, so it still finds the method based on the class of the current object.
You can get the right IMP by using the class method +instanceMethodForSelector::
NSString *(*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[[[self class] superclass] instanceMethodForSelector:#selector(sendObjectsPassingTest:withAuthBlock:)];
However, using the above will not work correctly if the current object is an instance of a subclass, because then [self class] will be the subclass. So to make sure it does what we want, we need to hard-code the name of our current class, or the superclass:
NSString *(*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[[SyncArrayMT superclass] instanceMethodForSelector:#selector(sendObjectsPassingTest:withAuthBlock:)];
NSString *reply = getFuncPtr(_weak_self,#selector(sendObjectsPassingTest:withAuthBlock:),predicate,authBlock);
It is also possible to do it using objc_msgSendSuper directly, but that function is not really that easy to use either. So I think you should stick with the IMP approach above.
I have a general routine, which takes a few parameters.
Something like:
-(id) doStuff:(int)A:(int)B:(int)C {
//doStuff
return object;
}
I have a UITableViewController, which houses a number of custom cells, each with their own ID. When 'Save' is hit, these cells are iterated and some cells need 'additional behaviour' when they are being saved.
Up to now, I've created a 'Callback' object, which stores an NSString * and a delegate in the custom cell. Upon being 'Saved', the cell looks, whether it has any callbacks to apply and uses
SEL sel = NSSelectorFromString(Sel);
if([Del respondsToSelector:sel])
[Del performSelector:sel withObject:Cell];
Now that works somewhat well..., however, it requires the method I pass to do a switch/case on the ID of the Cell that's passed, and I'd like to avoid that.
That's why I'd like to use blocks instead, but I don't really know how to store a parameterized block in a variable.
What I'm trying to do:
Declare a function block doStuff.
id (^doStuff) (int, int, int) = ^(int A, int B, int C) {
//does Stuff
};
And add the previously created block as callback
[Cell addCallback:(^doStuff)(1, 2, 3)];
The block must NOT be called at that moment, but stored in the cell and only called it when the time is right.
How would I go about this correctly?
Thank you very much.
Edit: What I'd also like to avoid is storing the parameters for the block in the cell and pass them upon calling, because that would require me to further specialize the cells unnecessarily.
It sounds like what you want is a block that calls your block, something like this:
[cell addCallback:^{ doStuff(1, 2, 3); }];
But this is a rather odd and convoluted design. It seems like there is probably a way to write it with only one block, but it's hard to give a solution that specific without a better idea of what you're doing.
The most straight forward way is to create a typedef containing how the block parameters should look like, then use it to declare a new property/ivar. The following sample code is copied from the Sensible TableView framework SCCellActions class:
typedef void(^SCCellAction_Block)(SCTableViewCell *cell, NSIndexPath *indexPath);
#interface SCCellActions : NSObject
...
#property (nonatomic, copy) SCCellAction_Block willDisplay;
...
#end
You could then set the property as follows:
cellActions.willDisplay = ^(SCTableViewCell *cell, NSIndexPath *indexPath)
{
cell.backgroundColor = [UIColor yellowColor];
};
Similarly, you could declare a parameter as follows:
...
- (void)callActionBlock:(SCCellAction_Block actionBlock)
{
if(actionBlock)
{
actionBlock(self.cell, self.cellIndexPath);
}
}
...
In which case the method should be called like this:
[myObject callActionBlock:^(SCTableViewCell *cell, NSIndexPath *indexPath {cell.backgroundColor = [UIColor yellowColor];}];
This answer is based on Chuck's suggestion and describes the pitfalls I encountered realizing it.
Creation:
Cell = [self CreateCell];
[Cell addCallback:^{ return doStuff(Cell, 1, 2, 3, 4) } At:ON_SAVE];
doStuff is a local block, declared before the cells. I was unable to add it directly to the cell, because I also needed a reference to the calling cell within the block.
Pitfall at this point: Class variables.
A block will only retain...or rather 'copy'...local variables, but not class variables.
Assuming that 'Cell' was a class variable and set by 'CreateCell', the block would work with the value of Cell at the time the block is executed.
As such, it is important to remember to declare a local variable, which assumes the value of the class variable if necessary.
Storage:
- (void) addCallback:(CallBlock_t)B At:(int)at {
//Creates a Callback-Object and passes it the block and adds it to an Array.
}
- (id) initWithBlock:(CallBlock_t)B At:(int)at {
self = [super init];
if(self) {
Block = [B copy]; //Yes, Copy. Not retain.
When = at;
}
return self;
}
Pitfall at this point: If the block is merely retained, the local block from the calling function will go out of scope and the program will fail with 'Bad Access'. Copy resolves this problem.
Of course you need to release the Block once you're done using it (in the dealloc of the callback class), but that's a given.
I hope this little explanation will save someone some grief.
Ok, so I understand how to avoid self retain cycles with blocks, how about cases when I send a message to self from a method within a block nested deeper in call stack like this:
- (void)methodA {
__block MyClass *blockSelf = self;
[someObject block:^{
[blockSelf methodB];
}];
}
- (void)methodB {
...
[self methodC];
...
}
- (void)methodC {
}
In this case [blockSelf methodB] is fine, but is sending [self methodC] from methodB causing retain cycle or not? Can't find the answer anywhere...
There’s no retain cycle there. When a Block literal is defined inside a method, the context that can be captured by the Block is limited to what is visible inside that method. In your example:
- (void)methodA {
__block MyClass *blockSelf = self;
[someObject block:^{
[blockSelf methodB];
}];
}
the Block literal, namely:
^{
[blockSelf methodB];
}
is able to see the following:
self and _cmd, which are hidden parameters available in every Objective-C method. If -methodA had formal parameters, the Block literal would be able to see them, too;
Any block-scope variables inside the function/method block, i.e., every local variable inside the method and which is visible at the point where the Block literal is defined. In the example, the only local variable inside -methodA is blockSelf which, because it’s __block-qualified, is not retained;
Any file-scope variables (aka global variables).
A Block literal is unaware (and, in the general case, cannot be aware) of what happens inside other functions/methods, hence whatever context is available inside called functions/methods is not captured by the Block literal. You only need to worry about the method where the Block literal is defined.
I’m using Apple’s convention of capitalising Block when referring to closures/lambdas (i.e., ^{}) and lowercase block when referring to C blocks (i.e., {}).
So I am doing this to initialize my selector:
//In .h
SEL selectors[3];
//In .m
selectors[0] = #selector(rotate);
selectors[1] = #selector(discharge);
And here is the problem:
When I call this in my init method in Cocos2d like this:
[self performSelector:selectors[0]];
it works fine, but when I call this line of code in a method called moveThings which is invoked through the schedule ([self schedule:#selector(moveThings:)]) at the end of my init method in Cocos2d it gives EXC_BAD_ACCESS. What is the problem with scheduling things?
UPDATE:
I have found there is a problem with the rotate function (the function being stored in selector[0]). Here it is:
-(void)rotate:(ccTime)delta {
if (((CCSprite *)[creature objectAtIndex:0]).rotation < 360) {
((CCSprite *)[creature objectAtIndex:0]).rotation++;
}
else {
((CCSprite *)[creature objectAtIndex:0]).rotation++;
}
}
If I comment the contents of the method out it works fine when called through moveThings and init.
If I change the methods contents with:
((CCSprite *)[creature objectAtIndex:0]).rotation++;
It fails... But, again, I would like to state that all of these things do work if I call it in my init method, even call it twice in a row, but it will not work (except when I take out the contents of the rotate method) if I call it through the moveThings: method which is being invoke through the schedule method it fails.
Further update:
If I call:
((CCSprite *)[creature objectAtIndex:0]).rotation++;
In moveThings (which is being, as I've said before, invoked by the schedule:(SEL) method) it fails. Where as long as it is not invoked through a method that is the called by schedule it works.
The problem is that when you call performSelector there are only two options:
have your selector take no arguments and leave the ":" off the #selector(foo) definition.
have your selector take either one or two arguments which both must be an NSObject or subclass
it is the latter that is messing you up here I suspect.
Here are the three forms of performSelector:
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
You'll note that the withObject arguments are all of type id which is an Objective C object.
The selector you're trying to use takes a ccTime which is a float and not an Objective C object as it's param and that is why things are crashing:
-(void)rotate:(ccTime)delta;
One option is to make a wrapper method that takes a wrapped ccTime and unwraps it and calls the rotate:(ccTime) method:
- (void) wrappedRotate: (NSNumber*) inDelta
{
[self rotate: [inDelta floatValue]];
}
then use
selectors[0] = #selector(wrappedRotate:);
and then call via:
[self schedule: #selector(moveThings:)]); // cocos2d schedule
...
- (void) moveThings: (ccTime) dt
{
[self performSelector: selectors[0] withObject: [NSNumber numberWithFloat: dt]];
...
}
One reason you are getting confused is because Cocos2d is using #selector in somewhat more complicated ways (see CCScheduler.m in the CCTimer::initWithTarget:selector:interval: and CCTimer::update: method in particular).
disclaimer: code typed into SO so not checked with a compiler, but the essence of what you need should be here.
One problem for sure is that you are using a variable declared inside a .h while initializing it inside the relative .m. According to the linking I'm not sure that just one variable selectors will exist (so that different files that include .h will have different versions).
First of all I suggest you to try adding the keyword extern to have
extern SEL selectors[3];
to tell your linker that it is initialized inside the relative .m and to use just that one.
I think your problem stems from your method definition which is - (void)rotate; and not - (void)rotate:(ccTime)dt;
You should adjust your selectors likewise.
If your method does not have any arguments then do not use a colon in your selector call.
// Requires #selector(foo:)
- (void) foo:(id)sender;
// Requires #selector(foo)
- (void) foo;
With reference to the code below, once a block has been put into an array, how could you take that block object and run the actual code in the block.
Another Question: If I call a method in a block like below, does that block encapsulate the code in that method or capture the signature of the method and call it that way?
-(void)blockCalledMethod
{
NSLog(#"Hello World");
}
-(void)programStart
{
NSArray * array = [[NSArray alloc] initWithObjects:[[^ { [self blockCalledMethod];} copy] autorelease],nil];
id pointerToBlock = [array lastObject];
}
Call it like this:
void (^pointerToBlock)(void) = [array lastObject];
pointerToBlock(); // because ^ {} is void(^)(void)
You cannot declare pointerToBlock as an id if you want to call it directly, because the compiler has to recognize it as a block type and not just an object.
If I call a method in a block like above, does that block encapsulate the code in that method or capture the signature of the method and call it that way?
I should think self refers to the calling class.