Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue when object dealloc in DFS #59

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
37 changes: 18 additions & 19 deletions FBRetainCycleDetector/Detector/FBRetainCycleDetector.mm
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ @implementation FBRetainCycleDetector
{
NSMutableArray *_candidates;
FBObjectGraphConfiguration *_configuration;
NSMutableSet *_objectSet;
NSHashTable *_objectSet;
}

- (instancetype)initWithConfiguration:(FBObjectGraphConfiguration *)configuration
{
if (self = [super init]) {
_configuration = configuration;
_candidates = [NSMutableArray new];
_objectSet = [NSMutableSet new];
_objectSet = [[NSHashTable alloc] initWithOptions:NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPointerPersonality capacity:kFBRetainCycleDetectorDefaultStackDepth];
}

return self;
Expand Down Expand Up @@ -99,8 +99,8 @@ - (void)addCandidate:(id)candidate
NSMutableArray<FBNodeEnumerator *> *stack = [NSMutableArray new];

// To make the search non-linear we will also keep
// a set of previously visited nodes.
NSMutableSet<FBNodeEnumerator *> *objectsOnPath = [NSMutableSet new];
// a set of previously visited objects that nodes monitor.
NSHashTable *objectsOnPath = [[NSHashTable alloc] initWithOptions:NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPointerPersonality capacity:kFBRetainCycleDetectorDefaultStackDepth];

// Let's start with the root
[stack addObject:wrappedObject];
Expand All @@ -112,31 +112,33 @@ - (void)addCandidate:(id)candidate
@autoreleasepool {
// Take topmost node in stack and mark it as visited
FBNodeEnumerator *top = [stack lastObject];
// Take object that topmost node monitor
__weak id object = top.object.object;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it needs to mimic storeWeak?


// We don't want to retraverse the same subtree
if (![objectsOnPath containsObject:top]) {
if ([_objectSet containsObject:@([top.object objectAddress])]) {
if (![objectsOnPath containsObject:object]) {
if ([_objectSet containsObject:object]) {
[stack removeLastObject];
continue;
}
// Add the object address to the set as an NSNumber to avoid
// unnecessarily retaining the object
[_objectSet addObject:@([top.object objectAddress])];

[_objectSet addObject:object];
}

[objectsOnPath addObject:top];
[objectsOnPath addObject:object];

// Take next adjecent node to that child. Wrapper object can
// persist iteration state. If we see that node again, it will
// give us new adjacent node unless it runs out of them
FBNodeEnumerator *firstAdjacent = [top nextObject];
if (firstAdjacent) {
__weak id firstAdjacentObject = firstAdjacent.object.object;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it needs to mimic storeWeak?

if (firstAdjacentObject) {
// Current node still has some adjacent not-visited nodes

BOOL shouldPushToStack = NO;

// Check if child was already seen in that path
if ([objectsOnPath containsObject:firstAdjacent]) {
if ([objectsOnPath containsObject:firstAdjacentObject]) {
// We have caught a retain cycle

// Ignore the first element which is equal to firstAdjacent, use firstAdjacent
Expand All @@ -145,10 +147,7 @@ - (void)addCandidate:(id)candidate
NSUInteger index = [stack indexOfObject:firstAdjacent];
NSInteger length = [stack count] - index;

if (index == NSNotFound) {
// Object got deallocated between checking if it exists and grabbing its index
shouldPushToStack = YES;
} else {
if (firstAdjacentObject) {
NSRange cycleRange = NSMakeRange(index, length);
NSMutableArray<FBNodeEnumerator *> *cycle = [[stack subarrayWithRange:cycleRange] mutableCopy];
[cycle replaceObjectAtIndex:0 withObject:firstAdjacent];
Expand All @@ -157,10 +156,10 @@ - (void)addCandidate:(id)candidate
// 2. Shift to lowest address (if we omit that, and the cycle is created by same class,
// we might have duplicates)
// 3. Shift by class (lexicographically)

[retainCycles addObject:[self _shiftToUnifiedCycle:[self _unwrapCycle:cycle]]];
}
} else {
} else if (firstAdjacentObject) {
// Node is clear to check, add it to stack and continue
shouldPushToStack = YES;
}
Expand All @@ -173,7 +172,7 @@ - (void)addCandidate:(id)candidate
} else {
// Node has no more adjacent nodes, it itself is done, move on
[stack removeLastObject];
[objectsOnPath removeObject:top];
[objectsOnPath removeObject:object];
}
}
}
Expand Down