aboutsummaryrefslogtreecommitdiff
path: root/Foundation
diff options
context:
space:
mode:
authorGravatar thomasvl@gmail.com <thomasvl@gmail.com@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-11-17 21:30:25 +0000
committerGravatar thomasvl@gmail.com <thomasvl@gmail.com@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-11-17 21:30:25 +0000
commit6fdb9dfef2c10696fc38a3ad860e014782b7d698 (patch)
tree0869bd773375520a3238a6885f9a6662b860d26b /Foundation
parent8ddb49cefd01b220ad5e1d2f0060b2a0ad54efdb (diff)
- Added GTMLightweightProxy
- Added installer for the spotlight importers.
Diffstat (limited to 'Foundation')
-rw-r--r--Foundation/GTMLightweightProxy.h46
-rw-r--r--Foundation/GTMLightweightProxy.m112
-rw-r--r--Foundation/GTMLightweightProxyTest.m77
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