Skip to content

Instantly share code, notes, and snippets.

@zwaldowski
Created May 4, 2011 12:08
Show Gist options
  • Save zwaldowski/955123 to your computer and use it in GitHub Desktop.
Save zwaldowski/955123 to your computer and use it in GitHub Desktop.
Perform blocks with delays and cancellation
//
// NSObject+Blocks.h
// Filemator
//
// Created by Zachary Waldowski on 4/12/11.
// Copyright 2011 Dizzy Technology. All rights reserved.
//
@interface NSObject (Blocks)
+ (id)performBlock:(void (^)(void))block afterDelay:(NSTimeInterval)delay;
+ (id)performBlock:(void (^)(id arg))block withObject:(id)anObject afterDelay:(NSTimeInterval)delay;
- (id)performBlock:(void (^)(void))block afterDelay:(NSTimeInterval)delay;
- (id)performBlock:(void (^)(id arg))block withObject:(id)anObject afterDelay:(NSTimeInterval)delay;
+ (void)cancelBlock:(id)block;
@end
//
// NSObject+Blocks.m
// Filemator
//
// Created by Zachary Waldowski on 4/12/11.
// Copyright 2011 Dizzy Technology. All rights reserved.
//
#import "NSObject+Blocks.h"
#import <dispatch/dispatch.h>
static inline dispatch_time_t dTimeDelay(NSTimeInterval time) {
int64_t delta = (int64_t)(NSEC_PER_SEC * time);
return dispatch_time(DISPATCH_TIME_NOW, delta);
}
@implementation NSObject (Blocks)
+ (id)performBlock:(void (^)(void))block afterDelay:(NSTimeInterval)delay {
if (!block) return nil;
__block BOOL cancelled = NO;
void (^wrappingBlock)(BOOL) = ^(BOOL cancel) {
if (cancel) {
cancelled = YES;
return;
}
if (!cancelled)block();
};
wrappingBlock = [[wrappingBlock copy] autorelease];
dispatch_after(dTimeDelay(delay), dispatch_get_main_queue(), ^{ wrappingBlock(NO); });
return wrappingBlock;
}
+ (id)performBlock:(void (^)(id arg))block withObject:(id)anObject afterDelay:(NSTimeInterval)delay {
if (!block) return nil;
__block BOOL cancelled = NO;
void (^wrappingBlock)(BOOL, id) = ^(BOOL cancel, id arg) {
if (cancel) {
cancelled = YES;
return;
}
if (!cancelled) block(arg);
};
wrappingBlock = [[wrappingBlock copy] autorelease];
dispatch_after(dTimeDelay(delay), dispatch_get_main_queue(), ^{ wrappingBlock(NO, anObject); });
return wrappingBlock;
}
- (id)performBlock:(void (^)(void))block afterDelay:(NSTimeInterval)delay {
if (!block) return nil;
__block BOOL cancelled = NO;
void (^wrappingBlock)(BOOL) = ^(BOOL cancel) {
if (cancel) {
cancelled = YES;
return;
}
if (!cancelled) block();
};
wrappingBlock = [[wrappingBlock copy] autorelease];
dispatch_after(dTimeDelay(delay), dispatch_get_main_queue(), ^{ wrappingBlock(NO); });
return wrappingBlock;
}
- (id)performBlock:(void (^)(id arg))block withObject:(id)anObject afterDelay:(NSTimeInterval)delay {
if (!block) return nil;
__block BOOL cancelled = NO;
void (^wrappingBlock)(BOOL, id) = ^(BOOL cancel, id arg) {
if (cancel) {
cancelled = YES;
return;
}
if (!cancelled) block(arg);
};
wrappingBlock = [[wrappingBlock copy] autorelease];
dispatch_after(dTimeDelay(delay), dispatch_get_main_queue(), ^{ wrappingBlock(NO, anObject); });
return wrappingBlock;
}
+ (void) cancelBlock:(id)block {
if (!block) return;
void (^aWrappingBlock)(BOOL) = (void(^)(BOOL))block;
aWrappingBlock(YES);
}
@end
@malaba
Copy link

malaba commented Jul 3, 2013

what about using 'dispatch_get_current_queue()' instead of 'dispatch_get_main_queue()' to be more general ?

@saman-mb
Copy link

Very nice, thanks a lot for posting this. Its exactly what I was looking for. Nice clean implementation also. :)

@nyteshade
Copy link

Any chance of having this open sourced for any and all users? Currently your single line copyright, auto added by Xcode, may prevent others from legally using this code that you've written.

@ruslanchek
Copy link

Excuse me, but i can't understand, how to use "cancel Block"?

@kallipigous
Copy link

I found using this crashed my app but only when t was compiled for the store. Not ad hoc or debug. Any idea why on earth this might be?

@newacct
Copy link

newacct commented Dec 12, 2014

The wrappingBlock retains block even after the block has been run or cancelled. It would be a good idea to release it in those cases.

People who use this API will often pass a block that retains some controller object to perform actions on it, and that controller object in turn retains the wrappingBlock returned from this API. This causes a retain cycle that does not automatically get broken.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment