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
@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