Block references as instance vars in Objective-C
Asked Answered
D

2

45

I was wondering if it's possible to store a reference to an anonymous function (block) as an instance variable in Objective-C.

I know how to use delegation, target-action, etc. I am not talking about this.

Deppy answered 12/7, 2010 at 16:30 Comment(0)
P
88

Sure.

typedef void(^MyCustomBlockType)(void);

@interface MyCustomObject {
  MyCustomBlockType block;
}
@property (nonatomic, copy) MyCustomBlockType block; //note: this has to be copy, not retain
- (void) executeBlock;
@end

@implementation MyCustomObject
@synthesize block;

- (void) executeBlock {
  if (block != nil) {
    block();
  }
}

- (void) dealloc {
  [block release];
  [super dealloc];
}
@end

//elsewhere:

MyCustomObject * object = [[MyCustomObject alloc] init];
[object setBlock:^{
  NSLog(@"hello, world!");
}];

[object executeBlock];
[object release];
Patric answered 12/7, 2010 at 16:46 Comment(9)
is there a way to have a generic type for a block? I hate that i need to have a typedef there.Deppy
@Jacob yeah, you can do: void(^)(void) (or whatever block signature you need) anywhere you see MyCustomBlockType. However, I promise that using the typedef will make things much easier to grok in the long run. However, there's no way to say "any block with any return value and any parameters. You must be explicit about the signature.Patric
@Jacob You're still limited by C's type system. There is no such thing as a "generic" block, though in C++ you might be able to do some pretty mind-bending things with templates and blocks. More realistically, type id is generic with respect to Objective-C types, so taking and returning (possibly nil) id's gives you a lot of flexibility.Pure
Why copy, not strong? Even if I'm using ARC?Mullin
@MattDiPasquale "answered Jul 12 '10 at 16:46" means this answer pre-dates ARC ;)Patric
Why the release after the call to executeBlock? You'd only want to do that if you were absolutely sure you wouldn't be calling it again, no? Or is that call necessary to release the captured variables in the stack block?Artistic
@BrentDillingham the point was to illustrate how to have a block as an ivar. The allocation, execution, and immediate release of a helper object are just to provide some context of how one might use this.Patric
@DaveDeLong Thanks - I realize now I mistakenly thought you were releasing the block itself as opposed to the object. As usual I read too quickly.Artistic
Just to answer the EXPLICIT question asked by Jacob: @property (nonatomic, copy) void (^userObject)();Pressurize
P
12

Yes, you most certainly can store a reference to a (copy) of an Objective-C block. The variable declaration is a little bit hairy, like C function pointers, but beyond that it's no problem. For a block that takes and id and returns void:

typedef void (^MyActionBlockType)(id);

@interface MyClass : NSObject 
{

}

@property (readwrite,nonatomic,copy) MyActionBlockType myActionBlock;
@end

will do the trick.

Pure answered 12/7, 2010 at 16:47 Comment(3)
The definition is definitely hairy. I much prefer using typedef to simply if. Then you can do insane things "blocks that accept a block as a parameter and return a block" and not get lost in the parenthesis. :)Patric
is this typedef void (^MyActionBlockType)(id); correct? shouldn't it be typedef void (^MyActionBlockType)(id obj);Twirl
@RubberDuck, the name of the variable (obj) is irrelevant to the type, thus you don't need to declare it in typedef. The name is only relevant when you implement the block;Heller

© 2022 - 2024 — McMap. All rights reserved.