Next article: Friday Q&A 2009-08-21: Writing Vararg Macros and Functions
Previous article: Friday Q&A 2009-07-17: Format Strings Tips and Tricks
Tags: blocks fridayqna
Welcome back to another edition of Friday Q&A. I'm back from my break and ready to bring you more programming goodies. This week I want to take Landon Fuller's suggestion to write a followup to my original Friday Q&A on blocks now that the design is finalized and code available for them.
Although Apple has yet to ship blocks with any of their developer tools, they have released code for their blocks implementation due to their participation in the open-source compilers gcc and clang. Landon has used this code to put together PLBlocks, which allows building and running blocks-based code on Mac OS X 10.5. While there are no blocks-based APIs on 10.5 (aside from the very basics which are part of the runtime), they can still be used to great effect.
I'm not going to cover the installation or basic use of PLBlocks here, as the PLBlocks page covers this in great detail. If you want to follow along, go there and follow the directions. For more information about how blocks work, see Clang's blocks language spec and implementation spec.
I'm also going to assume that you know the basics of block syntax and usage, as covered in my last Friday Q&A on the subject. Today's is essentially Part II of that one. If you haven't already, go read that one first.
Fundamentals
Blocks are Objective-C objects. When you write a block in code, that is an expression of object type, much like the @"..."
constant string syntax gives you an expression of object type. You can then use this object like you would any other Objective-C object, by sending it messages that it responds to, putting it into containers, passing it as a parameter, returning it, etc.
There is a major difference from the constant string syntax. Unlike constant strings, blocks are not exactly the same each time through a piece of code. This is because blocks capture their enclosing scope, and that scope is different every time they're called. In short, each time code execution hits a ^{...}
construct, a new object is created.
Allocating a new object every time would be kind of slow, so blocks take an unusual approach: the object you get from a ^{...}
construct is a stack object. This means that it has the same lifetime as local variables, and will be destroyed automatically upon leaving the current scope. Weird, huh?
It's frequently useful for a block to outlive the scope where it was created. For example, you may want to return a block, or save it for later. For this to work, you must copy the block. You can do this like any other Objective-C object by sending it the -copy
message. And like any other Objective-C object, if you aren't running under Garbage Collection then you own the resulting object and must eventually dispose of it using -release
or -autorelease
.
This, then, is an example of returning a block from a method:
- (void (^)(void))block { return [[^{ ... } copy] autorelease]; }
Note that external variable capture gives const copies of those variables by default. In other words, this is not legal:
int i;
^{ i++; };
__block
keyword, like so:
__block int i;
^{ i++; };
__block
variables are significantly more costly than the regular kind, and have different semantics when applied to Objective-C object pointers (more details on that later), so rather than figure out a one-size-fits-all policy, the blocks guys decided it was better to just let the programmer choose.
Examples
I'm going to be showing a bunch of examples for how to use blocks with PLBlocks on 10.5. Those of you who want to follow along may wish to look at the example project I built, which you can get out of my public subversion repository here:
svn co http://www.mikeash.com/svn/PLBlocksPlayground/If you just want to browse the code you can just click on the link in that command.
Custom APIs
That's the basic idea of how they work, now let's see what we can do with them.
As I mentioned in my first blocks post, blocks essentially allow you to build new control constructs without needing to modify the language. Before we get into it, I want to introduce a little typedef to keep things simple. Most control-construct blocks are blocks which take no parameters and return no value. As such, it's nice to have that type wrapped up in something a little nicer to write:
typedef void (^BasicBlock)(void);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
...
[pool release];
void WithAutoreleasePool(BasicBlock block)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
block();
[pool release];
}
for(id obj in array)
WithAutoreleasePool(^{
[self createLotsOfTemporaryObjectsWith:obj];
});
Let's attack something a little more complicated. It's common in Cocoa programs to need to run some code after a short delay, using -performSelector:withObject:afterDelay:
. Often we'll use a zero delay to mean "run this code immediately after returning to the runloop". The trouble with this is that it requires an object and a separate method, and getting the relevant context over can be painful. Let's write a quickie blocks function instead:
void RunAfterDelay(NSTimeInterval delay, BasicBlock block)
{
[[[block copy] autorelease] performSelector: @selector(my_callBlock) withObject: nil afterDelay: delay];
}
@implementation NSObject (BlocksAdditions)
- (void)my_callBlock
{
void (^block)(void) = (id)self;
block();
}
@end
NSString *something = ...;
RunAfterDelay(0, ^{
NSLog(@"%@", something);
[self doWorkWithSomething: something];
});
Another thing that we frequently write is a critical section of code protected by a lock. Normally this would look like:
[lock lock];
...do stuff...
[lock unlock];
@try/@finally
block like so:
[lock lock];
@try
{
...do stuff...
}
@finally
{
[lock unlock];
}
@implementation NSLock (BlocksAdditions)
- (void)whileLocked: (BasicBlock)block
{
[self lock];
@try
{
block();
}
@finally
{
[self unlock];
}
}
@end
@try/@finally
you can return a value from the method from within the @try
block and it works, whereas doing this from inside the block will simply error, because you'll be returning a value from the block instead, which will make the block's type incompatible. This can be worked around by using a __block
qualified variable to hold the return value. In my opinion this is superior, as it helps discourage tricky behavior inside the critical section, where the potential for bugs is high.
A Stylistic Note
There are two interesting choices in the above code, both due to the same reason. The first choice is that these are functions, not methods. Since blocks are NSObjects, category methods on NSObjects can be used for them. Rather than a RunAfterDelay
function, we could write a -runAfterDelay:
method on NSObject. The second choice is to always put the block parameter last, even though it's the most significant parameter and would make more sense to go first.
The reason for both of these is that you want the block to come absolutely last so that your code remains readable when the block is split onto multiple lines. For example, imagine some nested blocks code using the above functions recast using methods instead:
[^{
for(id obj in array)
[^{
[self doImportantWork:obj];
} withAutoreleasePool];
} runAfterDelay: 0];
Collections
Using blocks with collections can make for powerful looping constructs. Let's start out with this really simple substitute for a for loop, as a method on NSArray
:
- (void)do: (void (^)(id obj))block
{
for(id obj in self)
block(obj);
}
for/in
loop, but without the ability to statically type the objects. (It would have been nice to have before Apple introduced for/in
, at least, illustrating the idea of adding your own control constructs using blocks.) Example use:
NSArray *array = ...;
[array do: ^(id obj){ NSLog(@"%@", obj); }];
- (NSArray *)map: (id (^)(id obj))block
{
NSMutableArray *new = [NSMutableArray array];
for(id obj in self)
{
id newObj = block(obj);
[new addObject: newObj ? newObj : [NSNull null]];
}
return new;
}
NSArray *people = ...;
NSArray *names = [people map: ^(id person){
return [NSString stringWithFormat: @"%@ %@", [person firstName], [person lastName]];
}];
-map:
method. By passing blocks around, we only have to write that loop once, then reuse it many times.
One more example, this allows filtering an array:
- (NSArray *)select: (BOOL (^)(id obj))block
{
NSMutableArray *new = [NSMutableArray array];
for(id obj in self)
if(block(obj))
[new addObject: obj];
return new;
}
NSArray *longStrings = [strings select: ^ BOOL (id obj) { return [obj length] > 5; }];
int
, not BOOL
, so allowing the compiler to infer the return value would produce a block of incompatible type. An alternative would be to cast the expression used in the return statement.
Here's an example of a use in a GUI application, for getting all the text fields inside a particular view:
NSArray *textFields = [[view subviews] select: ^(id obj){ return [obj isKindOfClass: [NSTextField class]]; }];
Callbacks
Callbacks-based APIs are a place where blocks really shine. Instead of passing a selector/delegate pair, or a function pointer/context pointer pair, pass a block. It makes it much easier to pass context around (since the block packages up all needed context automatically) and keeps all of the code nearby.
An obvious example of a callback is notifications. While there's often a benefit to having separated methods for notifications, the implementation is easy and it can sometimes make for nicer code:
@implementation NSNotificationCenter (BlocksAdditions)
- (void)addObserverForName: (NSString *)name object: (id)object block: (void (^)(NSNotification *note))block
{
[self addObserver: [block copy] selector: @selector(my_callBlockWithObject:) name: name object: object];
}
@end
my_callBlockWithObject:
method is implemented in a category on NSObject
, much like the my_callBlock
method seen earlier, except that it takes a parameter and passes that parameter on to the block.
You can use it like so:
[[NSNotificationCenter defaultCenter] addObserverForName: NSApplicationDidBecomeActiveNotification
object: nil
block: ^(NSNotification *note){ NSLog(@"Did become active"); }];
Sheets are a good example of a painful callbacks-based API in Cocoa. You have to implement a callback method, then cram all of the state associated with the sheet, which is often large, either into the single void *
context parameter provided, or into instance variables. Neither way is particularly nice.
Here is a small category which translates this API to use blocks instead:
@implementation NSApplication (SheetAdditions)
- (void)beginSheet: (NSWindow *)sheet modalForWindow:(NSWindow *)docWindow didEndBlock: (void (^)(NSInteger returnCode))block
{
[self beginSheet: sheet
modalForWindow: docWindow
modalDelegate: self
didEndSelector: @selector(my_blockSheetDidEnd:returnCode:contextInfo:)
contextInfo: [block copy]];
}
- (void)my_blockSheetDidEnd: (NSWindow *)sheet returnCode: (NSInteger)returnCode contextInfo: (void *)contextInfo
{
void (^block)(NSInteger returnCode) = contextInfo;
block(returnCode);
[block release];
}
@end
Another example of a similarly painful API is NSURLConnection
. It provides two modes: synchronous and asynchronous. The synchronous mode can only be used from a secondary thread which can be blocked for arbitrarily long amounts of time, since any network operation can take a long time to complete. The asynchronous mode requires writing a lot of boilerplate code. Let's write a method that adds an asynchronous mode that simply makes a single call to a block when it's done to hand over the data, the response metadata, and the error, if any. To do this, we'll just use the synchronous API in a background thread. This code uses two functions, RunInBackground
and RunOnThread
, which are blocks-based APIs for spawning a new thread and running a block on an existing thread, respectively. These functions are pretty straightforward to implement and I won't duplicate them here, but you can get them in the sample project if you need.
Here's what the code looks like:
@implementation NSURLConnection (BlocksAdditions)
+ (void)sendAsynchronousRequest: (NSURLRequest *)request
completionBlock: (void (^)(NSData *data, NSURLResponse *response, NSError *error))block
{
NSThread *originalThread = [NSThread currentThread];
RunInBackground(^{
WithAutoreleasePool(^{
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [self sendSynchronousRequest: request returningResponse: &response error: &error;];
RunOnThread(originalThread, NO, ^{ block(data, response, error); });
});
});
}
@end
RunInBackground
ends with a call to RunOnThread
which uses another block to call back to the originating thread. This kind of nested block messaging is handy for making asynchronous callbacks in a concise manner.
Here's an example of using this API:
NSURLRequest *request = [NSURLRequest requestWithURL: [NSURL URLWithString: @"http://www.google.com/"]];
[NSURLConnection sendAsynchronousRequest: request
completionBlock: ^(NSData *data, NSURLResponse *response, NSError *error){
NSLog(@"data: %ld bytes response: %@ error: %@", (long)[data length], response, error);
}];
Caveats
It should be no surprise that there are some things to look out for when working with blocks.
When blocks are copied, any local object variables they refer to get automatically retained. They are then automatically released when the block is destroyed. This is convenient to ensure that the references remain valid. Any reference to self
is a reference to a local object variable, causing self
to be retained. Any reference to an instance variable is an implicit reference to self
and causes the same thing. However, this makes it easy to cause a retain cycle in some instances. Imagine using a more fleshed-out version of that blocks-based notification API which allows unregistering the notification. If your block refers to self
in any way, and you do the standard Cocoa thing of unregistering the notification in -dealloc
, your object will leak because the block will hold a reference to your object.
A simple workaround to this lies in the fact that __block
variables are not retained. This is because such variables are mutable, and automatic memory management of them would require each mutation to generate memory management code behind the scenes. This was seen as too intrusive and difficult to get right, especially since the same block may be executing from multiple threads simultaneously. Thus you can avoid the retain cycle like so:
__block MyClass *blockSelf = self;
^{
[blockSelf message];
[blockSelf->ivar message];
};
__block
variables, just like Objective-C objects, since they really are Objective-C objects as well. However, the compiler doesn't see them this way. To help with this, the compilers have added an attribute, __attribute__((NSObject))
, which causes struct pointers to be treated like Objective-C objects as far as their block retain/release semantics. We can assume that Apple will be applying this attribute to all CFTypes
on 10.6. While we're stuck in 10.5, however, they won't have this attribute, and so CoreFoundation objects will not be correctly memory managed when captured by blocks. To avoid problems, either avoid capturing CoreFoundation objects in blocks (declaring the variables to be of the equivalent Objective-C toll-free bridged type instead will convince the compiler to retain/release them) or ensure that the lifetime of the CF objects is at least as long as the lifetime of the block.
Another pitfall with blocks is due to the fact that they are stack objects. Using the ^{...}
syntax is essentially the same, behind the scenes, as declaring a local variable and then taking its address for something. The address can be passed around, but as soon as you leave the scope in which the local variable was declared, it's no longer valid. Thus, something as innocent as this ends up being broken code:
BasicBlock block;
if(condition)
block = ^{...};
else
block = ^{...};
if
statement (and the else
clause) is a separate scope from the main body which is destroyed as soon as control flow exits the if
/else
clause. The block reference which is being stored in block
is invalid as soon as control flow returns to the main body! It's simple to fix this just by copying the blocks:
BasicBlock block;
if(condition)
block = [[^{...} copy] autorelease];
else
block = [[^{...} copy] autorelease];
Conclusion
That wraps up this week's Friday Q&A. You've seen how to get blocks up and running with your current tool chain, a few examples of how they can be used in a useful manner, and some problems to watch out for. Now you're ready to start using blocks in your 10.5 apps today, no need to wait for 10.6 to ship.
Questions about blocks? Have your own ideas for how best to use them? Post below.
Come back next week for another edition of Friday Q&A. As always, Friday Q&A is driven by your ideas. If you have a topic that you would like to see covered here, post it below or e-mail it to me.
Comments:
I'm impressed by how much this one feature increases the expressiveness of the language. With this, who needs Ruby anymore? ;)
I'm writing a guide on C blocks. I added some references to this blog entry in it. It's over at: http://thirdcog.eu/pwcblocks/
It reminded me that I wrote a blog post on this about a week ago or so, solving it in the old Leopard-y way (ie, without blocks)... http://www.degutis.org/dev/2009/08/01/next-runloop-trick/ if anyone is interested!
It seems worth noting to me that, unlike ObjC objects, you cannot execute a block that == nil. Otherwise you will crash, no question about it. Simplest way around this is to check it with a simple if() before executing, such as:
if (block)
block();
That is all.
As for the if check, if you need to be told to use an if statement to avoid calling a nil block, then you are on the wrong blog.
Can't wait for blocks but in the meantime I have been using the NSProxy forwardInvocation pattern to capture a method call into an NSInvocation and then use perform selector, with delay, on thread, etc on the invocation instance.
Dave Dribin wrote about it at http://toxicsoftware.com/grab-that-invocation/ way back when and I've been using a customized version which I posted about at: http://rel.me/2009/05/22/nsinvocation-nsproxy-forwardinvocatio/
For example:
[[self gh_proxyAfterDelay:0] listWithOffset:40 limit:20];
Anyway, thought it might be useful to share this technique since you have the RunAfterDelay example above. Not sure if its really that related to the blocks proposal..
Looking forward to the next one.
But with 10.6 it seems that we'll be getting new developer tools, whereby we would not need to use PLBlocks? I guess I'm a little unclear here (maybe I'm also reading the wrong blog).
(Would we be able to run part of the new dev tools (including the blocks bits) on 10.5.9 or whatever? Or would we forever be restricted to PLBlocks based implementations when not on Snow Leopard? (Since currently unemployed I'm trying to make my PowerMac dual G5 last as long as possible.)
Thanks for all the thought provoking examples. When I look at blocks they seem easy way to roll up context when defining variable (per object, rather than class defined) behavior. In those cases distinguishing invocation via function call or methods doesn't seem to be critical, yes?
The basic situation appears to be public, and not covered by NDA, via Apple's into to Grand Central Dispatch here: http://images.apple.com/macosx/technology/docs/GrandCentral_TB_brief_20090608.pdf
Apple's blocks implementation is currently limited to open source release in gcc and Clang. This is what PLBlocks is built from. It will make an appearance in Snow Leopard. Traditionally, Apple does not make major language/API changes in the middle of an OS life cycle, so we can say pretty confidently that blocks support will not be showing up in the 10.5.x series.
On 10.5, your sole choice (unless you want to build your own) for blocks is PLBlocks. PLBlocks requires installing and using a custom compiler for blocks-using code, however this is extremely easy to do. It also requires shipping a framework which contains the blocks runtime code; this is also very easy. PLBlocks is built in such a way that blocks-using code built on 10.5 using PLBlocks will continue to work fine on 10.6 and will not conflict with the built-in blocks system.
On 10.6, you have two choices. First, you can use the Apple-standard compilers to build code with blocks. This code will require 10.6 to run, because it will require Apple's blocks runtime. It might be possible to backport this runtime to 10.5, but I doubt that anyone will. Symbol conflicts will make life unpleasant. The PLBlocks approach of using different symbol names to ensure no conflicts can happen seems like the superior way.
The other choice is to use PLBlocks even on 10.6, which will generate 10.5-compatible code. Just like on 10.5, you'll be installing and building with a custom compiler and will be shipping the PLBlocks framework.
If you're on a G5 then your only choice is to use PLBlocks, since 10.6 won't be available. However there's no real disadvantage to PLBlocks; the semantics and functionality are identical to what Apple will give us. This only makes sense, as PLBlocks is built with apple's code! The main advantage on 10.6 is the presence of lots of tasty APIs that use blocks, like Grand Central Dispatch, which you can't use on 10.5. But as far as blocks themselves go, there's nothing wrong with using PLBlocks to get them.
As for function call/method invocation, the only point I was making was that there's no reason to expect the block call syntax to tolerate a nil block just because the [] message send syntax does. It's an unrelated operation with unrelated semantics.
"All Blocks are constructed to be Objective-C objects regardless of whether the Objective-C runtime is operational in the program or not"
Even if there is no ObjC runtime, the structs representing blocks will be objective-c objects; if you write a C or C++ library that uses blocks, you can link that library to an ObjC application, and any blocks that are returned will be Objective-C objects.
Also, if blocks don't inherit from NSObject, how come performSelector:withObject:afterDelay: works with blocks, as per Mike's examples above?
@interface _PLAbstractBlock : NSObject
I can see no reason for them not to inherit from NSObject, and many reasons for them to.
Really? Anyone thinking if blocks are needed in there code for code they've written, shouldn't be writing code. They may or may-not be needed in library code, by they should never be needed otherwise.
Wow.
Anyway, I might be at the wrong blog too, as I am an objective-c noob, and a lot of the material on this blog goes WAY above my head. Still an interesting read, and hopefully once I get a better grasp of objective-c and cocoa, it will click into place :)
Keep up the good work!
If you're not worried about using the 10.6 runtime but just thinking about being able to take advantage of 10.6 advances, you can start out writing 10.5/iPhone code with PLBlocks now, then just move your code to the newer compilers and OSes when the time comes. PLBlock syntax is 100% identical to the official Apple syntax so your code will just keep right on working.
As for the if statement, you're exactly right. Cautioning that you'll crash if you call a nil block, it's not surprising but it's not 100% obvious either. Showing people how to use an if statement in C, that's kind of ridiculous in context.
void (^block)(void) is the declaration syntax for a block variable. You should already be familiar with this syntax from the first blocks article.
= (id)self; assigns the value of 'self' to 'block'. The cast is because 'self' is of type NSObject* in this code. Remember that blocks are objects too, and NSObject descendants, so we're basically just telling the compiler some more type information about what's going on.
block(); just calls the block declared above, which is pointing to the same object as 'self'.
If that did not answer your question, I recommend being more specific.
...
- contextInfo:[block copy]];
+ contextInfo:CFRetain([block copy])];
...
- [block release];
+ CFRelease(contextInfo);
Since objC retain/release are short-circuited, you have to CFRetain/CFRelease the block to avoid blowing up.
Given the example of the NSURLConnection block category (which is pretty close to real world scenario) are there and performance issues to be concerned about when nesting, copying, calling block methods, etc as compared to the non-block ways? I have no idea what the compiler is doing behind the scenes and was very curious if blocks reduce performance.
In short, the extra costs incurred, if any, are extremely small and will be far outweighed by the actual work being done by NSURLConnection.
Invoking a block is cheap. Cheaper than sending a message. This part can be ignored.
There are basically two costs incurred by the block approach. First, you have to pay for copies of captured local variables, and for the extra indirection needed when accessing __block qualified local variables. Second, you have to pay for copying the block itself, which includes a memory allocation for the block, another one for __block variables if there are any, copying the captured locals across, and performing memory management on captured ObjC object pointer variables.
Nothing here is all that dire. Memory allocations are pretty cheap, extremely cheap when compared to the cost of performing an HTTP request. There isn't much data to copy, so they'll go very fast.
For completeness, you also have to consider the cost incurred by the normal approach. To get equivalent functionality with NSURLConnection, you'll generally have a delegate object for each connection, which means, guess what, allocating memory for a new instance of it, copying values into it, etc. This will end up being pretty similar to the cost of using a block. Both cases will be dwarfed by the NSURLConnection itself.
Are there any open sourced collections of block based categories yet for UIKit? That would be super useful.
copy
a block and the declaration of it had gone out of scope, and nothing broke.
Turns out, that the block I declared did not use any variables from it's scope. This way, a global block object is created (
NSGlobalBlock
). This type of block object is allocated in some kind of global memory, and copying and retaining this block is a no-op. Even more, releasing it is a no-op.
It took me hours to, ironically, break my code.
For more info on this, see http://cocoawithlove.com/2009/10/how-blocks-are-implemented-and.html
It may sound odd, but it's extremely useful when you want to create a block that only takes one argument, which is a block that only takes one argument, which is a block that only takes one argument, which is a block that (etc, etc ...)
Just I thought it might also be helpful to someone besides me.
(Disclaimer: I've only tried this with LLVM + clang)
dispatch_block_t blk = ^{
if (some_mutable_condition)
blk(); // loop
};
blk(); // start loop
The problem is this doesn't work. When called within the block, `blk` refers to the pointer it was declared as, before it was assigned. Once assigned, it has a garbage reference and will crash. And don't get to thinking this will fix it, because it doesn't solve the problem at all:
dispatch_block_t blk;
blk = ^{
if (some_mutable_condition)
blk(); // loop
};
blk(); // start loop
One solution is to pass the block in, such as `blk(blk)` which is really lame since it requires changing your API. A better solution is to declare the block as a pointer to the block, and assign to the dereferenced pointer. This way, you still have access to the same memory address within the block as outside it, since that never changes, and can easily get to the -- well, you get the idea, it's just a pointer, which is a perfect work-around in and of itself for this problem. So you can do this and it works just fine:
dispatch_block_t *blk;
*blk = ^{
if (some_mutable_condition)
(*blk)(); // loop
};
(*blk)(); // start loop
This can be very useful when you want to implement a loop whose iterations are asynchronously performed, and thus you can't use a traditional for-loop or while-loop.. (I used this in conjunction with NSEnumerator in particular).
One question: you state that you've chosen to used functions instead of methods for the sake of readability.
However, I imagine there are many situations where you might want to execute a block by calling a method on it. For instance, if you defined a -performBlock method on NSObject, you could stuff block objects into NSUndoManager:
[[undoManager prepareWithInvocationTarget:^{
NSLog( @"hello world" );
}] performBlock];
Have I missed anything, or would this be a worthwhile reason to use methods?
if(condition)
block = ^{...};
else
block = ^{...};
seem no error, i already test like below:
typedef void (^BasicBlock)(void);
BasicBlock block;
if(condition)
block = ^{...};
else
block = ^{...};
block();
can you explain it?
willonboyzhang$gmail.com
In short, you can't determine whether code like that is good or not by testing it. It may work for you but fail elsewhere.
I’m wondering how to turn the RunAfterDelay example into ARC-code.
I just removed the autorelease call and the compiler doesn’t complain, the app runs smoothly.
But how can I be sure the block will be released by ARC at some point?
I read your article on ARC and the __bridge… casting annotations. Are they needed here?
I hope this is not a silly question!
right now (after update to iOS 7) I have been experimenting some issues with:
@implementation NSNotificationCenter (BlocksAdditions)
- (void)addObserverForName: (NSString *)name object: (id)object block: (void (^)(NSNotification *note))block
{
[self addObserver: [block copy] selector: @selector(my_callBlockWithObject:) name: name object: object];
}
when i call:
[[NSNotificationCenter defaultCenter] addObserverForName: NSApplicationDidBecomeActiveNotification
object: nil
block: ^(NSNotification *note){ NSLog(@"Did become active"); }];
before iOS 7 it worked but right now is not working :(
do you have any idea why?
Regards.
RunAfterDelay
under ARC is something like
[[aBlock copy] performSelector: @selector(my_callBlock) withObject: nil afterDelay: aDelay];
though I wonder why not just use
dispatch_after
as the guts?Learned a lot of new things!
Comments RSS feed for this page
Add your thoughts, post a comment:
Spam and off-topic posts will be deleted without notice. Culprits may be publicly humiliated at my sole discretion.
isFirst YES
Great article !!
Though in iterations you forgot two things:
1. Why not include as parameter the index of object in the array ? That'd be quite useful, also we can't break out of the loop in the middle from the block... A BOOL *stop parameter would be nice. :P
2. You didn't show NSDictionary iterations where you can get ass parameter of the block both keys and values at the same time, and avoid writing [dict objectForKey: key] all the time.
Anyway, it's very nice, thanks.