├── BackgroundTimer.xcodeproj ├── project.pbxproj └── xcuserdata │ └── mikeash.xcuserdatad │ ├── xcbreakpoints │ └── Breakpoints.xcbkptlist │ └── xcschemes │ ├── BackgroundTimer.xcscheme │ └── xcschememanagement.plist ├── BackgroundTimer_Prefix.pch ├── LICENSE └── Source ├── BackgroundResizingArray.h ├── BackgroundResizingArray.m ├── MABGTimer.h ├── MABGTimer.m └── main.m /BackgroundTimer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C21329D311D2CDD000EC59F7 /* BackgroundResizingArray.m in Sources */ = {isa = PBXBuildFile; fileRef = C21329D211D2CDD000EC59F7 /* BackgroundResizingArray.m */; }; 11 | C249147411D2FBCE00D4783C /* MABGTimer.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C249147211D2FBCE00D4783C /* MABGTimer.h */; }; 12 | C249147511D2FBCE00D4783C /* MABGTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = C249147311D2FBCE00D4783C /* MABGTimer.m */; }; 13 | C29BB10E11D2CC0C004D62AB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C29BB10D11D2CC0C004D62AB /* Foundation.framework */; }; 14 | C29BB11211D2CC0C004D62AB /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C29BB11111D2CC0C004D62AB /* main.m */; }; 15 | C2B2EFF011D9347400980F15 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C2B2EFEF11D9347400980F15 /* CoreServices.framework */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | C29BB10711D2CC0C004D62AB /* CopyFiles */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = /usr/share/man/man1/; 23 | dstSubfolderSpec = 0; 24 | files = ( 25 | C249147411D2FBCE00D4783C /* MABGTimer.h in CopyFiles */, 26 | ); 27 | runOnlyForDeploymentPostprocessing = 1; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | C21329D111D2CDD000EC59F7 /* BackgroundResizingArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BackgroundResizingArray.h; sourceTree = ""; }; 33 | C21329D211D2CDD000EC59F7 /* BackgroundResizingArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BackgroundResizingArray.m; sourceTree = ""; }; 34 | C249147211D2FBCE00D4783C /* MABGTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MABGTimer.h; sourceTree = ""; }; 35 | C249147311D2FBCE00D4783C /* MABGTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MABGTimer.m; sourceTree = ""; }; 36 | C29BB10911D2CC0C004D62AB /* BackgroundTimer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BackgroundTimer; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | C29BB10D11D2CC0C004D62AB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 38 | C29BB11111D2CC0C004D62AB /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 39 | C29BB11411D2CC0C004D62AB /* BackgroundTimer_Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BackgroundTimer_Prefix.pch; sourceTree = ""; }; 40 | C2B2EFEF11D9347400980F15 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = /System/Library/Frameworks/CoreServices.framework; sourceTree = ""; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | C29BB10611D2CC0C004D62AB /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | C29BB10E11D2CC0C004D62AB /* Foundation.framework in Frameworks */, 49 | C2B2EFF011D9347400980F15 /* CoreServices.framework in Frameworks */, 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | C29BB0FC11D2CC0C004D62AB = { 57 | isa = PBXGroup; 58 | children = ( 59 | C29BB10311D2CC0C004D62AB /* Source */, 60 | C29BB10A11D2CC0C004D62AB /* Products */, 61 | C29BB10C11D2CC0C004D62AB /* Frameworks */, 62 | C29BB11311D2CC0C004D62AB /* Other Sources */, 63 | ); 64 | sourceTree = ""; 65 | }; 66 | C29BB10311D2CC0C004D62AB /* Source */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | C29BB11111D2CC0C004D62AB /* main.m */, 70 | C21329D111D2CDD000EC59F7 /* BackgroundResizingArray.h */, 71 | C21329D211D2CDD000EC59F7 /* BackgroundResizingArray.m */, 72 | C249147211D2FBCE00D4783C /* MABGTimer.h */, 73 | C249147311D2FBCE00D4783C /* MABGTimer.m */, 74 | ); 75 | path = Source; 76 | sourceTree = ""; 77 | }; 78 | C29BB10A11D2CC0C004D62AB /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | C29BB10911D2CC0C004D62AB /* BackgroundTimer */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | C29BB10C11D2CC0C004D62AB /* Frameworks */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | C2B2EFEF11D9347400980F15 /* CoreServices.framework */, 90 | C29BB10D11D2CC0C004D62AB /* Foundation.framework */, 91 | ); 92 | name = Frameworks; 93 | sourceTree = ""; 94 | }; 95 | C29BB11311D2CC0C004D62AB /* Other Sources */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | C29BB11411D2CC0C004D62AB /* BackgroundTimer_Prefix.pch */, 99 | ); 100 | name = "Other Sources"; 101 | sourceTree = ""; 102 | }; 103 | /* End PBXGroup section */ 104 | 105 | /* Begin PBXNativeTarget section */ 106 | C29BB10811D2CC0C004D62AB /* BackgroundTimer */ = { 107 | isa = PBXNativeTarget; 108 | buildConfigurationList = C29BB11711D2CC0C004D62AB /* Build configuration list for PBXNativeTarget "BackgroundTimer" */; 109 | buildPhases = ( 110 | C29BB10511D2CC0C004D62AB /* Sources */, 111 | C29BB10611D2CC0C004D62AB /* Frameworks */, 112 | C29BB10711D2CC0C004D62AB /* CopyFiles */, 113 | ); 114 | buildRules = ( 115 | ); 116 | dependencies = ( 117 | ); 118 | name = BackgroundTimer; 119 | productName = BackgroundTimer; 120 | productReference = C29BB10911D2CC0C004D62AB /* BackgroundTimer */; 121 | productType = "com.apple.product-type.tool"; 122 | }; 123 | /* End PBXNativeTarget section */ 124 | 125 | /* Begin PBXProject section */ 126 | C29BB0FE11D2CC0C004D62AB /* Project object */ = { 127 | isa = PBXProject; 128 | buildConfigurationList = C29BB10111D2CC0C004D62AB /* Build configuration list for PBXProject "BackgroundTimer" */; 129 | compatibilityVersion = "Xcode 3.2"; 130 | hasScannedForEncodings = 0; 131 | mainGroup = C29BB0FC11D2CC0C004D62AB; 132 | productRefGroup = C29BB10A11D2CC0C004D62AB /* Products */; 133 | projectDirPath = ""; 134 | projectRoot = ""; 135 | targets = ( 136 | C29BB10811D2CC0C004D62AB /* BackgroundTimer */, 137 | ); 138 | }; 139 | /* End PBXProject section */ 140 | 141 | /* Begin PBXSourcesBuildPhase section */ 142 | C29BB10511D2CC0C004D62AB /* Sources */ = { 143 | isa = PBXSourcesBuildPhase; 144 | buildActionMask = 2147483647; 145 | files = ( 146 | C29BB11211D2CC0C004D62AB /* main.m in Sources */, 147 | C21329D311D2CDD000EC59F7 /* BackgroundResizingArray.m in Sources */, 148 | C249147511D2FBCE00D4783C /* MABGTimer.m in Sources */, 149 | ); 150 | runOnlyForDeploymentPostprocessing = 0; 151 | }; 152 | /* End PBXSourcesBuildPhase section */ 153 | 154 | /* Begin XCBuildConfiguration section */ 155 | C29BB11511D2CC0C004D62AB /* Debug */ = { 156 | isa = XCBuildConfiguration; 157 | buildSettings = { 158 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 159 | GCC_C_LANGUAGE_STANDARD = gnu99; 160 | GCC_OPTIMIZATION_LEVEL = 0; 161 | GCC_PREPROCESSOR_DEFINITIONS = DEBUG; 162 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 163 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 164 | GCC_WARN_UNUSED_VARIABLE = YES; 165 | MACOSX_DEPLOYMENT_TARGET = 10.6; 166 | ONLY_ACTIVE_ARCH = YES; 167 | PREBINDING = NO; 168 | SDKROOT = macosx10.6; 169 | }; 170 | name = Debug; 171 | }; 172 | C29BB11611D2CC0C004D62AB /* Release */ = { 173 | isa = XCBuildConfiguration; 174 | buildSettings = { 175 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 176 | GCC_C_LANGUAGE_STANDARD = gnu99; 177 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 178 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 179 | GCC_WARN_UNUSED_VARIABLE = YES; 180 | MACOSX_DEPLOYMENT_TARGET = 10.6; 181 | PREBINDING = NO; 182 | SDKROOT = macosx10.6; 183 | }; 184 | name = Release; 185 | }; 186 | C29BB11811D2CC0C004D62AB /* Debug */ = { 187 | isa = XCBuildConfiguration; 188 | buildSettings = { 189 | ALWAYS_SEARCH_USER_PATHS = NO; 190 | COPY_PHASE_STRIP = NO; 191 | GCC_DYNAMIC_NO_PIC = NO; 192 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 193 | GCC_MODEL_TUNING = G5; 194 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 195 | GCC_PREFIX_HEADER = BackgroundTimer_Prefix.pch; 196 | INSTALL_PATH = /usr/local/bin; 197 | PRODUCT_NAME = BackgroundTimer; 198 | }; 199 | name = Debug; 200 | }; 201 | C29BB11911D2CC0C004D62AB /* Release */ = { 202 | isa = XCBuildConfiguration; 203 | buildSettings = { 204 | ALWAYS_SEARCH_USER_PATHS = NO; 205 | COPY_PHASE_STRIP = YES; 206 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 207 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 208 | GCC_MODEL_TUNING = G5; 209 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 210 | GCC_PREFIX_HEADER = BackgroundTimer_Prefix.pch; 211 | INSTALL_PATH = /usr/local/bin; 212 | PRODUCT_NAME = BackgroundTimer; 213 | }; 214 | name = Release; 215 | }; 216 | /* End XCBuildConfiguration section */ 217 | 218 | /* Begin XCConfigurationList section */ 219 | C29BB10111D2CC0C004D62AB /* Build configuration list for PBXProject "BackgroundTimer" */ = { 220 | isa = XCConfigurationList; 221 | buildConfigurations = ( 222 | C29BB11511D2CC0C004D62AB /* Debug */, 223 | C29BB11611D2CC0C004D62AB /* Release */, 224 | ); 225 | defaultConfigurationIsVisible = 0; 226 | defaultConfigurationName = Release; 227 | }; 228 | C29BB11711D2CC0C004D62AB /* Build configuration list for PBXNativeTarget "BackgroundTimer" */ = { 229 | isa = XCConfigurationList; 230 | buildConfigurations = ( 231 | C29BB11811D2CC0C004D62AB /* Debug */, 232 | C29BB11911D2CC0C004D62AB /* Release */, 233 | ); 234 | defaultConfigurationIsVisible = 0; 235 | defaultConfigurationName = Release; 236 | }; 237 | /* End XCConfigurationList section */ 238 | }; 239 | rootObject = C29BB0FE11D2CC0C004D62AB /* Project object */; 240 | } 241 | -------------------------------------------------------------------------------- /BackgroundTimer.xcodeproj/xcuserdata/mikeash.xcuserdatad/xcbreakpoints/Breakpoints.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /BackgroundTimer.xcodeproj/xcuserdata/mikeash.xcuserdatad/xcschemes/BackgroundTimer.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 11 | 12 | 14 | 15 | /> 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /BackgroundTimer.xcodeproj/xcuserdata/mikeash.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | BackgroundTimer.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | BackgroundTimer 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /BackgroundTimer_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'BackgroundTimer' target in the 'BackgroundTimer' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #endif 7 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MABGTimer and all code associated with it is distributed under a BSD license, as listed below. 2 | 3 | 4 | Copyright (c) 2010, Michael Ash 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 8 | 9 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10 | 11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 12 | 13 | Neither the name of Michael Ash nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Source/BackgroundResizingArray.h: -------------------------------------------------------------------------------- 1 | // 2 | // BackgroundResizingArray.h 3 | // BackgroundTimer 4 | // 5 | // Created by Michael Ash on 6/23/10. 6 | // Copyright 2010 Michael Ash. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @class MABGTimer; 13 | 14 | @interface BackgroundResizingArray : NSMutableArray 15 | { 16 | id *_objs; 17 | NSUInteger _count; 18 | NSUInteger _capacity; 19 | 20 | MABGTimer *_resizeTimer; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Source/BackgroundResizingArray.m: -------------------------------------------------------------------------------- 1 | // 2 | // BackgroundResizingArray.m 3 | // BackgroundTimer 4 | // 5 | // Created by Michael Ash on 6/23/10. 6 | // Copyright 2010 Michael Ash. All rights reserved. 7 | // 8 | 9 | #import "BackgroundResizingArray.h" 10 | 11 | #import 12 | 13 | #import "MABGTimer.h" 14 | 15 | 16 | @implementation BackgroundResizingArray 17 | 18 | - (id)init 19 | { 20 | if((self = [super init])) 21 | { 22 | _resizeTimer = [[MABGTimer alloc] initWithObject: self]; 23 | } 24 | return self; 25 | } 26 | 27 | - (void)dealloc 28 | { 29 | [self removeAllObjects]; 30 | 31 | [_resizeTimer cancel]; 32 | [_resizeTimer release]; 33 | free(_objs); 34 | [super dealloc]; 35 | } 36 | 37 | - (void)_realloc: (NSUInteger)howmuch 38 | { 39 | size_t size = howmuch * sizeof(*_objs); 40 | size = malloc_good_size(size); 41 | _objs = realloc(_objs, size); 42 | _capacity = size / sizeof(*_objs); 43 | } 44 | 45 | - (void)_ensureSpace: (NSUInteger)amount 46 | { 47 | if(amount > _capacity) 48 | [self _realloc: MAX(_capacity * 2, 16)]; 49 | } 50 | 51 | - (void)_resize 52 | { 53 | NSLog(@"Resizing! %llu %llu", (unsigned long long)_capacity, (unsigned long long)_count); 54 | if(_capacity > _count) 55 | [self _realloc: _count]; 56 | } 57 | 58 | - (NSUInteger)count 59 | { 60 | return _count; 61 | } 62 | 63 | - (id)objectAtIndex: (NSUInteger)index 64 | { 65 | if(index < _count) 66 | { 67 | __block id ret; 68 | [_resizeTimer performWhileLocked: ^{ 69 | ret = [_objs[index] retain]; 70 | }]; 71 | return [ret autorelease]; 72 | } 73 | else 74 | [NSException raise: NSRangeException format: @"Index %llu is beyond end of array %llu", (unsigned long long)index, (unsigned long long)_count]; 75 | return nil; 76 | } 77 | 78 | - (void)addObject: (id)anObject 79 | { 80 | [self insertObject: anObject atIndex: _count]; 81 | } 82 | 83 | - (void)insertObject: (id)anObject atIndex: (NSUInteger)index 84 | { 85 | if(index > _count) 86 | [NSException raise: NSRangeException format: @"Index %llu is beyond end of array %llu", (unsigned long long)index, (unsigned long long)_count]; 87 | 88 | [_resizeTimer performWhileLocked: ^{ 89 | [self _ensureSpace: _count + 1]; 90 | memmove(_objs + index + 1, _objs + index, (_count - index) * sizeof(*_objs)); 91 | _objs[index] = [anObject retain]; 92 | }]; 93 | _count++; 94 | } 95 | 96 | - (void)removeLastObject 97 | { 98 | if(_count) 99 | [self removeObjectAtIndex: _count - 1]; 100 | } 101 | 102 | - (void)removeObjectAtIndex: (NSUInteger)index 103 | { 104 | if(index >= _count) 105 | [NSException raise: NSRangeException format: @"Index %llu is beyond end of array %llu", (unsigned long long)index, (unsigned long long)_count]; 106 | 107 | [_resizeTimer performWhileLocked: ^{ 108 | [_objs[index] release]; 109 | _objs[index] = nil; 110 | memmove(_objs + index, _objs + index + 1, (_count - index) * sizeof(*_objs)); 111 | }]; 112 | _count--; 113 | 114 | [_resizeTimer afterDelay: 0.5 do: ^(id self) { 115 | [self _resize]; 116 | }]; 117 | } 118 | 119 | - (void)replaceObjectAtIndex: (NSUInteger)index withObject: (id)anObject 120 | { 121 | if(index >= _count) 122 | [NSException raise: NSRangeException format: @"Index %llu is beyond end of array %llu", (unsigned long long)index, (unsigned long long)_count]; 123 | [_resizeTimer performWhileLocked: ^{ 124 | [_objs[index] release]; 125 | _objs[index] = [anObject retain]; 126 | }]; 127 | } 128 | 129 | @end 130 | -------------------------------------------------------------------------------- /Source/MABGTimer.h: -------------------------------------------------------------------------------- 1 | // 2 | // MABGTimer.h 3 | // BackgroundTimer 4 | // 5 | // Created by Michael Ash on 6/23/10. 6 | // Copyright 2010 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | 13 | #ifndef mt_dispatch_strong 14 | #if TARGET_OS_IPHONE 15 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000 16 | #define mt_dispatch_release(__v) 17 | #define mt_dispatch_strong strong 18 | #else 19 | #define mt_dispatch_release(__v) (dispatch_release(__v)); 20 | #define mt_dispatch_strong assign 21 | #endif 22 | #else 23 | #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 24 | #define mt_dispatch_release(__v) 25 | #define mt_dispatch_strong strong 26 | #else 27 | #define mt_dispatch_release(__v) (dispatch_release(__v)); 28 | #define mt_dispatch_strong assign 29 | #endif 30 | #endif 31 | #endif 32 | 33 | typedef enum 34 | { 35 | MABGTimerCoalesce, // subsequent calls with charged timer can only reduce the time until firing, not extend; default value 36 | MABGTimerDelay // subsequent calls replace the existing time, potentially extending it 37 | } MABGTimerBehavior; 38 | 39 | @interface MABGTimer : NSObject 40 | { 41 | __unsafe_unretained id _obj; 42 | dispatch_queue_t _queue; 43 | dispatch_source_t _timer; 44 | MABGTimerBehavior _behavior; 45 | NSTimeInterval _nextFireTime; 46 | } 47 | 48 | @property (assign) id obj; 49 | @property (mt_dispatch_strong, readonly) dispatch_queue_t queue; 50 | 51 | - (id)initWithObject:(id)obj; 52 | - (id)initWithObject:(id)obj behavior:(MABGTimerBehavior)behavior queueLabel:(char const *)queueLabel; 53 | 54 | - (void)setTargetQueue: (dispatch_queue_t)target; 55 | - (void)afterDelay: (NSTimeInterval)delay do: (void (^)(id self))block; 56 | - (void)performWhileLocked: (void (^)(void))block; 57 | - (void)cancel; 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /Source/MABGTimer.m: -------------------------------------------------------------------------------- 1 | // 2 | // MABGTimer.m 3 | // BackgroundTimer 4 | // 5 | // Created by Michael Ash on 6/23/10. 6 | // Copyright 2010 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "MABGTimer.h" 10 | 11 | #import 12 | #import 13 | 14 | 15 | 16 | @implementation MABGTimer 17 | @synthesize obj = _obj; 18 | @synthesize queue = _queue; 19 | 20 | - (id)initWithObject: (id)obj 21 | { 22 | return [self initWithObject: obj behavior: MABGTimerCoalesce queueLabel:"com.mikeash.MABGTimer"]; 23 | } 24 | 25 | - (id)initWithObject: (id)obj behavior: (MABGTimerBehavior)behavior queueLabel:(char const *)queueLabel 26 | { 27 | if((self = [super init])) 28 | { 29 | _obj = obj; 30 | _behavior = behavior; 31 | _queue = dispatch_queue_create(queueLabel, NULL); 32 | } 33 | return self; 34 | } 35 | 36 | - (void)_cancel 37 | { 38 | if (_timer) 39 | { 40 | dispatch_source_cancel(_timer); 41 | mt_dispatch_release(_timer); 42 | _timer = NULL; 43 | } 44 | } 45 | 46 | - (void)_finalize 47 | { 48 | [self _cancel]; 49 | 50 | mt_dispatch_release(_queue); 51 | _queue = nil; 52 | } 53 | 54 | - (void)finalize 55 | { 56 | [self _finalize]; 57 | [super finalize]; 58 | } 59 | 60 | - (void)dealloc 61 | { 62 | [self _finalize]; 63 | #if !__has_feature(objc_arc) 64 | [super dealloc]; 65 | #endif 66 | } 67 | 68 | - (void)setTargetQueue: (dispatch_queue_t)target 69 | { 70 | dispatch_set_target_queue(_queue, target); 71 | } 72 | 73 | - (NSTimeInterval)_now 74 | { 75 | static mach_timebase_info_data_t info; 76 | static dispatch_once_t pred; 77 | dispatch_once(&pred, ^{ 78 | mach_timebase_info(&info); 79 | }); 80 | 81 | NSTimeInterval t = mach_absolute_time(); 82 | t *= info.numer; 83 | t /= info.denom; 84 | return t / NSEC_PER_SEC; 85 | } 86 | 87 | - (void)afterDelay: (NSTimeInterval)delay do: (void (^)(id self))block 88 | { 89 | NSTimeInterval requestTime = [self _now]; 90 | 91 | [self performWhileLocked: ^{ 92 | 93 | // adjust delay to take into account time elapsed between the method call and execution of this block 94 | NSTimeInterval now = [self _now]; 95 | NSTimeInterval adjustedDelay = delay - (now - requestTime); 96 | if (adjustedDelay < 0.0) 97 | adjustedDelay = 0.0; 98 | 99 | BOOL hasTimer = _timer != nil; 100 | 101 | BOOL shouldProceed = NO; 102 | if (!hasTimer) 103 | shouldProceed = YES; 104 | else if (_behavior == MABGTimerDelay) 105 | shouldProceed = YES; 106 | else if (_behavior == MABGTimerCoalesce && [self _now] + adjustedDelay < _nextFireTime) 107 | shouldProceed = YES; 108 | 109 | if(shouldProceed) 110 | { 111 | if (!hasTimer) 112 | _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue); 113 | dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, adjustedDelay * NSEC_PER_SEC), 0, 0); 114 | _nextFireTime = [self _now] + adjustedDelay; 115 | dispatch_source_set_event_handler(_timer, ^{ 116 | block(_obj); 117 | [self _cancel]; 118 | }); 119 | if(!hasTimer) 120 | dispatch_resume(_timer); 121 | } 122 | }]; 123 | } 124 | 125 | - (void)performWhileLocked: (dispatch_block_t)block 126 | { 127 | if (_queue) 128 | dispatch_sync(_queue, block); 129 | } 130 | 131 | - (void)cancel 132 | { 133 | [self performWhileLocked: ^{ 134 | [self _cancel]; 135 | }]; 136 | } 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /Source/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // BackgroundTimer 4 | // 5 | // Created by Michael Ash on 6/23/10. 6 | // Copyright 2010 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | 10 | #import 11 | 12 | #import "BackgroundResizingArray.h" 13 | #import "MABGTimer.h" 14 | 15 | 16 | int main (int argc, const char * argv[]) 17 | { 18 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 19 | 20 | MABGTimer *timer = [[MABGTimer alloc] initWithObject: nil]; 21 | id block = ^{ NSLog(@"coalesced!"); }; 22 | [timer afterDelay: 1000 do: block]; 23 | [timer afterDelay: 0.1 do: block]; 24 | sleep(1); 25 | 26 | NSMutableArray *array = [[BackgroundResizingArray alloc] init]; 27 | [array addObject: @"one"]; 28 | [array addObject: @"two"]; 29 | [array addObject: @"three"]; 30 | NSLog(@"%@", array); 31 | [array removeObjectAtIndex: 1]; 32 | NSLog(@"%@", array); 33 | for(int i = 0; i < 100000; i++) 34 | [array addObject: [NSString stringWithFormat: @"%d", i]]; 35 | for(int i = 0; i < 1000000; i++) 36 | { 37 | if(random() % 2 == 0) 38 | [array addObject: [NSString stringWithFormat: @"%d", i]]; 39 | else 40 | [array removeLastObject]; 41 | } 42 | [array removeAllObjects]; 43 | NSLog(@"%@", array); 44 | 45 | NSLog(@"sleeping"); 46 | sleep(2); 47 | [array release]; 48 | NSLog(@"all done!"); 49 | [pool drain]; 50 | 51 | sleep(1000); 52 | 53 | return 0; 54 | } 55 | 56 | --------------------------------------------------------------------------------