PageRenderTime 72ms CodeModel.GetById 14ms app.highlight 54ms RepoModel.GetById 1ms app.codeStats 1ms

/libs/ObjectAL/Session/OALSuspendHandler.m

http://github.com/kstenerud/ObjectAL-for-iPhone
Objective C | 229 lines | 148 code | 23 blank | 58 comment | 29 complexity | 905a7d12676f5a89e985343bbd3f3302 MD5 | raw file
  1//
  2//  OALSuspendHandler.m
  3//  ObjectAL
  4//
  5//  Created by Karl Stenerud on 10-12-19.
  6//
  7// Copyright 2010 Karl Stenerud
  8//
  9// Licensed under the Apache License, Version 2.0 (the "License");
 10// you may not use this file except in compliance with the License.
 11// You may obtain a copy of the License at
 12//
 13// http://www.apache.org/licenses/LICENSE-2.0
 14//
 15// Unless required by applicable law or agreed to in writing, software
 16// distributed under the License is distributed on an "AS IS" BASIS,
 17// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 18// See the License for the specific language governing permissions and
 19// limitations under the License.
 20//
 21// Note: You are NOT required to make the license available from within your
 22// iOS application. Including it in your project is sufficient.
 23//
 24// Attribution is not required, but appreciated :)
 25//
 26
 27#import "OALSuspendHandler.h"
 28#import "NSMutableArray+WeakReferences.h"
 29#import <objc/message.h>
 30
 31@implementation OALSuspendHandler
 32
 33+ (OALSuspendHandler*) handlerWithTarget:(id) target selector:(SEL) selector
 34{
 35	return [[[self alloc] initWithTarget:target selector:selector] autorelease];
 36}
 37
 38- (id) initWithTarget:(id) target selector:(SEL) selector
 39{
 40	if(nil != (self = [super init]))
 41	{
 42		listeners = [NSMutableArray newMutableArrayUsingWeakReferencesWithCapacity:10];
 43		manualSuspendStates = [[NSMutableArray alloc] initWithCapacity:10];
 44		suspendStatusChangeTarget = target;
 45		suspendStatusChangeSelector = selector;
 46	}
 47	return self;
 48}
 49
 50- (void) dealloc
 51{
 52	[listeners release];
 53	[manualSuspendStates release];
 54
 55	[super dealloc];
 56}
 57
 58- (void) addSuspendListener:(id<OALSuspendListener>) listener
 59{
 60	@synchronized(self)
 61	{
 62		[listeners addObject:listener];
 63		// If this handler is already suspended, make sure we don't unsuspend
 64		// a newly added listener on the next manual unsuspend.
 65		bool startingSuspendedValue = manualSuspendLock ? listener.manuallySuspended : NO;
 66		[manualSuspendStates addObject:[NSNumber numberWithBool:startingSuspendedValue]];
 67	}
 68}
 69
 70- (void) removeSuspendListener:(id<OALSuspendListener>) listener
 71{
 72	@synchronized(self)
 73	{
 74		NSUInteger index = [listeners indexOfObject:listener];
 75		if(NSNotFound != index)
 76		{
 77			[listeners removeObjectAtIndex:index];
 78			[manualSuspendStates removeObjectAtIndex:index];
 79		}
 80	}
 81}
 82
 83- (bool) manuallySuspended
 84{
 85	@synchronized(self)
 86	{
 87		return manualSuspendLock;
 88	}
 89}
 90
 91- (void) setManuallySuspended:(bool) value
 92{
 93	/* This handler propagates all suspend/unsuspend events to all listeners.
 94	 * An unsuspend will occur in the reverse order to a suspend (meaning, it will
 95	 * unsuspend listeners in the reverse order that it suspended them).
 96	 * On suspend, all listeners will be suspended prior to suspending this handler's
 97	 * slave object. On unsuspend, all listeners will resume after the slave object.
 98	 *
 99	 * Since "suspended" is manually triggered, this handler records all listeners'
100	 * suspend states so that it can intelligently decide whether to unsuspend or
101	 * not.
102	 */
103	
104	@synchronized(self)
105	{
106		// Setting must occur in the opposite order to clearing.
107		if(value)
108		{
109			NSUInteger numListeners = [listeners count];
110			for(NSUInteger index = 0; index < numListeners; index++)
111			{
112				id<OALSuspendListener> listener = [listeners objectAtIndex:index];
113				
114				// Record whether they were already suspended or not
115				bool alreadySuspended = listener.manuallySuspended;
116				if(alreadySuspended != [[manualSuspendStates objectAtIndex:index] boolValue])
117				{
118					[manualSuspendStates replaceObjectAtIndex:index withObject:[NSNumber numberWithBool:alreadySuspended]];
119				}
120				
121				// Update listener suspend state if necessary
122				if(!alreadySuspended)
123				{
124					listener.manuallySuspended = YES;
125				}
126			}
127		}
128
129		/* If the new value is the same as the old, do nothing.
130		 * If the other lock is set, do nothing.
131		 * Otherwise, send a suspend/unsuspend event to the slave.
132		 */
133		if(value != manualSuspendLock)
134		{
135			manualSuspendLock = value;
136			if(!interruptLock)
137			{
138				if(nil != suspendStatusChangeTarget)
139				{
140					objc_msgSend(suspendStatusChangeTarget, suspendStatusChangeSelector, manualSuspendLock);
141				}
142			}
143		}
144		
145		// Ensure clearing occurs in opposing order
146		if(!value)
147		{
148			for(int index = (int)[listeners count] - 1; index >= 0; index--)
149			{
150				id<OALSuspendListener> listener = [listeners objectAtIndex:index];
151				
152				bool alreadySuspended = [[manualSuspendStates objectAtIndex:index] boolValue];
153				
154				// Update listener suspend state if necessary
155				if(!alreadySuspended && listener.manuallySuspended)
156				{
157					listener.manuallySuspended = NO;
158				}
159			}
160		}
161	}
162}
163
164- (bool) interrupted
165{
166	@synchronized(self)
167	{
168		return interruptLock;
169	}
170}
171
172- (void) setInterrupted:(bool) value
173{
174	/* This handler propagates all interrupt/end interrupt events to all listeners.
175	 * An end interrupt will occur in the reverse order to an interrupt (meaning, it will
176	 * end interrupt on listeners in the reverse order that it interrupted them).
177	 * On interrupt, all listeners will be interrupted prior to suspending this handler's
178	 * slave object. On end interrupt, all listeners will end interrupt after the slave object.
179	 */
180	@synchronized(self)
181	{
182		// Setting must occur in the opposite order to clearing.
183		if(value)
184		{
185			for(id<OALSuspendListener> listener in listeners)
186			{
187				if(!listener.interrupted)
188				{
189					listener.interrupted = YES;
190				}
191			}
192		}
193		
194		/* If the new value is the same as the old, do nothing.
195		 * If the other lock is set, do nothing.
196		 * Otherwise, send a suspend/unsuspend event to the slave.
197		 */
198		if(value != interruptLock)
199		{
200			interruptLock = value;
201			if(!manualSuspendLock)
202			{
203				if(nil != suspendStatusChangeTarget)
204				{
205					objc_msgSend(suspendStatusChangeTarget, suspendStatusChangeSelector, interruptLock);
206				}
207			}
208		}
209		
210		// Ensure clearing occurs in opposing order
211		if(!value)
212		{
213			for(id<OALSuspendListener> listener in [listeners reverseObjectEnumerator])
214			{
215				if(listener.interrupted)
216				{
217					listener.interrupted = NO;
218				}
219			}
220		}
221	}
222}
223
224- (bool) suspended
225{
226	return interruptLock | manualSuspendLock;
227}
228
229@end