iOS library to help detecting retain cycles in runtime.

Overview

FBRetainCycleDetector

Build Status Carthage compatible CocoaPods License

An iOS library that finds retain cycles using runtime analysis.

About

Retain cycles are one of the most common ways of creating memory leaks. It's incredibly easy to create a retain cycle, and tends to be hard to spot it. The goal of FBRetainCycleDetector is to help find retain cycles at runtime. The features of this project were influenced by Circle.

Installation

Carthage

To your Cartfile add:

github "facebook/FBRetainCycleDetector"

FBRetainCycleDetector is built out from non-debug builds, so when you want to test it, use

carthage update --configuration Debug

CocoaPods

To your podspec add:

pod 'FBRetainCycleDetector'

You'll be able to use FBRetainCycleDetector fully only in Debug builds. This is controlled by compilation flag that can be provided to the build to make it work in other configurations.

Example usage

Let's quickly dive in

#import <FBRetainCycleDetector/FBRetainCycleDetector.h>
FBRetainCycleDetector *detector = [FBRetainCycleDetector new];
[detector addCandidate:myObject];
NSSet *retainCycles = [detector findRetainCycles];
NSLog(@"%@", retainCycles);

- (NSSet<NSArray<FBObjectiveCGraphElement *> *> *)findRetainCycles will return a set of arrays of wrapped objects. It's pretty hard to look at at first, but let's go through it. Every array in this set will represent one retain cycle. Every element in this array is a wrapper around one object in this retain cycle. Check FBObjectiveCGraphElement.

Example output could look like this:

{(
    (
        "-> MyObject ",
        "-> _someObject -> __NSArrayI "
    )
)}

MyObject through someObject property retained NSArray that it was a part of.

FBRetainCycleDetector will look for cycles that are no longer than 10 objects. We can make it bigger (although it's going to be slower!).

FBRetainCycleDetector *detector = [FBRetainCycleDetector new];
[detector addCandidate:myObject];
NSSet *retainCycles = [detector findRetainCyclesWithMaxCycleLength:100];

Filters

There could also be retain cycles that we would like to omit. It's because not every retain cycle is a leak, and we might want to filter them out. To do so we need to specify filters:

NSMutableArray *filters = @[
  FBFilterBlockWithObjectIvarRelation([UIView class], @"_subviewCache"),
];

// Configuration object can describe filters as well as some options
FBObjectGraphConfiguration *configuration =
[[FBObjectGraphConfiguration alloc] initWithFilterBlocks:filters
                                     shouldInspectTimers:YES];
FBRetainCycleDetector *detector = [[FBRetainCycleDetector alloc] initWithConfiguration:configuration];
[detector addCandidate:myObject];
NSSet *retainCycles = [detector findRetainCycles];

Every filter is a block that having two FBObjectiveCGraphElement objects can say, if their relation is valid.

Check FBStandardGraphEdgeFilters to learn more about how to use filters.

NSTimer

NSTimer can be troublesome as it will retain it's target. Oftentimes it means a retain cycle. FBRetainCycleDetector can detect those, but if you want to skip them, you can specify that in the configuration you are passing to FBRetainCycleDetector.

FBObjectGraphConfiguration *configuration =
[[FBObjectGraphConfiguration alloc] initWithFilterBlocks:someFilters
                                     shouldInspectTimers:NO];
FBRetainCycleDetector *detector = [[FBRetainCycleDetector alloc] initWithConfiguration:configuration];

Associations

Objective-C let's us set associated objects for every object using objc_setAssociatedObject.

These associated objects can lead to retain cycles if we use retaining policies, like OBJC_ASSOCIATION_RETAIN_NONATOMIC. FBRetainCycleDetector can catch these kinds of cycles, but to do so we need to set it up. Early in the application's lifetime, preferably in main.m we can add this:

#import <FBRetainCycleDetector/FBAssociationManager.h>

int main(int argc, char * argv[]) {
  @autoreleasepool {
    [FBAssociationManager hook];
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
  }
}

In the code above [FBAssociationManager hook] will use fishhook to interpose functions objc_setAssociatedObject and objc_resetAssociatedObjects to track associations before they are made.

Getting Candidates

If you want to profile your app, you might want to have an abstraction over how to get candidates for FBRetainCycleDetector. While you can simply track it your own, you can also use FBAllocationTracker. It's a small tool we created that can help you track the objects. It offers simple API that you can query for example for all instances of given class, or all class names currently tracked, etc.

FBAllocationTracker and FBRetainCycleDetector can work nicely together. We have created a small example and drop-in project called FBMemoryProfiler that leverages both these projects. It offers you very basic UI that you can use to track all allocations and force retain cycle detection from UI.

Contributing

See the CONTRIBUTING file for how to help out.

License

FBRetainCycleDetector is BSD-licensed.

Comments
  • Xcode 12.5 compile error: Cannot initialize a parameter of type 'id<NSCopying> _Nonnull' with an rvalue of type

    Xcode 12.5 compile error: Cannot initialize a parameter of type 'id _Nonnull' with an rvalue of type

    Summary

    Getting this error at build time in Xcode 12.5 Cannot initialize a parameter of type 'id _Nonnull' with an rvalue of type '

    Code to reproduce

    Error on "FBClassStrongLayout.mm" file, line 202 layoutCache[currentClass] = ivars;

    iOS version

    iOS 14.5

    Installation method

    pod install

    SDK version

    FBRetainCycleDetector (0.1.4)

    Other information

    none

    opened by devlinlin 13
  • iOS 13 FBRetainCycleDetector crash when open Guided Access

    iOS 13 FBRetainCycleDetector crash when open Guided Access

    In iOS 13, when I turn on Guided Access switch in Accessibility (just turn on the switch, not enter Guided Access in app), the FBRetainCycleDetector will crash in starting time. It's easy to reappear.

    opened by CmST0us 8
  • NSGetSizeAndAlignment(): unsupported type encoding spec

    NSGetSizeAndAlignment(): unsupported type encoding spec

    Hi guys,

    In my mixed Swift/ObjC project, I copy/pasted the instructions under "Example usage" into a ObjC file. At

    NSSet *retainCycles = [detector findRetainCycles];

    I got an objc_exception

    NSGetSizeAndAlignment(): unsupported type encoding spec 'b' at 'b2' in 'b2'

    from FBClassStrongLayout.m line 43.

    I use FBRetainCycleDetector (0.1.1) via CococaPods (with frameworks enabled)

    opened by niklassaers 7
  • guard addObjectsFromArray receive nil param

    guard addObjectsFromArray receive nil param

    https://github.com/facebook/FBRetainCycleDetector/blob/master/FBRetainCycleDetector/Layout/Classes/FBClassStrongLayout.mm#L205

    • addObjectsFromArray: receive an nil param, we would guard at FBGetStrongReferencesForClass()

    image

    CLA Signed GH Review: review-needed 
    opened by liuzhiyi1992 5
  • array has incomplete element type 'struct rcd_rebinding'

    array has incomplete element type 'struct rcd_rebinding'

    hey there,when i import this library,appears the compile problem "array has incomplete element type 'struct rcd_rebinding'",i set the file name AppDelegate.mm and import libc++.tbd still can not fix this problem.any good suggestion ? thanks.

    1

    opened by crespoxiao 5
  • Array argument is not an NSArray

    Array argument is not an NSArray

    Hey,

    When I try to use your library, I run into a Array argument is not an NSArray exception whenever I try to call findRetainCycles.

    The issue seems to be coming from here.

    I'm running on XCode 3.8.1 targeting iOS 8.0.

    Any ideas?

    -[NSMutableArray addObjectsFromArray:]: array argument is not an NSArray
    -[UncaughtExceptionHandler handleException:] [Line 72] exception: *** -[NSMutableArray addObjectsFromArray:]: array argument is not an NSArray, with info:{
    	    UncaughtExceptionHandlerAddressesKey = (
    		0   CoreFoundation                      0x000000011076fb0b __exceptionPreprocess + 171
    		1   libobjc.A.dylib                     0x0000000116b5c141 objc_exception_throw + 48
    		2   CoreFoundation                      0x00000001106ef571 -[NSMutableArray addObjectsFromArray:] + 625
    		3   FBRetainCycleDetector               0x0000000110e3186b FBGetObjectStrongReferences + 443
    		4   FBRetainCycleDetector               0x0000000110e363d1 -[FBObjectiveCObject allRetainedObjects] + 369
    		5   FBRetainCycleDetector               0x0000000110e33db7 -[FBNodeEnumerator nextObject] + 103
    		6   FBRetainCycleDetector               0x0000000110e37c37 -[FBRetainCycleDetector _findRetainCyclesInObject:stackDepth:] + 471
    		7   FBRetainCycleDetector               0x0000000110e3786f -[FBRetainCycleDetector findRetainCyclesWithMaxCycleLength:] + 447
    		8   FBRetainCycleDetector               0x0000000110e376a2 -[FBRetainCycleDetector findRetainCycles] + 50
    		9   Whova                               0x000000010e658920 -[AgendaVC dealloc] + 144
    		10  UIKit                               0x00000001115d24e4 __destroy_helper_block_.125 + 80
    		11  libsystem_blocks.dylib              0x000000011592399d _Block_release + 111
    		12  UIKit                               0x00000001116a2e37 -[UIViewAnimationBlockDelegate .cxx_destruct] + 43
    		13  libobjc.A.dylib                     0x0000000116b5a9bc _ZL27object_cxxDestructFromClassP11objc_objectP10objc_class + 127
    		14  libobjc.A.dylib                     0x0000000116b66d34 objc_destructInstance + 129
    		15  libobjc.A.dylib                     0x0000000116b66d66 object_dispose + 22
    		16  libobjc.A.dylib                     0x0000000116b70b8e _ZN11objc_object17sidetable_releaseEb + 202
    		17  CoreFoundation                      0x00000001106d858d -[__NSDictionaryI dealloc] + 125
    		18  libobjc.A.dylib                     0x0000000116b70b8e _ZN11objc_object17sidetable_releaseEb + 202
    		19  libobjc.A.dylib                     0x0000000116b712fa _ZN12_GLOBAL__N_119AutoreleasePoolPage3popEPv + 866
    		20  CoreFoundation                      0x00000001106beef6 _CFAutoreleasePoolPop + 22
    		21  CoreFoundation                      0x00000001106faaec __CFRunLoopRun + 2172
    		22  CoreFoundation                      0x00000001106fa016 CFRunLoopRunSpecific + 406
    		23  GraphicsServices                    0x000000011881ea24 GSEventRunModal + 62
    		24  UIKit                               0x00000001115ea0d4 UIApplicationMain + 159
    		25  Whova                               0x000000010e2497d9 main + 137
    		26  libdyld.dylib                       0x00000001158db65d start + 1
    	);
    	}
    
    opened by Cylix 4
  • Misjudged on retained cycle

    Misjudged on retained cycle

    Hi Gricha, thank you for your reply. I have added the code to filter result of cycle detector. Please check it. PS: I'm sorry for that I posted the issue to FBMemoryProfiler repo, but I think the code should in FBRetainCycleDetector project. So I can't link the issue. The link of issue is here: https://github.com/facebook/FBMemoryProfiler/issues/35

    CLA Signed GH Review: review-needed 
    opened by without2002 4
  • MOD : rename fishook foldername

    MOD : rename fishook foldername

    The PR#57 has changed the filename of fishook, but the folder name of fishook in podspec is not them same with the real folder name, which is still fishook, not rcd_fishhook.

    The podspec in master branch:

    s.requires_arc = files.sort + [
        'rcd_fishhook/**/*.{c,h}'
    ]
    
    CLA Signed 
    opened by zld 3
  • rename the fishhook.h/c in FBRetainCycleDetector to rcd_fishhook.h/c

    rename the fishhook.h/c in FBRetainCycleDetector to rcd_fishhook.h/c

    React Native which introduced a fishhook in facebook/react-native. This causes a conflict because the project also depends on FBMemoryProfiler which in turn has a dependncy on FBRetainCycleDetector where another fishhhook exists.

    rename the fishhook.h/c in FBRetainCycleDetector to rcd_fishhook.h/c should solve the issue.

    CLA Signed 
    opened by BrooksWon 3
  • Remove incorrect type cast

    Remove incorrect type cast

    This change addresses following compiler warning: VendorLib/FBRetainCycleDetector/src/FBRetainCycleDetector/Layout/Classes/FBClassStrongLayout.mm:202:21: error: incompatible pointer types sending 'id' to parameter of type 'Class _Nonnull' [-Werror,-Wincompatible-pointer-types] layoutCache[(id)currentClass] = ivars;

    GH Review: review-needed 
    opened by ttsugriy 3
  • Questions about get block strong layout

    Questions about get block strong layout

    Hi, currently I am reading source code of this lib. But some code in FBBlockStrongLayout.m really confuses me. How does this lib figure out which variable block can retain.

    These lines of code are copied from _GetBlockStrongLayout function in FBBlockStrongLayout.m

      void (*dispose_helper)(void *src) = blockLiteral->descriptor->dispose_helper;
      const size_t ptrSize = sizeof(void *);
    
      // Figure out the number of pointers it takes to fill out the object, rounding up.
      const size_t elements = (blockLiteral->descriptor->size + ptrSize - 1) / ptrSize;
    
      // Create a fake object of the appropriate length.
      void *obj[elements];
      void *detectors[elements];
    
      for (size_t i = 0; i < elements; ++i) {
        FBBlockStrongRelationDetector *detector = [FBBlockStrongRelationDetector new];
        obj[i] = detectors[i] = detector;
      }
    
      @autoreleasepool {
        dispose_helper(obj);
      }
    
      // Run through the release detectors and add each one that got released to the object's
      // strong ivar layout.
      NSMutableIndexSet *layout = [NSMutableIndexSet indexSet];
    
      for (size_t i = 0; i < elements; ++i) {
        FBBlockStrongRelationDetector *detector = (FBBlockStrongRelationDetector *)(detectors[i]);
        if (detector.isStrong) {
          [layout addIndex:i];
        }
    
        // Destroy detectors
        [detector trueRelease];
      }
    

    I'm quite confused about the dispose_helper here. Does the dispose_helper dealloc the objects that block retains according to their memory address? So we can fake an array of objects and use dispose_helper to dealloc our objects, and then we can find out which object of the array is not existed. So we can get indexed and retrieve all the objects which block has retained.

    Thanks for your reading, and I really wanna know how this lib works. 😄

    opened by draveness 3
  • Archive error after updating Xcode 12.5

    Archive error after updating Xcode 12.5

    /Pods/FBRetainCycleDetector/FBRetainCycleDetector/Layout/Classes/FBClassStrongLayout.mm:202:21: cannot initialize a parameter of type 'id _Nonnull' with an rvalue of type 'Class'

        layoutCache[currentClass] = ivars;
    
    opened by Moussy 3
  • This allows a user to switch between volume caches, which increases the

    This allows a user to switch between volume caches, which increases the

    This allows a user to switch between volume caches, which increases the speed of testing operations where one needs to reset the volume cache.

    Signed-off-by: Alex Couture-Beil [email protected]

    Originally posted by @alexcb in https://github.com/earthly/earthly/pull/1083

    opened by jordamradford 1
Releases(0.1.4)
Owner
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
Facebook
A library that enables dynamically rebinding symbols in Mach-O binaries running on iOS.

fishhook fishhook is a very simple library that enables dynamically rebinding symbols in Mach-O binaries running on iOS in the simulator and on device

Meta 4.9k Jan 8, 2023
SwiftCop is a validation library fully written in Swift and inspired by the clarity of Ruby On Rails Active Record validations.

SwiftCop is a validation library fully written in Swift and inspired by the clarity of Ruby On Rails Active Record validations. Objective Build a stan

Andres Canal 542 Sep 17, 2022
Trackable is a simple analytics integration helper library. It’s especially designed for easy and comfortable integration with existing projects.

Trackable Trackable is a simple analytics integration helper library. It’s especially designed for easy and comfortable integration with existing proj

Vojta Stavik 145 Apr 14, 2022
Aardvark is a library that makes it dead simple to create actionable bug reports.

Aardvark Aardvark makes it dead simple to create actionable bug reports. Aardvark is made up of a collection of frameworks that provide different bug

Square 257 Dec 19, 2022
A dynamic library for Cornerstone macOS.

Cornerstone-Hook A dynamic library for Cornerstone macOS. Screenshot Overview Quick Start sudo ./install.sh Install or Upgrade CornerstoneStart.app su

ivar 3 Oct 8, 2022
iOS tool that helps with profiling iOS Memory usage.

FBMemoryProfiler An iOS library providing developer tools for browsing objects in memory over time, using FBAllocationTracker and FBRetainCycleDetecto

Facebook Archive 3.4k Dec 7, 2022
iOS project bootstrap aimed at high quality coding.

iOS Project bootstrap How do you setup your iOS projects? Since we are approaching 2015 I’m working on refreshing my project bootstrap. I’ve decided t

Krzysztof Zabłocki 2k Dec 23, 2022
Find memory leaks in your iOS app at develop time.

中文介绍 | FAQ中文 MLeaksFinder MLeaksFinder helps you find memory leaks in your iOS apps at develop time. It can automatically find leaks in UIView and UIV

Tencent 5.3k Dec 22, 2022
Find memory issues & leaks in your iOS app without instruments

HeapInspector Find memory issues & leaks in your iOS app HeapInspector is a debug tool that monitors the memory heap with backtrace recording in your

Christian Menschel 1.8k Nov 24, 2022
Awesome bug reporting for iOS apps

Buglife is an awesome bug reporting SDK & web platform for iOS apps. Here's how it works: User takes a screenshot, or stops screen recording User anno

Buglife 498 Dec 17, 2022
In-app memory usage monitoring for iOS

What's Stats Stats displays load statuses such as the memory usage, the CPU load, and the number of subviews in-app, and in realtime. How to use Just

Shuichi Tsutsumi 170 Sep 18, 2022
Makes it easier to support older versions of iOS by fixing things and adding missing methods

PSTModernizer PSTModernizer carefully applies patches to UIKit and related Apple frameworks to fix known radars with the least impact. The current set

PSPDFKit Labs 217 Aug 9, 2022
decoupling between modules in your iOS Project. iOS模块化过程中模块间解耦方案

DecouplingKit 中文readme Podfile platform :ios, '7.0' pod 'DecouplingKit', '~> 0.0.2' DecouplingKit, decoupling between modules in your iOS Project. D

coderyi 139 Aug 23, 2022
Flexible bug report framework for iOS

Clue is a simple smart-bug report framework for iOS, which allows your users to record full bug/crash report and send it to you as a single .clue file

Ahmed Sulaiman 279 Nov 3, 2022
The project used in the iOS Architect Crash Course lectures

iOS Architect Crash Course • August 2nd-8th • EssentialDeveloper.com https://www.essentialdeveloper.com/ios-architect-crash-course/aug-2021-a5220 It's

Aleksei Korolev 1 Jul 20, 2022
Skredvarsel app - an iOS, iPadOS, and macOS application that provides daily avalanche warnings from the Norwegian Avalanche Warning Service API

Skredvarsel (Avalanche warning) app is an iOS, iPadOS, and macOS application that provides daily avalanche warnings from the Norwegian Avalanche Warning Service API

Jonas Follesø 8 Dec 15, 2022
Simple iOS app blackbox assessment tool. Powered by frida.re and vuejs.

Discontinued Project This project has been discontinued. Please use the new Grapefruit #74 frida@14 compatibility issues frida@14 introduces lots of b

Chaitin Tech 1.6k Dec 16, 2022
Runtime Mobile Security (RMS) 📱🔥 - is a powerful web interface that helps you to manipulate Android and iOS Apps at Runtime

Runtime Mobile Security (RMS) ?? ?? by @mobilesecurity_ Runtime Mobile Security (RMS), powered by FRIDA, is a powerful web interface that helps you to

Mobile Security 2k Dec 29, 2022
Swift-compute-runtime - Swift runtime for Fastly Compute@Edge

swift-compute-runtime Swift runtime for Fastly Compute@Edge Getting Started Crea

Andrew Barba 57 Dec 24, 2022
LifetimeTracker can surface retain cycle / memory issues right as you develop your application

LifetimeTracker Bar style Circular style LifetimeTracker can surface retain cycle / memory issues right as you develop your application, and it will s

Krzysztof Zabłocki 2.8k Jan 4, 2023