Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
306 changes: 176 additions & 130 deletions Circle.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Circle/Circle-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.mikeash.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
Expand Down
49 changes: 34 additions & 15 deletions Circle/CircleIVarLayout.m
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,25 @@


// Dictionarys to cache the layout and classification of a class.
static CFMutableDictionaryRef gLayoutCache;
static CFMutableDictionaryRef gClassificationCache;

static CFMutableDictionaryRef getClassificationCache() {
static CFMutableDictionaryRef gClassificationCache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// When the layout cache doesn't exist, create it now. We'll be adding an entry.
gClassificationCache = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
});
return gClassificationCache;
}
static CFMutableDictionaryRef getLayoutCache() {
static dispatch_once_t onceToken;
static CFMutableDictionaryRef gLayoutCache;
dispatch_once(&onceToken, ^{
// If the classification cache doesn't exist, create it.
gLayoutCache = CFDictionaryCreateMutable(NULL, 0, NULL, &kCFTypeDictionaryValueCallBacks);
});
return gLayoutCache;
}


// This class detects releases sent to it and makes that information available to
Expand Down Expand Up @@ -170,10 +187,17 @@ static BOOL IsBlock(void *obj)
{
// Fetch the selector for the ARC destructor.
SEL destructorSEL = sel_getUid(".cxx_destruct");

// `@selector(doNotImplementThisItDoesNotExistReally)` doesn't exist, obviously. Let clang know we
// don't care so we don't get a warning.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"

// Fetch the IMP for the destructor. Also fetch the IMP for a known unimplemented selector.
void (*Destruct)(void *, SEL) = (__typeof__(Destruct))class_getMethodImplementation(c, destructorSEL);
void (*Forward)(void *, SEL) = (__typeof__(Forward))class_getMethodImplementation([NSObject class], @selector(doNotImplementThisItDoesNotExistReally));

#pragma clang diagnostic pop

// If the ARC destructor is not implemented (IMP equals that of an unimplemented selector)
// then the class contains no strong references. We can just bail out now.
Expand Down Expand Up @@ -202,13 +226,12 @@ static BOOL IsBlock(void *obj)
// Fetch the strong ivar layout for a class, pulling from the cache when possible.
static NSIndexSet *GetClassStrongLayout(Class c)
{
// If the layout cache doesn't exist, create it now. We'll be adding an entry.
if(!gLayoutCache)
gLayoutCache = CFDictionaryCreateMutable(NULL, 0, NULL, &kCFTypeDictionaryValueCallBacks);


CFMutableDictionaryRef gLayoutCache = getLayoutCache();

// Fetch the layout from the cache.
NSIndexSet *layout = (__bridge NSIndexSet *)CFDictionaryGetValue(gLayoutCache, (__bridge void *)c);

// If the layout doesn't exist in the cache, then compute it and cache it.
if(!layout)
{
Expand Down Expand Up @@ -238,15 +261,13 @@ static BOOL IsBlock(void *obj)

void (*dispose_helper)(void *src) = descriptor->rest[1];

// If the layout cache doesn't exist, create it now. We'll add an entry to it.
if(!gLayoutCache)
gLayoutCache = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);

CFMutableDictionaryRef gLayoutCache = getLayoutCache();

// See if the layout already exists. We can't use the block isa as a key, since different
// blocks can share an isa. Instead, we use the address of the destructor function as the
// key, since that destructor will always result in the same layout.
NSIndexSet *layout = (__bridge NSIndexSet *)CFDictionaryGetValue(gLayoutCache, dispose_helper);

// If the layout doesn't exist in the cache, calculate it using this block's isa, the block's
// size as pulled from its descriptor, and a destructor that just calls the block destructor.
if(!layout)
Expand All @@ -263,9 +284,7 @@ static BOOL IsBlock(void *obj)
// Classify an object into one of the listed classifications.
static enum Classification Classify(void *obj)
{
// If the classification cache doesn't exist, create it.
if(!gClassificationCache)
gClassificationCache = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
CFMutableDictionaryRef gClassificationCache = getClassificationCache();

// Key classifications off the object's class.
void *key = (__bridge void *)object_getClass((__bridge id)obj);
Expand Down
4 changes: 2 additions & 2 deletions CircleTests/CircleTests.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
// Copyright (c) 2012 Michael Ash. All rights reserved.
//

#import <SenTestingKit/SenTestingKit.h>
#import <XCTest/XCTest.h>

@interface CircleTests : SenTestCase
@interface CircleTests : XCTestCase

@end
40 changes: 20 additions & 20 deletions CircleTests/CircleTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ - (void)testSimpleCycle
}

@autoreleasepool {
STAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
XCTAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
}
[collector collect];
STAssertNil(weakObj, @"Collector didn't collect a cycle");
XCTAssertNil(weakObj, @"Collector didn't collect a cycle");
}

- (void)testSingleObjectCycle
Expand All @@ -68,10 +68,10 @@ - (void)testSingleObjectCycle
}

@autoreleasepool {
STAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
XCTAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
}
[collector collect];
STAssertNil(weakObj, @"Collector didn't collect a cycle");
XCTAssertNil(weakObj, @"Collector didn't collect a cycle");
}

- (void)testBlockCycle
Expand All @@ -89,10 +89,10 @@ - (void)testBlockCycle
}

@autoreleasepool {
STAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
XCTAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
}
[collector collect];
STAssertNil(weakObj, @"Collector didn't collect a cycle");
XCTAssertNil(weakObj, @"Collector didn't collect a cycle");
}

- (void)testComplexCycle
Expand All @@ -118,10 +118,10 @@ - (void)testComplexCycle
}

@autoreleasepool {
STAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
XCTAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
}
[collector collect];
STAssertNil(weakObj, @"Collector didn't collect a cycle");
XCTAssertNil(weakObj, @"Collector didn't collect a cycle");
}

- (void)testReachableCycle
Expand All @@ -142,10 +142,10 @@ - (void)testReachableCycle
}

@autoreleasepool {
STAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
XCTAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
}
[collector collect];
STAssertNotNil(weakObj, @"Collector collected a referenced cycle");
XCTAssertNotNil(weakObj, @"Collector collected a referenced cycle");
}

- (void)testNSObject
Expand Down Expand Up @@ -190,7 +190,7 @@ - (void)testManyCollections

@autoreleasepool {
for(int i = 0; i < COUNT; i++)
STAssertNotNil(weakObjs[i], @"Weak pointer to cycle should not be nil before running the collector");
XCTAssertNotNil(weakObjs[i], @"Weak pointer to cycle should not be nil before running the collector");
}

for(int i = 0; i < COUNT; i++)
Expand All @@ -202,7 +202,7 @@ - (void)testManyCollections
}

for(int i = 0; i < COUNT; i++)
STAssertNil(weakObjs[i], @"Collector failed to collect a cycle");
XCTAssertNil(weakObjs[i], @"Collector failed to collect a cycle");
}

- (void)testArrayCycle
Expand All @@ -220,10 +220,10 @@ - (void)testArrayCycle
}

@autoreleasepool {
STAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
XCTAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
}
[collector collect];
STAssertNil(weakObj, @"Collector didn't collect a cycle");
XCTAssertNil(weakObj, @"Collector didn't collect a cycle");
}

- (void)testSetCycle
Expand All @@ -241,10 +241,10 @@ - (void)testSetCycle
}

@autoreleasepool {
STAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
XCTAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
}
[collector collect];
STAssertNil(weakObj, @"Collector didn't collect a cycle");
XCTAssertNil(weakObj, @"Collector didn't collect a cycle");
}

- (void)testDictionaryCycle
Expand All @@ -262,10 +262,10 @@ - (void)testDictionaryCycle
}

@autoreleasepool {
STAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
XCTAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
}
[collector collect];
STAssertNil(weakObj, @"Collector didn't collect a cycle");
XCTAssertNil(weakObj, @"Collector didn't collect a cycle");
}

- (void)test__blockReference
Expand Down Expand Up @@ -295,10 +295,10 @@ - (void)testSimpleSubclassCycle
}

@autoreleasepool {
STAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
XCTAssertNotNil(weakObj, @"Weak pointer to cycle should not be nil before running the collector");
}
[collector collect];
STAssertNil(weakObj, @"Collector didn't collect a cycle");
XCTAssertNil(weakObj, @"Collector didn't collect a cycle");
}

@end
10 changes: 5 additions & 5 deletions CircleTests/CircleTests-Info.plist → CircleTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.mikeash.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
Expand Down
2 changes: 0 additions & 2 deletions CircleTests/en.lproj/InfoPlist.strings

This file was deleted.