diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8800512 --- /dev/null +++ b/.gitignore @@ -0,0 +1,85 @@ +# Finder + +.DS_Store + + + + + +# Sparkle + +dsa_priv.pem + + + + + +# Xcode Config + +*.mode1 +*.mode1v3 +*.mode2v3 +*.perspective +*.perspectivev3 +*.pbxuser +*.xcworkspace +*.xcuserdatad + + + + + +# Generated files + +VersionX-revision.h + + + + + +# Builds + +build/ +Build/ +*.[oa] + + + + + +# Other SCMs + +.hg +.svn +CVS + + + + + +# Backups + +*~.nib +*.swp +*~ +*(Autosaved).rtfd/ + +Backup[ ]of[ ]*.pages/ +Backup[ ]of[ ]*.key/ +Backup[ ]of[ ]*.numbers/ + + + + + +# Time Machine + +*.timemachine.supported + +.sass-cache/* +*/.sass-cache/* + +package/* +script/testflightCredentials + + diff --git a/IRRecurrenceMachine.xcodeproj/project.pbxproj b/IRRecurrenceMachine.xcodeproj/project.pbxproj new file mode 100644 index 0000000..43ac68b --- /dev/null +++ b/IRRecurrenceMachine.xcodeproj/project.pbxproj @@ -0,0 +1,403 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + FF22282D1463C77300541E31 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF22282C1463C77300541E31 /* Foundation.framework */; }; + FF2228331463C77300541E31 /* IRRecurrenceMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = FF2228321463C77300541E31 /* IRRecurrenceMachine.m */; }; + FF460D16149CF8B800DB7F0B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF460D15149CF8B800DB7F0B /* Cocoa.framework */; }; + FF460D20149CF8B800DB7F0B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = FF460D1E149CF8B800DB7F0B /* InfoPlist.strings */; }; + FF460D24149CF8B800DB7F0B /* IRRecurrenceMachineX.m in Sources */ = {isa = PBXBuildFile; fileRef = FF460D23149CF8B800DB7F0B /* IRRecurrenceMachineX.m */; }; + FF460D29149CF8CE00DB7F0B /* IRRecurrenceMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = FF2228321463C77300541E31 /* IRRecurrenceMachine.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + FF2228291463C77300541E31 /* libIRRecurrenceMachine.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libIRRecurrenceMachine.a; sourceTree = BUILT_PRODUCTS_DIR; }; + FF22282C1463C77300541E31 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + FF2228301463C77300541E31 /* IRRecurrenceMachine-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "IRRecurrenceMachine-Prefix.pch"; sourceTree = ""; }; + FF2228311463C77300541E31 /* IRRecurrenceMachine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IRRecurrenceMachine.h; sourceTree = ""; }; + FF2228321463C77300541E31 /* IRRecurrenceMachine.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IRRecurrenceMachine.m; sourceTree = ""; }; + FF460D14149CF8B800DB7F0B /* IRRecurrenceMachineX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = IRRecurrenceMachineX.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FF460D15149CF8B800DB7F0B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; }; + FF460D18149CF8B800DB7F0B /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + FF460D19149CF8B800DB7F0B /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + FF460D1A149CF8B800DB7F0B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + FF460D1D149CF8B800DB7F0B /* IRRecurrenceMachineX-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "IRRecurrenceMachineX-Info.plist"; sourceTree = ""; }; + FF460D1F149CF8B800DB7F0B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + FF460D21149CF8B800DB7F0B /* IRRecurrenceMachineX-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "IRRecurrenceMachineX-Prefix.pch"; sourceTree = ""; }; + FF460D22149CF8B800DB7F0B /* IRRecurrenceMachineX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IRRecurrenceMachineX.h; sourceTree = ""; }; + FF460D23149CF8B800DB7F0B /* IRRecurrenceMachineX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IRRecurrenceMachineX.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + FF2228261463C77300541E31 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FF22282D1463C77300541E31 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FF460D10149CF8B800DB7F0B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FF460D16149CF8B800DB7F0B /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + FF22281E1463C77300541E31 = { + isa = PBXGroup; + children = ( + FF22282E1463C77300541E31 /* IRRecurrenceMachine */, + FF460D1B149CF8B800DB7F0B /* IRRecurrenceMachineX */, + FF22282B1463C77300541E31 /* Frameworks */, + FF22282A1463C77300541E31 /* Products */, + ); + sourceTree = ""; + }; + FF22282A1463C77300541E31 /* Products */ = { + isa = PBXGroup; + children = ( + FF2228291463C77300541E31 /* libIRRecurrenceMachine.a */, + FF460D14149CF8B800DB7F0B /* IRRecurrenceMachineX.framework */, + ); + name = Products; + sourceTree = ""; + }; + FF22282B1463C77300541E31 /* Frameworks */ = { + isa = PBXGroup; + children = ( + FF22282C1463C77300541E31 /* Foundation.framework */, + FF460D15149CF8B800DB7F0B /* Cocoa.framework */, + FF460D17149CF8B800DB7F0B /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; + FF22282E1463C77300541E31 /* IRRecurrenceMachine */ = { + isa = PBXGroup; + children = ( + FF2228311463C77300541E31 /* IRRecurrenceMachine.h */, + FF2228321463C77300541E31 /* IRRecurrenceMachine.m */, + FF22282F1463C77300541E31 /* Supporting Files */, + ); + path = IRRecurrenceMachine; + sourceTree = ""; + }; + FF22282F1463C77300541E31 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + FF2228301463C77300541E31 /* IRRecurrenceMachine-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + FF460D17149CF8B800DB7F0B /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + FF460D18149CF8B800DB7F0B /* AppKit.framework */, + FF460D19149CF8B800DB7F0B /* CoreData.framework */, + FF460D1A149CF8B800DB7F0B /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + FF460D1B149CF8B800DB7F0B /* IRRecurrenceMachineX */ = { + isa = PBXGroup; + children = ( + FF460D22149CF8B800DB7F0B /* IRRecurrenceMachineX.h */, + FF460D23149CF8B800DB7F0B /* IRRecurrenceMachineX.m */, + FF460D1C149CF8B800DB7F0B /* Supporting Files */, + ); + path = IRRecurrenceMachineX; + sourceTree = ""; + }; + FF460D1C149CF8B800DB7F0B /* Supporting Files */ = { + isa = PBXGroup; + children = ( + FF460D1D149CF8B800DB7F0B /* IRRecurrenceMachineX-Info.plist */, + FF460D1E149CF8B800DB7F0B /* InfoPlist.strings */, + FF460D21149CF8B800DB7F0B /* IRRecurrenceMachineX-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + FF2228271463C77300541E31 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FF460D11149CF8B800DB7F0B /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + FF2228281463C77300541E31 /* IRRecurrenceMachine */ = { + isa = PBXNativeTarget; + buildConfigurationList = FF2228361463C77300541E31 /* Build configuration list for PBXNativeTarget "IRRecurrenceMachine" */; + buildPhases = ( + FF2228251463C77300541E31 /* Sources */, + FF2228261463C77300541E31 /* Frameworks */, + FF2228271463C77300541E31 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = IRRecurrenceMachine; + productName = IRRecurrenceMachine; + productReference = FF2228291463C77300541E31 /* libIRRecurrenceMachine.a */; + productType = "com.apple.product-type.library.static"; + }; + FF460D13149CF8B800DB7F0B /* IRRecurrenceMachineX */ = { + isa = PBXNativeTarget; + buildConfigurationList = FF460D27149CF8B800DB7F0B /* Build configuration list for PBXNativeTarget "IRRecurrenceMachineX" */; + buildPhases = ( + FF460D0F149CF8B800DB7F0B /* Sources */, + FF460D10149CF8B800DB7F0B /* Frameworks */, + FF460D11149CF8B800DB7F0B /* Headers */, + FF460D12149CF8B800DB7F0B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = IRRecurrenceMachineX; + productName = IRRecurrenceMachineX; + productReference = FF460D14149CF8B800DB7F0B /* IRRecurrenceMachineX.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + FF2228201463C77300541E31 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = FF2228231463C77300541E31 /* Build configuration list for PBXProject "IRRecurrenceMachine" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = FF22281E1463C77300541E31; + productRefGroup = FF22282A1463C77300541E31 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + FF2228281463C77300541E31 /* IRRecurrenceMachine */, + FF460D13149CF8B800DB7F0B /* IRRecurrenceMachineX */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + FF460D12149CF8B800DB7F0B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FF460D20149CF8B800DB7F0B /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + FF2228251463C77300541E31 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FF2228331463C77300541E31 /* IRRecurrenceMachine.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FF460D0F149CF8B800DB7F0B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FF460D24149CF8B800DB7F0B /* IRRecurrenceMachineX.m in Sources */, + FF460D29149CF8CE00DB7F0B /* IRRecurrenceMachine.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + FF460D1E149CF8B800DB7F0B /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + FF460D1F149CF8B800DB7F0B /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + FF2228341463C77300541E31 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + SDKROOT = iphoneos; + }; + name = Debug; + }; + FF2228351463C77300541E31 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + COPY_PHASE_STRIP = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + FF2228371463C77300541E31 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DSTROOT = /tmp/IRRecurrenceMachine.dst; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "IRRecurrenceMachine/IRRecurrenceMachine-Prefix.pch"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + FF2228381463C77300541E31 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DSTROOT = /tmp/IRRecurrenceMachine.dst; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "IRRecurrenceMachine/IRRecurrenceMachine-Prefix.pch"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + FF460D25149CF8B800DB7F0B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SYSTEM_APPS_DIR)/Xcode.app/Contents/Developer/Library/Frameworks\"", + ); + FRAMEWORK_VERSION = A; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "IRRecurrenceMachineX/IRRecurrenceMachineX-Prefix.pch"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + INFOPLIST_FILE = "IRRecurrenceMachineX/IRRecurrenceMachineX-Info.plist"; + INSTALL_PATH = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.7; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + WRAPPER_EXTENSION = framework; + }; + name = Debug; + }; + FF460D26149CF8B800DB7F0B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SYSTEM_APPS_DIR)/Xcode.app/Contents/Developer/Library/Frameworks\"", + ); + FRAMEWORK_VERSION = A; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "IRRecurrenceMachineX/IRRecurrenceMachineX-Prefix.pch"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + INFOPLIST_FILE = "IRRecurrenceMachineX/IRRecurrenceMachineX-Info.plist"; + INSTALL_PATH = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + WRAPPER_EXTENSION = framework; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + FF2228231463C77300541E31 /* Build configuration list for PBXProject "IRRecurrenceMachine" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FF2228341463C77300541E31 /* Debug */, + FF2228351463C77300541E31 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FF2228361463C77300541E31 /* Build configuration list for PBXNativeTarget "IRRecurrenceMachine" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FF2228371463C77300541E31 /* Debug */, + FF2228381463C77300541E31 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FF460D27149CF8B800DB7F0B /* Build configuration list for PBXNativeTarget "IRRecurrenceMachineX" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FF460D25149CF8B800DB7F0B /* Debug */, + FF460D26149CF8B800DB7F0B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = FF2228201463C77300541E31 /* Project object */; +} diff --git a/IRRecurrenceMachine/IRRecurrenceMachine-Prefix.pch b/IRRecurrenceMachine/IRRecurrenceMachine-Prefix.pch new file mode 100644 index 0000000..8871354 --- /dev/null +++ b/IRRecurrenceMachine/IRRecurrenceMachine-Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'IRRecurrenceMachine' target in the 'IRRecurrenceMachine' project +// + +#ifdef __OBJC__ + #import +#endif diff --git a/IRRecurrenceMachine/IRRecurrenceMachine.h b/IRRecurrenceMachine/IRRecurrenceMachine.h new file mode 100644 index 0000000..bea76dc --- /dev/null +++ b/IRRecurrenceMachine/IRRecurrenceMachine.h @@ -0,0 +1,83 @@ +// +// IRRecurrenceMachine.h +// IRRecurrenceMachine +// +// Created by Evadne Wu on 11/4/11. +// Copyright (c) 2011 Iridia Productions. All rights reserved. +// + +#import + +@interface IRRecurrenceMachine : NSObject + +- (id) initWithQueue:(NSOperationQueue *)aQueue; +@property (nonatomic, readonly, retain) NSOperationQueue *queue; + +// Designated initializer. If a queue is not provided, or if the method was invoked +// implicitly, the recurrence machine will create a queue on its own. + +// It is generally recommended to let the recurrence machine use its own queue. +// Although the machine is conscious not to interfere with the queue, exotic queues +// with unknown or unexpected attributes might be troublesome. + + +@property (nonatomic, readwrite, assign) NSTimeInterval recurrenceInterval; + +// The default interval is 30 seconds. Changing the interval invalidates the timer. + + +- (void) addRecurringOperation:(NSOperation *)anOperation; +@property (nonatomic, readonly, retain) NSArray *recurringOperations; + +// All the operations sent to the recurrence machine should be considered as prototypes. +// That means whenever the recurrence machine needs to do stuff, it copies all the operations +// and enqueues the copied instances. This prevents stale state, +// and avoids resetting them too. + +// The recurring operations array is available for KVO. If you need to remove stuff, +// or if you have to reorder stuff, you can invoke -mutableArrayValueForKey: +// before a more proper API is introduced. + + +- (void) beginPostponingOperations; +- (void) endPostponingOperations; +- (BOOL) isPostponingOperations; +@property (nonatomic, readonly, assign) NSInteger postponingRequestCount; + +// Instead of locking and unlocking the operation queue, which might not always be an option, +// this set of methods simply work on the internal timer held by the recurrence machine. +// Whenever operations are postponed, the timer is invalidated and destroyed; +// the timer is re-created whenever operations cease to be postponed. + +// That means if the recurrence machine has an interval of 30 seconds, +// the recreated timer will not fire right after the operations are not postponed; +// the timer will fire 30 seconds after that. + +// If you need to immediately resume all the operations, call -scheduleOperationsNow. + +// The postponing request count is only exposed for debugging purposes; +// do not rely on its value for anything; it is not considered to conform to KVO +// and its accuracy is not vetted at all. + + +- (BOOL) scheduleOperationsNow; + +// If the operations were still running, this method returns NO; it’ll do nothing. +// Otherwise, if the queue is empty, it starts a new interval +// where things will happen again after `recurrenceInterval` seconds. + +// If operations are rescheduled properly, the internal timer will also be reset. + + +- (NSOperation *) newPostponingWrapperPrefix NS_RETURNS_RETAINED; +- (NSOperation *) newPostponingWrapperSuffix NS_RETURNS_RETAINED; + +// They call -beginPostponingOperations and -endPostponingOperations on self +// and are sent to the operation queue in this order: +// +// { Prefix } - { Real Operation } - { Suffix } +// +// where the real operation is dependent on the prefix, and +// the suffix is dependent on the real operation. + +@end diff --git a/IRRecurrenceMachine/IRRecurrenceMachine.m b/IRRecurrenceMachine/IRRecurrenceMachine.m new file mode 100644 index 0000000..762e3b9 --- /dev/null +++ b/IRRecurrenceMachine/IRRecurrenceMachine.m @@ -0,0 +1,206 @@ +// +// IRRecurrenceMachine.m +// IRRecurrenceMachine +// +// Created by Evadne Wu on 11/4/11. +// Copyright (c) 2011 Iridia Productions. All rights reserved. +// + +#import "IRRecurrenceMachine.h" + +@interface IRRecurrenceMachine () + +@property (nonatomic, readwrite, retain) NSOperationQueue *queue; +@property (nonatomic, readwrite, retain) NSArray *recurringOperations; +@property (nonatomic, readwrite, retain) NSTimer *timer; +@property (nonatomic, readwrite, assign) NSInteger postponingRequestCount; + +@property (nonatomic, readwrite, assign) void *debugInitThreadPtr; +- (void) debugAssertThreadSafety; + +@end + + +@implementation IRRecurrenceMachine + +@synthesize queue, recurrenceInterval, recurringOperations, postponingRequestCount; +@synthesize timer; +@synthesize debugInitThreadPtr; + +- (void) dealloc { + + [queue release]; + [recurringOperations release]; + [timer invalidate]; + [timer release]; + [super dealloc]; + +} + +- (id) init { + + return [self initWithQueue:nil]; + +} + +- (id) initWithQueue:(NSOperationQueue *)aQueue { + + self = [super init]; + if (!self) + return nil; + + queue = aQueue ? aQueue : [[NSOperationQueue alloc] init]; + recurrenceInterval = 30; + recurringOperations = [[NSArray array] retain]; + + debugInitThreadPtr = [NSThread currentThread]; + + [self timer]; + + return self; + +} + +- (void) addRecurringOperation:(NSOperation *)anOperation { + + NSParameterAssert(![self.recurringOperations containsObject:anOperation]); + + [[self mutableArrayValueForKey:@"recurringOperations"] addObject:anOperation]; + +} + +- (void) setRecurrenceInterval:(NSTimeInterval)newInterval { + + if (recurrenceInterval == newInterval) + return; + + [self willChangeValueForKey:@"recurrenceInterval"]; + + recurrenceInterval = newInterval; + + [self didChangeValueForKey:@"recurrenceInterval"]; + + [timer invalidate]; + [timer release]; + timer = nil; + + if (![self isPostponingOperations]) + [self timer]; + +} + +- (NSTimer *) timer { + + if (timer) + return timer; + + timer = [[NSTimer scheduledTimerWithTimeInterval:self.recurrenceInterval target:self selector:@selector(handleTimerFire:) userInfo:nil repeats:YES] retain]; + + return timer; + +} + +- (void) handleTimerFire:(NSTimer *)aTimer { + + NSParameterAssert(![self isPostponingOperations]); + + [self scheduleOperationsNow]; + +} + +- (BOOL) scheduleOperationsNow { + + if (self.queue.operationCount) + return NO; + + [self beginPostponingOperations]; + + [self.recurringOperations enumerateObjectsUsingBlock: ^ (NSOperation *operationPrototype, NSUInteger idx, BOOL *stop) { + + NSOperation *prefix = [[self newPostponingWrapperPrefix] autorelease]; + NSOperation *operation = [[operationPrototype copy] autorelease]; + NSOperation *suffix = [[self newPostponingWrapperSuffix] autorelease]; + + [operation addDependency:prefix]; + [suffix addDependency:operation]; + + [queue addOperation:prefix]; + [queue addOperation:operation]; + [queue addOperation:suffix]; + + }]; + + [self endPostponingOperations]; + + return YES; + +} + +- (NSOperation *) newPostponingWrapperPrefix { + + __block typeof(self) nrSelf = self; + + return [[NSBlockOperation blockOperationWithBlock: ^ { + dispatch_sync(dispatch_get_main_queue(), ^{ + [nrSelf beginPostponingOperations]; + }); + }] retain]; + +} + +- (NSOperation *) newPostponingWrapperSuffix { + + __block typeof(self) nrSelf = self; + + return [[NSBlockOperation blockOperationWithBlock: ^ { + dispatch_sync(dispatch_get_main_queue(), ^{ + [nrSelf endPostponingOperations]; + }); + }] retain]; + +} + +- (void) beginPostponingOperations { + + [self debugAssertThreadSafety]; + + self.postponingRequestCount += 1; + + if (postponingRequestCount == 1) { + + [self.timer invalidate]; + self.timer = nil; + + } + +} + +- (void) endPostponingOperations { + + [self debugAssertThreadSafety]; + + NSParameterAssert(postponingRequestCount > 0); + + self.postponingRequestCount -= 1; + + if (!postponingRequestCount) { + + [self timer]; + + } + +} + +- (BOOL) isPostponingOperations { + + return !!(self.postponingRequestCount); + +} + +- (void) debugAssertThreadSafety { + + NSAssert2([NSThread currentThread] == debugInitThreadPtr, @"Current Thread %@ differents from 0x%x", [NSThread currentThread], (unsigned int)debugInitThreadPtr); + +} + +@end diff --git a/IRRecurrenceMachineX/IRRecurrenceMachineX-Info.plist b/IRRecurrenceMachineX/IRRecurrenceMachineX-Info.plist new file mode 100644 index 0000000..de2911e --- /dev/null +++ b/IRRecurrenceMachineX/IRRecurrenceMachineX-Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.iridia.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2011 Iridia Productions. All rights reserved. + NSPrincipalClass + + + diff --git a/IRRecurrenceMachineX/IRRecurrenceMachineX-Prefix.pch b/IRRecurrenceMachineX/IRRecurrenceMachineX-Prefix.pch new file mode 100644 index 0000000..365013a --- /dev/null +++ b/IRRecurrenceMachineX/IRRecurrenceMachineX-Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'IRRecurrenceMachineX' target in the 'IRRecurrenceMachineX' project +// + +#ifdef __OBJC__ + #import +#endif diff --git a/IRRecurrenceMachineX/IRRecurrenceMachineX.h b/IRRecurrenceMachineX/IRRecurrenceMachineX.h new file mode 100644 index 0000000..040435b --- /dev/null +++ b/IRRecurrenceMachineX/IRRecurrenceMachineX.h @@ -0,0 +1,10 @@ +// +// IRRecurrenceMachineX.h +// IRRecurrenceMachineX +// +// Created by Evadne Wu on 12/18/11. +// Copyright (c) 2011 Iridia Productions. All rights reserved. +// + +#import +#import "IRRecurrenceMachine.h" diff --git a/IRRecurrenceMachineX/IRRecurrenceMachineX.m b/IRRecurrenceMachineX/IRRecurrenceMachineX.m new file mode 100644 index 0000000..f4ab855 --- /dev/null +++ b/IRRecurrenceMachineX/IRRecurrenceMachineX.m @@ -0,0 +1,9 @@ +// +// IRRecurrenceMachineX.m +// IRRecurrenceMachineX +// +// Created by Evadne Wu on 12/18/11. +// Copyright (c) 2011 Iridia Productions. All rights reserved. +// + +#import "IRRecurrenceMachineX.h" diff --git a/IRRecurrenceMachineX/en.lproj/InfoPlist.strings b/IRRecurrenceMachineX/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/IRRecurrenceMachineX/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ +