diff options
author | thomasvl@gmail.com <thomasvl@gmail.com@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3> | 2008-11-17 21:30:25 +0000 |
---|---|---|
committer | thomasvl@gmail.com <thomasvl@gmail.com@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3> | 2008-11-17 21:30:25 +0000 |
commit | 6fdb9dfef2c10696fc38a3ad860e014782b7d698 (patch) | |
tree | 0869bd773375520a3238a6885f9a6662b860d26b /Foundation | |
parent | 8ddb49cefd01b220ad5e1d2f0060b2a0ad54efdb (diff) |
- Added GTMLightweightProxy
- Added installer for the spotlight importers.
Diffstat (limited to 'Foundation')
-rw-r--r-- | Foundation/GTMLightweightProxy.h | 46 | ||||
-rw-r--r-- | Foundation/GTMLightweightProxy.m | 112 | ||||
-rw-r--r-- | Foundation/GTMLightweightProxyTest.m | 77 |
3 files changed, 235 insertions, 0 deletions
diff --git a/Foundation/GTMLightweightProxy.h b/Foundation/GTMLightweightProxy.h new file mode 100644 index 0000000..2e2a748 --- /dev/null +++ b/Foundation/GTMLightweightProxy.h @@ -0,0 +1,46 @@ +// +// GTMLightweightProxy.h +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import <Foundation/Foundation.h> + +// +// GTMLightweightProxy +// +// An object which does nothing but stand in for another object and forward +// messages (other than basic NSObject messages) to it, suitable for breaking +// retain cycles. It does *not* retain the represented object, so the +// represented object must be set to nil when that object is deallocated. +// +// Messages sent to a GTMLightweightProxy with no represented object set will +// be silently discarded. +// +@interface GTMLightweightProxy : NSProxy { + @private + __weak id representedObject_; +} + +// Initializes the object to represent |object|. +- (id)initWithRepresentedObject:(id)object; + +// Gets the object that the proxy represents. +- (id)representedObject; + +// Changes the proxy to represent |object| +- (void)setRepresentedObject:(id)object; + +@end diff --git a/Foundation/GTMLightweightProxy.m b/Foundation/GTMLightweightProxy.m new file mode 100644 index 0000000..ad7e0a1 --- /dev/null +++ b/Foundation/GTMLightweightProxy.m @@ -0,0 +1,112 @@ +// +// GTMLightweightProxy.m +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMLightweightProxy.h" + + +@implementation GTMLightweightProxy + +- (id)initWithRepresentedObject:(id)object { + // it's weak, we don't retain + representedObject_ = object; + return self; +} + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 +// -[NSProxy finalize] is only in 10.5 or later +- (void)finalize { + representedObject_ = nil; + [super finalize]; +} +#endif + +- (void)dealloc { + // it's weak, we don't release + representedObject_ = nil; + [super dealloc]; +} + +- (id)representedObject { + // Use a local variable to avoid a bogus compiler warning. + id repObject = nil; + @synchronized(self) { + // Even though we don't retain this object, we hang it on the lifetime + // of the calling threads pool so it's lifetime is safe for at least that + // long. + repObject = [[representedObject_ retain] autorelease]; + } + return repObject; +} + +- (void)setRepresentedObject:(id)object { + @synchronized(self) { + representedObject_ = object; + } +} + +// Passes any unhandled method to the represented object if it responds to that +// method. +- (void)forwardInvocation:(NSInvocation*)invocation { + id target = [self representedObject]; + // Silently discard all messages when there's no represented object + if (!target) + return; + + SEL aSelector = [invocation selector]; + if ([target respondsToSelector:aSelector]) + [invocation invokeWithTarget:target]; +} + +// Gets the represented object's method signature for |selector|; necessary for +// forwardInvocation. +- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector { + id target = [self representedObject]; + if (target) { + return [target methodSignatureForSelector:selector]; + } else { + // Apple's underlying forwarding code crashes if we return nil here. + // Since we are not going to use the invocation being constructed + // if there's no representedObject, a random valid NSMethodSignature is fine. + return [NSObject methodSignatureForSelector:@selector(alloc)]; + } +} + +// Prevents exceptions from unknown selectors if there is no represented +// object, and makes the exception come from the right place if there is one. +- (void)doesNotRecognizeSelector:(SEL)selector { + id target = [self representedObject]; + if (target) + [target doesNotRecognizeSelector:selector]; +} + +// Checks the represented object's selectors to allow clients of the proxy to +// do respondsToSelector: tests. +- (BOOL)respondsToSelector:(SEL)selector { + if ([super respondsToSelector:selector] || + selector == @selector(initWithRepresentedObject:) || + selector == @selector(representedObject) || + selector == @selector(setRepresentedObject:)) + { + return YES; + } + + id target = [self representedObject]; + return target && [target respondsToSelector:selector]; +} + +@end diff --git a/Foundation/GTMLightweightProxyTest.m b/Foundation/GTMLightweightProxyTest.m new file mode 100644 index 0000000..ad0961e --- /dev/null +++ b/Foundation/GTMLightweightProxyTest.m @@ -0,0 +1,77 @@ +// +// GTMLightweightProxyTest.m +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMSenTestCase.h" +#import "GTMLightweightProxy.h" + +@interface GTMLightweightProxyTest : GTMTestCase +- (BOOL)returnYes; +@end + +// Declare a non-existent method that we can call without compiler warnings. +@interface GTMLightweightProxyTest (GTMLightweightProxyTestMadeUpMethodDeclaration) +- (void)someMadeUpMethod; +@end + +@implementation GTMLightweightProxyTest + +- (void)testProxy { + id proxy = [[GTMLightweightProxy alloc] initWithRepresentedObject:self]; + STAssertEqualObjects(self, [proxy representedObject], @"Represented object setup failed"); + + // Check that it identifies itself as a proxy. + STAssertTrue([proxy isProxy], @"Should identify as a proxy"); + // Check that it passes class requests on + STAssertTrue([proxy isMemberOfClass:[self class]], @"Should pass class requests through"); + + // Check that it claims to respond to its selectors. + STAssertTrue([proxy respondsToSelector:@selector(initWithRepresentedObject:)], + @"Claims not to respond to initWithRepresentedObject:"); + STAssertTrue([proxy respondsToSelector:@selector(representedObject)], + @"Claims not to respond to representedObject:"); + STAssertTrue([proxy respondsToSelector:@selector(setRepresentedObject:)], + @"Claims not to respond to setRepresentedObject:"); + // Check that it responds to its represented object's selectors + STAssertTrue([proxy respondsToSelector:@selector(returnYes)], + @"Claims not to respond to returnYes"); + // ... but not to made up selectors. + STAssertThrows([proxy someMadeUpMethod], @"Calling a bogus method should throw"); + + // Check that callthrough works. + STAssertTrue([proxy returnYes], + @"Calling through to the represented object failed"); + + // Check that nilling out the represented object works. + [proxy setRepresentedObject:nil]; + STAssertTrue([proxy respondsToSelector:@selector(setRepresentedObject:)], + @"Claims not to respond to setRepresentedObject: after nilling out represented object"); + STAssertFalse([proxy respondsToSelector:@selector(returnYes)], + @"Claims to respond to returnYes after nilling out represented object"); + // Calling through once the represented object is nil should fail silently + STAssertNoThrow([proxy returnYes], + @"Calling through without a represented object should fail silently"); + // ... even when they are made up. + STAssertNoThrow([proxy someMadeUpMethod], @"Calling a bogus method on a nilled proxy should not throw"); +} + +// Simple method to test calling through the proxy. +- (BOOL)returnYes { + return YES; +} + +@end |