Skip to content

Commit

Permalink
Added unit tests for zeroing weak references
Browse files Browse the repository at this point in the history
  • Loading branch information
defagos committed Apr 4, 2012
1 parent 95a6f1a commit 0d80663
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 5 deletions.
6 changes: 6 additions & 0 deletions CoconutKit-test/CoconutKit-test.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
6F4D6B6014EE7CD50002F09B /* GHUnitIOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F4D6B5F14EE7CD50002F09B /* GHUnitIOS.framework */; };
6F61D12E14161E4C004C91F5 /* NSTimeZone+HLSExtensionsTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F61D12D14161E4C004C91F5 /* NSTimeZone+HLSExtensionsTestCase.m */; };
6F7B848B14CF32B20091EE4B /* UIActionSheet+HLSExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F7B848A14CF32B20091EE4B /* UIActionSheet+HLSExtensions.m */; };
6F897873152B505D006C8231 /* HLSZeroingWeakRefTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F897872152B505D006C8231 /* HLSZeroingWeakRefTestCase.m */; };
6F91452A14CEBDF100AFA609 /* UIBarButtonItem+HLSActionSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F91452914CEBDF100AFA609 /* UIBarButtonItem+HLSActionSheet.m */; };
6F91F77314F3EF0B00E95EFA /* UIViewController+HLSExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F91F77214F3EF0B00E95EFA /* UIViewController+HLSExtensions.m */; };
6F93C4CE1404287400FEC9B0 /* HLSFloatTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F93C4CD1404287400FEC9B0 /* HLSFloatTestCase.m */; };
Expand Down Expand Up @@ -172,6 +173,8 @@
6F61D12D14161E4C004C91F5 /* NSTimeZone+HLSExtensionsTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTimeZone+HLSExtensionsTestCase.m"; sourceTree = "<group>"; };
6F7B848914CF32B20091EE4B /* UIActionSheet+HLSExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIActionSheet+HLSExtensions.h"; sourceTree = "<group>"; };
6F7B848A14CF32B20091EE4B /* UIActionSheet+HLSExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIActionSheet+HLSExtensions.m"; sourceTree = "<group>"; };
6F897871152B505D006C8231 /* HLSZeroingWeakRefTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HLSZeroingWeakRefTestCase.h; sourceTree = "<group>"; };
6F897872152B505D006C8231 /* HLSZeroingWeakRefTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLSZeroingWeakRefTestCase.m; sourceTree = "<group>"; };
6F91452714CEBDF100AFA609 /* HLSActionSheet+Friend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "HLSActionSheet+Friend.h"; sourceTree = "<group>"; };
6F91452814CEBDF100AFA609 /* UIBarButtonItem+HLSActionSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIBarButtonItem+HLSActionSheet.h"; sourceTree = "<group>"; };
6F91452914CEBDF100AFA609 /* UIBarButtonItem+HLSActionSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIBarButtonItem+HLSActionSheet.m"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -518,6 +521,8 @@
6F93C4CD1404287400FEC9B0 /* HLSFloatTestCase.m */,
6F3B060A14BC4C2D0026F512 /* HLSValidatorsTestCase.h */,
6F3B060B14BC4C2D0026F512 /* HLSValidatorsTestCase.m */,
6F897871152B505D006C8231 /* HLSZeroingWeakRefTestCase.h */,
6F897872152B505D006C8231 /* HLSZeroingWeakRefTestCase.m */,
6F33351413FB7F80000FC9FD /* NSCalendar+HLSExtensionsTestCase.h */,
6F33351513FB7F80000FC9FD /* NSCalendar+HLSExtensionsTestCase.m */,
6F33351613FB7F80000FC9FD /* NSDate+HLSExtensionsTestCase.h */,
Expand Down Expand Up @@ -1008,6 +1013,7 @@
6FB991FE1523B18B00E13BED /* HLSZeroingWeakRef.m in Sources */,
6FDDEC131529776000CED462 /* UITextField+HLSExtensions.m in Sources */,
6FDDEC251529782500CED462 /* UITextView+HLSExtensions.m in Sources */,
6F897873152B505D006C8231 /* HLSZeroingWeakRefTestCase.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
11 changes: 11 additions & 0 deletions CoconutKit-test/Sources/Core/HLSZeroingWeakRefTestCase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// HLSZeroingWeakRefTestCase.h
// CoconutKit-test
//
// Created by Samuel Défago on 03.04.12.
// Copyright (c) 2012 Hortis. All rights reserved.
//

@interface HLSZeroingWeakRefTestCase : GHTestCase

@end
39 changes: 39 additions & 0 deletions CoconutKit-test/Sources/Core/HLSZeroingWeakRefTestCase.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// HLSZeroingWeakRefTestCase.m
// CoconutKit-test
//
// Created by Samuel Défago on 03.04.12.
// Copyright (c) 2012 Hortis. All rights reserved.
//

#import "HLSZeroingWeakRefTestCase.h"

@interface BasicClass : NSObject

@end

@implementation HLSZeroingWeakRefTestCase

#pragma mark Tests

- (void)testNonTollFreeBridgedObject
{
BasicClass *basicClass = [[BasicClass alloc] init];
HLSZeroingWeakRef *zeroingWeakRef = [[[HLSZeroingWeakRef alloc] initWithObject:basicClass] autorelease];
GHAssertNotNil(zeroingWeakRef.object, @"Non-zeroed object reference");
[basicClass release];
GHAssertNil(zeroingWeakRef.object, @"Zeroed object reference");
}

- (void)testTollFreeBridgedObject
{
NSNumber *number = [[NSNumber alloc] initWithInt:1012];
GHAssertThrows([[HLSZeroingWeakRef alloc] initWithObject:number], @"Cannot create from Core Foundation objects");
[number release];
}

@end

@implementation BasicClass

@end
16 changes: 13 additions & 3 deletions CoconutKit/Sources/Core/HLSZeroingWeakRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,17 @@
* way to execute custom code before the reference is zeroed. This is especially useful when it makes
* sense to proactively stop some process whose delegate gets deallocated, for example.
*
* HLSZeroingWeakRef instances must be retained by the objects which store them
* HLSZeroingWeakRef instances must be retained by the objects which store them.
*
* The current implementation of HLSZeroingWeakRef is fairly basic:
* - zeroing weak references to toll-free bridged objects (NSString, NSURL, NSNumber, etc.) are not
* supported. Attempting to initialize a zeroing weak with a toll-free bridged object results in
* an exception being thrown
* - thread-safety issues have been ignored
*
* This implementation should suffice in most cases (weak pointers to instances of custom Objective-C
* classes, accessed from a single thread). In other cases, consider using Mike Ash implementation
* found at https://github.com/mikeash/MAZeroingWeakRef or ARC zeroing weak references.
*/
@interface HLSZeroingWeakRef : NSObject {
@private
Expand All @@ -22,8 +32,8 @@
}

/**
* Initialize a weak reference to an object. When the object gets deallocated, the weak reference object
* is automatically set to nil
* Initialize a weak reference to an Objective-C object (throws an exception if object is a toll-free
* bridged object)
*/
- (id)initWithObject:(id)object;

Expand Down
15 changes: 13 additions & 2 deletions CoconutKit/Sources/Core/HLSZeroingWeakRef.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,24 @@ - (id)initWithObject:(id)object
self.invocations = [NSMutableArray array];
self.object = object;

if (self.object) {
if (object) {
static NSString * const kSubclassPrefix = @"HLSZeroingWeakRef_";

// Access the real class, do not use [self class] here since can be faked
Class class = object_getClass(object);

// No support for Core Foundation objects (lead to infinite recursion)
// For more information, see
// http://www.mikeash.com/pyblog/friday-qa-2010-01-22-toll-free-bridging-internals.html
if ([NSStringFromClass(class) hasPrefix: @"__NSCF"]) {
@throw [NSException exceptionWithName:NSInvalidArgumentException
reason:@"Cannot create zeroing weak references to toll-free bridged objects"
userInfo:nil];
}

// Dynamically subclass the object class to override -dealloc selectively, and use this class instead.
// Another approach would involve swizzling -dealloc at the NSObject level, but this solution would
// incur an unacceptable overhead on all NSObjects
Class class = object_getClass(object); // Access the real class, do not use [self class] here since can be faked
NSString *className = [NSString stringWithUTF8String:class_getName(class)];
if (! [className hasPrefix:kSubclassPrefix]) {
NSString *subclassName = [kSubclassPrefix stringByAppendingString:className];
Expand Down

0 comments on commit 0d80663

Please sign in to comment.