mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-22 10:20:43 +00:00
New: - CPObject performSelector:withObject:afterDelay:.
Also - CPObject cancelPreviousPerformWithTarget: and other related methods.
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* CPDelayedPerform.j
|
||||
* Foundation
|
||||
*
|
||||
* Portions based on NSDelayedPerform.m (2013-03-03) in Cocotron (http://www.cocotron.org/)
|
||||
* Copyright (c) 2006-2007 Christopher J. W. Lloyd
|
||||
*
|
||||
* Created by Alexander Ljungberg.
|
||||
* Copyright 2013, SlevenBits Ltd.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "CPObject.j"
|
||||
@import "CPRunLoop.j"
|
||||
@import "CPString.j"
|
||||
|
||||
@implementation CPDelayedPerform : CPObject
|
||||
{
|
||||
id _object;
|
||||
SEL _selector;
|
||||
id _argument;
|
||||
}
|
||||
|
||||
+ (CPDelayedPerform)delayedPerformWithObject:anObject selector:(SEL)aSelector argument:anArgument
|
||||
{
|
||||
return [[self alloc] initWithObject:anObject selector:aSelector argument:anArgument];
|
||||
}
|
||||
|
||||
- (id)initWithObject:(id)anObject selector:(SEL)aSelector argument:(id)anArgument
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_object = anObject;
|
||||
_selector = aSelector;
|
||||
_argument = anArgument;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToPerform:(CPDelayedPerform)anOther
|
||||
{
|
||||
if (!anOther || !anOther.isa)
|
||||
return NO;
|
||||
|
||||
if (_object !== anOther._object)
|
||||
return NO;
|
||||
|
||||
if (!_selector || !anOther._selector)
|
||||
return YES;
|
||||
|
||||
if (_selector !== anOther._selector)
|
||||
return NO;
|
||||
|
||||
if (_argument !== anOther._argument)
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)perform
|
||||
{
|
||||
try
|
||||
{
|
||||
[_object performSelector:_selector withObject:_argument];
|
||||
}
|
||||
catch(ex)
|
||||
{
|
||||
CPLog(@"exception %@ raised during delayed perform", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPRunLoop(CPDelayedPerform)
|
||||
|
||||
- (void)invalidateTimerWithDelayedPerform:(CPDelayedPerform)aDelayedPerform
|
||||
{
|
||||
for (var aKey in _timersForModes)
|
||||
{
|
||||
if (!_timersForModes.hasOwnProperty(aKey))
|
||||
continue;
|
||||
|
||||
var timersForMode = _timersForModes[aKey];
|
||||
for (var i = 0, count = [timersForMode count]; i < count; i++)
|
||||
{
|
||||
var aTimer = [timersForMode objectAtIndex:i],
|
||||
userInfo = [aTimer userInfo];
|
||||
|
||||
if ([userInfo isKindOfClass:CPDelayedPerform] && [userInfo isEqualToPerform:aDelayedPerform])
|
||||
[aTimer invalidate];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPObject(CPDelayedPerform)
|
||||
|
||||
+ (void)cancelPreviousPerformRequestsWithTarget:target selector:(SEL)selector object:argument
|
||||
{
|
||||
var aDelayedPerform = [CPDelayedPerform delayedPerformWithObject:target selector:selector argument:argument];
|
||||
|
||||
[[CPRunLoop currentRunLoop] invalidateTimerWithDelayedPerform:aDelayedPerform];
|
||||
}
|
||||
|
||||
+ (void)cancelPreviousPerformRequestsWithTarget:target
|
||||
{
|
||||
var aDelayedPerform = [CPDelayedPerform delayedPerformWithObject:target selector:NULL argument:nil];
|
||||
|
||||
[[CPRunLoop currentRunLoop] invalidateTimerWithDelayedPerform:aDelayedPerform];
|
||||
}
|
||||
|
||||
+ (void)_delayedPerform:(CPTimer)aTimer
|
||||
{
|
||||
var aDelayedPerform = [aTimer userInfo];
|
||||
|
||||
[aDelayedPerform perform];
|
||||
}
|
||||
|
||||
+ (void)object:object performSelector:(SEL)selector withObject:argument afterDelay:(CPTimeInterval)delay inModes:(CPArray)modes
|
||||
{
|
||||
var aDelayedPerform = [CPDelayedPerform delayedPerformWithObject:object selector:selector argument:argument],
|
||||
aTimer = [CPTimer timerWithTimeInterval:delay target:[CPObject class] selector:@selector(_delayedPerform:) userInfo:aDelayedPerform repeats:NO];
|
||||
|
||||
for (var i = 0, count = [modes count]; i < count; i++)
|
||||
[[CPRunLoop currentRunLoop] addTimer:aTimer forMode:[modes objectAtIndex:i]];
|
||||
}
|
||||
|
||||
- (void)performSelector:(SEL)selector withObject:object afterDelay:(CPTimeInterval)delay
|
||||
{
|
||||
[[self class] object:self performSelector:selector withObject:object afterDelay:delay inModes:[CPArray arrayWithObject:CPDefaultRunLoopMode]];
|
||||
}
|
||||
|
||||
- (void)performSelector:(SEL)selector withObject:object afterDelay:(CPTimeInterval)delay inModes:(CPArray)modes
|
||||
{
|
||||
[[self class] object:self performSelector:selector withObject:object afterDelay:delay inModes:modes];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -270,7 +270,6 @@ var CPRunLoopLastNativeRunLoop = 0;
|
||||
|
||||
aTimer._lastNativeRunLoopsForModes[aMode] = CPRunLoopLastNativeRunLoop;
|
||||
|
||||
|
||||
// FIXME: Hack for not doing this in CommonJS
|
||||
if ([CFBundle.environments() indexOfObject:("Browser")] !== CPNotFound)
|
||||
{
|
||||
@@ -308,6 +307,7 @@ var CPRunLoopLastNativeRunLoop = 0;
|
||||
nextTimerFireDate = _nextTimerFireDatesForModes[aMode];
|
||||
|
||||
// Perform Timers if necessary
|
||||
|
||||
if (_didAddTimer || nextTimerFireDate && nextTimerFireDate <= now)
|
||||
{
|
||||
_didAddTimer = NO;
|
||||
@@ -326,12 +326,16 @@ var CPRunLoopLastNativeRunLoop = 0;
|
||||
|
||||
_timersForModes[aMode] = nil;
|
||||
|
||||
// Loop through timers looking for ones that had fired
|
||||
// If we're running in CommonJS (unit tests) we shouldn't wait for at least 1 native run loop
|
||||
// since those will never happen.
|
||||
var hasNativeTimers = [CFBundle.environments() indexOfObject:("Browser")] !== CPNotFound;
|
||||
|
||||
// Loop through timers looking for ones that had fired
|
||||
while (index--)
|
||||
{
|
||||
var timer = timers[index];
|
||||
|
||||
if (timer._lastNativeRunLoopsForModes[aMode] < CPRunLoopLastNativeRunLoop && timer._isValid && timer._fireDate <= now)
|
||||
if ((!hasNativeTimers || timer._lastNativeRunLoopsForModes[aMode] < CPRunLoopLastNativeRunLoop) && timer._isValid && timer._fireDate <= now)
|
||||
[timer fire];
|
||||
|
||||
// Timer may or may not still be valid
|
||||
|
||||
@@ -33,14 +33,15 @@
|
||||
@import "CPDateFormatter.j"
|
||||
@import "CPDecimal.j"
|
||||
@import "CPDecimalNumber.j"
|
||||
@import "CPDelayedPerform.j"
|
||||
@import "CPDictionary.j"
|
||||
@import "CPEnumerator.j"
|
||||
@import "CPError.j"
|
||||
@import "CPException.j"
|
||||
@import "CPExpression.j"
|
||||
@import "CPFormatter.j"
|
||||
@import "CPIndexSet.j"
|
||||
@import "CPIndexPath.j"
|
||||
@import "CPIndexSet.j"
|
||||
@import "CPInvocation.j"
|
||||
@import "CPJSONPConnection.j"
|
||||
@import "CPKeyedArchiver.j"
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPRunLoop.j>
|
||||
@import <OJUnit/OJTestCase.j>
|
||||
|
||||
@implementation CPRunLoopTest : OJTestCase
|
||||
{
|
||||
}
|
||||
|
||||
- (void)testDelayedPerform
|
||||
{
|
||||
var aPerformer = [Performer new];
|
||||
|
||||
[aPerformer performSelector:@selector(performWithObject:) withObject:5 afterDelay:0];
|
||||
[aPerformer performSelector:@selector(performWithObject:) withObject:1 afterDelay:0 inModes:[CPDefaultRunLoopMode]];
|
||||
[aPerformer performSelector:@selector(performWithObject:) withObject:10 afterDelay:9999 inModes:[CPDefaultRunLoopMode]];
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
|
||||
[self assertTrue:[aPerformer.callArgs containsObject:5]];
|
||||
[self assertTrue:[aPerformer.callArgs containsObject:1]];
|
||||
[CPObject cancelPreviousPerformRequestsWithTarget:aPerformer];
|
||||
|
||||
var performer1 = [Performer new],
|
||||
performer2 = [Performer new];
|
||||
|
||||
[performer1 performSelector:@selector(performWithObject:) withObject:5 afterDelay:0 inModes:[CPDefaultRunLoopMode]];
|
||||
[performer2 performSelector:@selector(performWithObject:) withObject:10 afterDelay:0 inModes:[CPDefaultRunLoopMode]];
|
||||
[performer1 performSelector:@selector(performWithObject:) withObject:1 afterDelay:0 inModes:[CPDefaultRunLoopMode]];
|
||||
[CPObject cancelPreviousPerformRequestsWithTarget:performer1];
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
|
||||
[self assert:nil equals:performer1.callArgs];
|
||||
[self assert:[10] equals:performer2.callArgs];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation Performer : CPObject
|
||||
{
|
||||
CPArray callArgs;
|
||||
}
|
||||
|
||||
- (void)performWithObject:(id)anObject
|
||||
{
|
||||
if (!callArgs)
|
||||
callArgs = [];
|
||||
|
||||
[callArgs addObject:anObject];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user