FBRetainCycleDetector源码分析

FBRetainCycleDetector:iOS库可帮助检测运行时中的保留周期 FBRetainCycleDetector 使用运行时分析查找保留周期的iOS库。 关于 保留周期是造成内存泄漏的最常见方法之一。 创建保留周期非常容易,而且往往很难发现它。 FBRetainCycleDetector的目标是帮助在运行时查找保留周期。 该项目的特点受影响。 安装 迦太基 在您的Cartfile中添加: github "facebook/FBRetainCycleDetector" FBRetainCycleDetector是从非调试版本中构建的,因此当您要对其进行测试时,请使用 carthage update --configuration Debug 可可豆 在您的podspec中添加: pod 'FBRetainCycleDetector' 您将只能在Debug版本中完全使用FBRetainCycleDetector 。 这由可提供给内部版本的控制,以使其在其他 立即下载

概述

FBRetainCycleDetector是facebook开源的一个用来检测对象是否有强引用循环的静态库。

strong和weak

strong和weak在声明中使用表示这是一个强引用还是弱引用对象。

  • 强引用:只要引用存在,对象就不能被销毁。
  • 弱引用:弱引用不会导致对象不能销毁,只要没有强引用了,对象就会销毁,对象销毁后,弱引用会自动设置为nil。
  • 当一个对象不再有strong类型的指针指向它的时候,它就会被释放,即使该对象还有weak类型的指针指向它。
  • 一旦最后一个指向该对象的strong类型的指针离开,这个对象将被释放,如果这个时候还有weak指针指向该对象,则会清除所有剩余的weak指针。

在OC中strong就相当于retain属性,而weak相当于assign。使用weak也就是为了避免retain cycles,比如父类中含有子类对象(retain了子类),子类中又有父类的对象(子类又retain了父类),这样就会形成retain cycles,导致两个对象都无法release。而FBRetainCycleDetector做的事情就是去检测是否存在这样的retain cycles。下面是循环引用的一个例图:

在 Objective-C 中找循环引用类似于在一个有向无环图(directed acyclic graph)中找环, 而节点就是对象,边就是对象之间的引用(如果对象A持有对象B,那么,A到B之间就存在着引用)。我们的 Objective-C 对象已经在我们的图中,我们要做的就是用深度优先搜索遍历它。



使用方法

构建一个引用循环,然后使用FBRetainCycleDetector提供的接口来进行检测,具体代码如下:

     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
     
MyObject1* obj1 = [MyObject1 new];
MyObject2* obj2 = [MyObject2 new];
MyObject3* obj3 = [MyObject3 new];
obj1.object1 = obj2;
obj1.name = @"obj1";
obj2.object2 = obj3;
obj2.name = @"obj2";
obj3.object3 = obj1;
obj3.name = @"obj3";
//初始化一个引用循环检测器
FBRetainCycleDetector *detector = [FBRetainCycleDetector new];
//增加需要检测的对象
[detector addCandidate:obj1];
//寻找引用循环
NSSet< NSArray<FBObjectiveCGraphElement *> *> *retainCycles = [detector findRetainCycles];
//输出结果
NSLog( @"%@", retainCycles);

运行结果如下:

     
1
2
3
4
5
6
7
     
2016-05-15 12:38:25.208 FBRetainCycleDetectorDemo[57524:6267406] {(
(
"-> _object3 -> MyObject1 ",
"-> _object1 -> MyObject2 ",
"-> _object2 -> MyObject3 "
)
)}

也就是说检测的对象存在引用循环。

执行流程

首先需要初始化一个FBRetainCycleDetector检测器,先来分析这个类都有什么接口及其功能。

     
1
     
FBRetainCycleDetector *detector = [FBRetainCycleDetector new];

new 相当于[[alloc] init],所以会调用FBRetainCycleDetector的init函数。

     
1
2
3
4
5
6
7
     
- ( instancetype)init
{
//默认初始化一个标准的过滤器,并检查NSTimer
return [ self initWithConfiguration:
[[FBObjectGraphConfiguration alloc] initWithFilterBlocks:FBGetStandardGraphEdgeFilters()
shouldInspectTimers: YES]];
}

调用了initWithConfiguration:初始化了一个标准的过滤器,这个过滤器是为了过滤引用循环中的一些类和方法。

然后调用addCandidate添加一个需要被检测的对象。

     
1
2
3
4
5
6
7
     
- ( void)addCandidate:( id)candidate
{
//初始化一个FBObjectiveCGraphElement对象
FBObjectiveCGraphElement *graphElement = FBWrapObjectGraphElement(candidate, _configuration);
//加入NSMutableArray
[_candidates addObject:graphElement];
}

跟进FBWrapObjectGraphElement函数,发现里面调用了-[FBObjectiveCGraphElement initWithObject:configuration:namePath:]方法,其中就是初始化一个FBObjectiveCGraphElement。

重点是findRetainCycles这个方法来查找是否存在引用循环。这个方法调用了findRetainCyclesWithMaxCycleLength:来根据指定的深度进行深度搜索,默认深度是10。

     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
     
- ( NSSet< NSArray<FBObjectiveCGraphElement *> *> *)findRetainCyclesWithMaxCycleLength:( NSUInteger)length
{
NSMutableSet< NSArray<FBObjectiveCGraphElement *> *> *allRetainCycles = [ NSMutableSet new];
//遍历需要检测的对象,逐个进行检测
for (FBObjectiveCGraphElement *graphElement in _candidates) {
NSSet< NSArray<FBObjectiveCGraphElement *> *> *retainCycles = [ self _findRetainCyclesInObject:graphElement
stackDepth:length];
//合并集合
[allRetainCycles unionSet:retainCycles];
}
[_candidates removeAllObjects];
return allRetainCycles;
}

继续跟进_findRetainCyclesInObject:stackDepth:

     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
     
- ( NSSet< NSArray<FBObjectiveCGraphElement *> *> *)_findRetainCyclesInObject:(FBObjectiveCGraphElement *)graphElement
stackDepth:( NSUInteger)stackDepth
{
NSMutableSet< NSArray<FBObjectiveCGraphElement *> *> *retainCycles = [ NSMutableSet new];
//初始化深度搜索树中的一个节点
FBNodeEnumerator *wrappedObject = [[FBNodeEnumerator alloc] initWithObject:graphElement];
//根据需要检测的对象进行深度搜索
// We will be doing DFS over graph of objects
//保存当前DFS搜索树中的搜索路径
// Stack will keep current path in the graph
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];
//增加根节点,从根节点开始搜索
// Let's start with the root
[stack addObject:wrappedObject];
//判断是否已经搜索完毕
while ([stack count] > 0) {
// Algorithm creates many short-living objects. It can contribute to few
// hundred megabytes memory jumps if not handled correctly, therefore
// we're gonna drain the objects with our autoreleasepool.
@autoreleasepool {
// Take topmost node in stack and mark it as visited
//访问搜索栈中的最上面一个节点
FBNodeEnumerator *top = [stack lastObject];
//添加到objectsOnPath
[objectsOnPath addObject:top];
// 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) {
// Current node still has some adjacent not-visited nodes
BOOL shouldPushToStack = NO;
// Check if child was already seen in that path
//如果该节点已经存在被访问过的对象中,说明构成了retain cycle
if ([objectsOnPath containsObject:firstAdjacent]) {
// We have caught a retain cycle
// Ignore the first element which is equal to firstAdjacent, use firstAdjacent
// we're doing that because firstAdjacent has set all contexts, while its
// first occurence could be a root without any context
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 {
NSRange cycleRange = NSMakeRange(index, length);
NSMutableArray<FBNodeEnumerator *> *cycle = [[stack subarrayWithRange:cycleRange] mutableCopy];
[cycle replaceObjectAtIndex: 0 withObject:firstAdjacent];
// 1. Unwrap the cycle
// 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 {
// Node is clear to check, add it to stack and continue
shouldPushToStack = YES;
}
if (shouldPushToStack) {
if ([stack count] < stackDepth) {
[stack addObject:firstAdjacent];
}
}
} else {
// Node has no more adjacent nodes, it itself is done, move on
[stack removeLastObject];
[objectsOnPath removeObject:top];
}
}
}
return retainCycles;
}

上面这个函数就是DFS深度搜索的代码,以搜索对象相关的所有属性构成的搜索树进行深度搜索,一旦发现构成了环就记录下,然后继续搜索直接到达到指定的深度或搜索完毕。以上面的例子为例,搜索图是这样的:

图中从obj1节点开始搜索,依次遍历,n1、obj2、n2、obj3、n3、obj1,然后发现构成了环,然后保存环节点。

获取强引用

怎样获取自己所持有的所有引用的对象,FBRetainCycleDetector把传入的对象都封装成了一个FBObjectiveCGraphElement对象,根据对象的不同类型分为派生出子类FBObjectiveCBlock(Block)对象,FBObjectiveCBlock对象以及FBObjectiveCNSCFTimer(NSTimer)对象,不同对象获取所持有的引用时都会调用父类的allRetainedObjects,然后再进行自己的处理。

先来看看FBObjectiveCGraphElement的allRetainedObjects方法:

     
1
2
3
4
5
6
7
8
9
10
11
12
13
     
- ( NSSet *)allRetainedObjects
{
NSArray *retainedObjectsNotWrapped = [FBAssociationManager associationsForObject:_object];
NSMutableSet *retainedObjects = [ NSMutableSet new];
for ( id obj in retainedObjectsNotWrapped) {
[retainedObjects addObject:FBWrapObjectGraphElementWithContext(obj,
_configuration,
@[ @"__associated_object"])];
}
return retainedObjects;
}

[FBAssociationManager associationsForObject:]获取该对象所有通过objc_setAssociatedObject关联的对象。因为后者会增加对象的引用,为了做到这一点,必须main.m中调用[FBAssociationManager hook]进行fishhook。

     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
     
+ ( void)hook
{
#if _INTERNAL_RCD_ENABLED
std::lock_guard< std::mutex> l(*FB::AssociationManager::hookMutex);
rcd_rebind_symbols(( struct rcd_rebinding[ 2]){
{
"objc_setAssociatedObject",
( void *)FB::AssociationManager::fb_objc_setAssociatedObject,
( void **)&FB::AssociationManager::fb_orig_objc_setAssociatedObject
},
{
"objc_removeAssociatedObjects",
( void *)FB::AssociationManager::fb_objc_removeAssociatedObjects,
( void **)&FB::AssociationManager::fb_orig_objc_removeAssociatedObjects
}}, 2);
FB::AssociationManager::hookTaken = true;
#endif //_INTERNAL_RCD_ENABLED
}

该函数hook了objc_setAssociatedObject和objc_removeAssociatedObjects这两个C函数来监控。接下来看看子类的不同处理。

FBObjectiveCObject

FBObjectiveCObject的allRetainedObjects方法中首先调用了_unfilteredRetainedObjects获取所有引用对象,然后调用了filterObjects:调用过滤接口来进行过滤。

     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
     
- ( NSArray *)_unfilteredRetainedObjects
{
Class aCls = object_getClass( self.object);
if (! self.object || !aCls) {
return nil;
}
//获取一个对象的所有强引用属性
NSArray *strongIvars = FBGetObjectStrongReferences( self.object);
NSMutableArray *retainedObjects = [[[ super allRetainedObjects] allObjects] mutableCopy];
//获取强引用的引用对象
for ( id<FBObjectReference> ref in strongIvars) {
id referencedObject = [ref objectReferenceFromObject: self.object];
if (referencedObject) {
NSArray< NSString *> *namePath = [ref namePath];
[retainedObjects addObject:FBWrapObjectGraphElementWithContext(referencedObject,
self.configuration,
namePath)];
}
}
if ([ NSStringFromClass(aCls) hasPrefix: @"__NSCF"]) {
/**
If we are dealing with toll-free bridged collections, we are not guaranteed that the collection
will hold only Objective-C objects. We are not able to check in runtime what callbacks it uses to
retain/release (if any) and we could easily crash here.
*/
return retainedObjects;
}
if (class_isMetaClass(aCls)) {
// If it's a meta-class it can conform to following protocols,
// but it would crash when trying enumerating
return nil;
}
//获取集合类的引用
if ([aCls conformsToProtocol: @protocol(NSFastEnumeration)]) {
BOOL retainsKeys = [ self _objectRetainsEnumerableKeys];
BOOL retainsValues = [ self _objectRetainsEnumerableValues];
BOOL isKeyValued = NO;
if ([aCls instancesRespondToSelector: @selector(objectForKey:)]) {
isKeyValued = YES;
}
/**
This codepath is prone to errors. When you enumerate a collection that can be mutated while enumeration
we fall into risk of crash. To save ourselves from that we will catch such exception and try again.
We should not try this endlessly, so at some point we will simply give up.
*/
NSInteger tries = 10;
for ( NSInteger i = 0; i < tries; ++i) {
// If collection is mutated we want to rollback and try again - let's keep refs in temporary set
NSMutableSet *temporaryRetainedObjects = [ NSMutableSet new];
@try {
for ( id subobject in self.object) {
if (retainsKeys) {
[temporaryRetainedObjects addObject:FBWrapObjectGraphElement(subobject, self.configuration)];
}
if (isKeyValued && retainsValues) {
[temporaryRetainedObjects addObject:FBWrapObjectGraphElement([ self.object objectForKey:subobject],
self.configuration)];
}
}
}
@catch ( NSException *exception) {
// mutation happened, we want to try enumerating again
continue;
}
// If we are here it means no exception happened and we want to break outer loop
[retainedObjects addObjectsFromArray:[temporaryRetainedObjects allObjects]];
break;
}
}
return retainedObjects;
}

该函数首先调用父类方法allRetainedObjects获取associated objects。然后调用FBGetObjectStrongReferences获取强引用的属性,并得到属性的对象,最后判断是否为集合来获取集合里面的引用。FBGetObjectStrongReferences内部调用了FBGetStrongReferencesForClass通过类来获取强引用,后者先通过FBGetClassReferences获取所有引用。

     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
     
NSArray< id<FBObjectReference>> *FBGetClassReferences(Class aCls) {
NSMutableArray< id<FBObjectReference>> *result = [ NSMutableArray new];
unsigned int count;
//获取变量列表
Ivar *ivars = class_copyIvarList(aCls, &count);
for ( unsigned int i = 0; i < count; ++i) {
Ivar ivar = ivars[i];
//包装成FBIvarReference
FBIvarReference *wrapper = [[FBIvarReference alloc] initWithIvar:ivar];
//针对struct类型的处理
if (wrapper.type == FBStructType) {
NSString *encoding = @(ivar_getTypeEncoding(wrapper.ivar));
NSArray<FBObjectInStructReference *> *references = FBGetReferencesForObjectsInStructEncoding(wrapper, encoding);
[result addObjectsFromArray:references];
} else {
[result addObject:wrapper];
}
}
free(ivars);
return [result copy];
}

获得所有引用后,再通过class_getIvarLayout来提取强引用。

FBObjectiveCBlock

流程类似先获取引用然后过滤,只是获取强引用的方法是通过FBGetBlockStrongReferences来实现的。判断是不是Block的方式是新建一个空的Block然后判断该对象的是不是其子类。具体获取方法_GetBlockStrongLayout。

     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
     
static NSIndexSet *_GetBlockStrongLayout( void *block) {
struct BlockLiteral *blockLiteral = block;
/**
BLOCK_HAS_CTOR - Block has a C++ constructor/destructor, which gives us a good chance it retains
objects that are not pointer aligned, so omit them.
!BLOCK_HAS_COPY_DISPOSE - Block doesn't have a dispose function, so it does not retain objects and
we are not able to blackbox it.
*/
if ((blockLiteral->flags & BLOCK_HAS_CTOR)
|| !(blockLiteral->flags & BLOCK_HAS_COPY_DISPOSE)) {
return nil;
}
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];
}
return layout;
}

通过block的size_t大小创建相同大小的数组类型,其对象类型是FBBlockStrongRelationDetector,然后调用dispose_helper,根据判断是否调用了FBBlockStrongRelationDetector对象的release方法来判断是不是强引用,最后返回一个表示位置的数组,然后根据该数组获取具体的对象

这里使用了一个黑盒技术。创建一个对象来假扮想要调查的Block。因为知道Block的接口,知道在哪可以找到Block持有的引用。伪造的对象将会拥有“释放检测(release detectors)”来代替这些引用。释放检测器是一些很小的对象,这里是FBBlockStrongRelationDetector,它们会观察发送给它们的释放消息。当持有者想要放弃它的持有的时候,这些消息会发送给强引用对象。当释放伪造的对象的时候,可以检测哪些检测器接收到了这些消息。只要知道哪些索引在伪造的对象的检测器中,就可以找到原来Block中实际持有的对象。

image

FBObjectiveCNSCFTimer

主要是通过CFRunLoopTimerGetContext获取CFRunLoopTimerContext然后获取target和userInfo。

过滤配置器

FBObjectGraphConfiguration是一个过滤的配置器,根据传入的Block来过滤的Block调用,以及是否检查NSTimer。传入FBGraphEdgeFilterBlock的定义如下:

     
1
2
     
typedef FBGraphEdgeType (^FBGraphEdgeFilterBlock)(FBObjectiveCGraphElement *_Nullable fromObject,
FBObjectiveCGraphElement * _Nullable toObject);

fromObject是传入的对象,toObject是传入对象引用的对象,根据指定的规则来判断是否需要过滤,过滤的话就相当于断掉两个节点之间的连线,来看看官方的使用例子:

     
1
2
3
4
5
6
7
8
9
10
11
     
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];

这里过滤的是UIView类的_subviewCache属性的引用。

总结

FBRetainCycleDetector目前来说肯定还存在一些问题,还有引用关系比较复杂的话,DFS占用的内存还是挺大的。

转:http://www.alonemonkey.com/2016/05/15/fbretaincycledetector-analyse/

swift-FBRetainCycleDetector-使用运行时分析查找保留周期的iOS库 FBRetainCycleDetector - 使用运行时分析查找保留周期的iOS库 立即下载

相关推荐

工业互联网工控安全与工业质检AI大模型建设方案.pptx

工业互联网工控安全与工业质检AI大模型建设方案.pptx

iOS开发-FBRetainCycleDetector中的深度优先搜索DFS

文章目录图有向图图的实现方式遍历广度优先搜索 BFS深度优先搜索 DFSFBRetainCycleDetector的应用 最近复习 FBRetainCycleDetector 源码的时候,需要的图的查询方法,深度优先搜索,这里记录下,便于系统复习。 这里仅仅为分析FBRetainCycleDetector实现原理,采用深度优先搜索DFS 图 图由顶点(vertex)和边(edge)组成,通常表示为...

CaicaiNo.1 744

【数电课程设计】---出租车计价器

文章目录1、任务要求2、方案3、收获 1、任务要求 2、方案 3、收获

LZY 1万+

FBRetainCycleDetector深入解析

FBRetainCycleDetector是FaceBook开源的用于检测强引用循环的工具。默认是在DEBUG环境中启用,当然你也可以通过设置RETAIN_CYCLE_DETECTOR_ENABLED以始终开启。需要注意的是,FBRetainCycleDetector使用了私有API,会导致苹果审核失败,所以不建议始终开启。 当我们拿到一个工具,并且知道它可以检测强引用循环,最关心的当然是如何使用它,所以第一步,我们先学习如何使用FBRetainCycleDetector。 需要注意的是FBRetainCy

lg767201403的博客 4526

CANoe SOME/IP实战:从ARXML到VCODM的完整配置与调试

本文详细介绍了使用CANoe进行SOME/IP通信测试的完整流程,从硬件环境准备、ARXML文件处理到VCODM文件配置与调试。通过实战技巧和常见问题解决方案,帮助工程师高效完成从ARXML到VCODM的配置,确保SOME/IP通信的稳定性和可靠性。

weixin_29169899的博客 465

波束成形(Beamforming)详解

波束成形(Beamforming)是一种智能天线技术,通过调整天线阵列的相位和幅度,使信号在特定方向上增强,同时抑制干扰,提高通信性能。根据实现方式,可分为固定波束成形和自适应波束成形;按计算方式,则包括模拟、数字和混合波束成形。该技术在 5G/6G、毫米波通信、MIMO、车联网、卫星通信 等领域广泛应用。其关键技术包括 相控阵、预编码、波束扫描和波束跟踪。未来,结合 AI、超大规模 MIMO(XL-MIMO)和太赫兹通信,波束成形将在无线通信、智能交通、环境感知等领域发挥核心作用。

斯黄人民的博客 2382

探索FBRetainCycleDetector:检测iOS内存泄漏的新武器

是Facebook开源的一款强大的工具,专为解决iOS应用中的内存泄漏问题而设计。本文将详细介绍该项目,技术原理,应用场景及其独特之处,帮助开发者更好地管理和优化iOS应用的内存。 ## 项目简介 在iOS开发中,内存泄漏是一个常见的性能瓶颈,可能导致应用无响应,消耗过多电量,甚至造成数据丢失。FBRetainCycleDetector通过自动检测并报告应用程序中的强引用循环(retain c...

gitblog_00056的博客 680

【人工智能】Spring AI项目在定义 `response_format` 的 Invalid schema for response_format ‘custom_schema‘

确保。

奶爸的编程之路,也就一周冷个三天 560

【亲测免费】 FBRetainCycleDetector 使用教程

FBRetainCycleDetector 是一个用于在 iOS 应用中检测内存泄漏的工具。内存泄漏通常是由于对象之间的循环引用(retain cycles)引起的,这种问题在运行时很难被发现。FBRetainCycleDetector 通过运行时分析,帮助开发者找到这些循环引用,从而避免内存泄漏。 该项目由 Facebook 开发并开源,适用于 iOS 平台,支持 Objective-C 和 ...

gitblog_00802的博客 1404

【BUG已解决】RuntimeError: CUDA error: no kernel image is available for execution on the device 解决方案

方案适用场景推荐指数精确匹配官方版本绝大多数场景(大众消费卡+主流数据中心卡)⭐⭐⭐⭐⭐安装历史旧版本Pascal/Maxwell等老显卡⭐⭐⭐⭐安装Nightly版本刚发布的最新架构显卡⭐⭐⭐⭐源码编译官方各版本均不支持的特殊硬件⭐⭐Docker官方镜像追求省心、避免版本排查⭐⭐⭐⭐⭐的核心是PyTorch 预编译包不包含你显卡对应的计算能力架构先确认显卡真实计算能力),而不是盲目重装驱动对比 PyTorch 支持的架构列表),定位是否真的不匹配精确匹配官方版本。

博客 1万+

OpenClaw进阶实战(二十五):抖音企业号——私信回复与视频评论分析

摘要:本文介绍抖音企业号自动化运营方案,突破抖音强风控体系的技术挑战。针对内容生产、私信互动和账号矩阵三大场景,提出混合架构解决方案: 私信自动化:结合关键词触发与随机延迟(45秒±15),实现拟人化回复,支持价格咨询/商务合作等高优先级消息识别 混合技术栈:官方API(合规)+真机RPA(覆盖长尾需求)+AI辅助生成(脚本→剪辑→发布闭环) 安全策略:通过设备指纹模拟、行为间隔随机化和内容差异化处理规避风控,提供每日200条的安全操作阈值。配套OpenClaw技能实现一键部署,包含cookie配置、回复规

PeterPan的博客 141

关于fishhook

关于fishhook,最近关注到的几篇文章,mark一下 目前只是个人简单的学习了其实现原理及应用场景,真正理解全部其实内容挺多的。之前了解过些逆向,所以看起来还算比较流畅,后续有时间实践后再总结下 1、fishhook 的实现原理浅析 2、fishhook 使用场景&源码分析 3、objc_msgSend Hook 精简学习过程 4、FBRetainCycleDete...

弄潮者的博客 1279

基于STM32设计的ECG+PPG人体参数测量系统(华为云IOT)(217)

该系统的核心是基于STM32F103RCT6微控制器,这是一种高性能、低功耗的嵌入式处理器,适用于各种复杂的控制和数据处理任务。系统利用先进的传感器技术,如光电传感器PulseSensor模块来准确测量人体脉搏血氧饱和度及心率(PPG),同时采用AD8232生物电信号采集模块捕捉并处理人体的心电图信号(ECG),从而全面反映用户的心脏活动状态。

1万+

MLLeaksFinder

基本原理: MlLeaksFinder是app运行的过程中,检测内存泄漏的第三方库,可以帮助在代码调试阶段发现问题。通过method swizzled hook 对象生命周期的方法,在对象结束生命周期的时候,在指定时间之后给对象发送某个消息。如果这个时候对象已经被释放,消息不会被执行,如果没有释放说明发生了内存泄漏,消息就会被执行,从而提醒开发人员。通过递归的方式,会记录下某个视图或者contro...

weixin_34417200的博客 703

【python股票价格预测】基于CNN-LSTM的股票价格预测(附python代码)

【python股票价格预测】基于CNN-LSTM的股票价格预测(附python代码)

k8291121的博客 2008

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

循环引用检测工具FBRetainCycleDetector

FBRetainCycleDetector用以检测循环引用,可以检测NSObject的循环引用、关联对象(Associated Object)的循环引用、block的循环引用。 一句话描述原理: 把可疑的对象告诉FBRetainCycleDetector,然后FBRetainCycleDetector会查找可疑对象的所有strong属性, 关联对象, block,构成一个最大长度为10的有向图, 按照深度优先遍历看看是否存在环, 如果有环说明存在循环应用,并把所存在的引用循环中各对象和引用在终端进行打印.

gcs的博客 2026

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

FBRetainCycleDetector:iOS内存泄漏检测利器

在iOS开发中,内存泄漏是一个常见且棘手的问题,而**保留循环(Retain Cycle)**是导致内存泄漏的主要原因之一。保留循环通常发生在对象之间相互持有强引用,导致它们无法被释放,从而造成内存泄漏。为了帮助开发者快速定位并解决这些问题,Facebook开源了**FBRetainCycleDetector**,一个基于运行时分析的iOS库,专门用于检测保留循环。 ## 项目技术分析 **F...

gitblog_00546的博客 1227

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

使用FBRetainCycleDetector检测引用循环

在iOS开发中,对象之间形成引用循环是一个很大的问题,它会让内存无故被占,甚至还有可能影响通知的接收等。我们在写代码的时候各种小心,但有时候还是避免不了掉入陷阱。下面是几种常见的循环引用示例: 1 . 两个对象通过强引用类型的属性相互持有。 ClassA *aObj = [ClassA new]; ClassB *bObj = [ClassB new]; aObj.b = bObj; bOb

gaoyuqiang30的博客 2025

ios开发--如何在 iOS 中解决循环引用的问题(FBRetainCycleDetector框架)

稍有常识的人都知道在 iOS 开发时,我们经常会遇到循环引用的问题,比如两个强指针相互引用,但是这种简单的情况作为稍有经验的开发者都会轻松地查找出来。 但是遇到下面这样的情况,如果只看其实现代码,也很难仅仅凭借肉眼上的观察以及简单的推理就能分析出其中存在的循环引用问题,更何况真实情况往往比这复杂的多: testObject1.object = testObject2; testObje

zzzzzdddddxxxxx的专栏 1905

FBRetainCycleDetector快速入门:5分钟学会检测iOS应用中的内存问题

iOS应用开发中,内存泄漏是常见的性能瓶颈,而**保留循环(Retain Cycle)** 则是内存泄漏的主要原因之一。FBRetainCycleDetector是Facebook开源的一款强大的iOS运行时内存检测工具,能够帮助开发者快速发现应用中的保留循环问题。🚀 ## 什么是保留循环? 在iOS开发中,当两个或多个对象相互强引用时,就会形成保留循环。这些对象无法被ARC(自动引用计数)

gitblog_00958的博客 509

如何实现 iOS 中的 Associated Object

关注仓库,及时获得更新:iOS-Source-Code-Analyze Follow: Draveness · Github 这一篇文章是对 FBRetainCycleDetector 中实现的关联对象机制的分析;因为追踪的需要, FBRetainCycleDetector 重新实现了关联对象,本文主要就是对其实现关联对象的方法进行分...

weixin_34129696的博客 207

3选1不踩坑:Higgsfield混合精度训练深度解析,bf16、fp16与bf16_mixed到底怎么选?

Higgsfield 是一个容错、高可扩展的 GPU 编排与机器学习训练框架,专为训练数十亿到万亿参数的大模型设计。本文带你彻底搞懂它在 `Llama` / `Mistral` 训练中提供的 3 种混合精度策略:**bf16、fp16 与 bf16_mixed**,并给出面向新手的最快选择指南,让你在分布式大模型训练中不再为精度选项纠结。 [![Higgsfield多节点GPU分布式训练架构图,

gitblog_00884的博客 906

如何快速掌握FBRetainCycleDetector:iOS内存泄漏检测的终极工具指南

FBRetainCycleDetector是一款强大的iOS运行时内存泄漏检测库,能够帮助开发者精准识别应用中的循环引用问题。作为Facebook开源的核心工具,它通过构建对象图并分析引用关系,让原本隐藏的内存泄漏问题无所遁形,是iOS开发必备的性能优化利器。 ## 🧩 核心功能解析:为什么选择FBRetainCycleDetector? ### 自动构建对象关系图谱 该工具最核心的能力在于

gitblog_00918的博客 830

FBRetainCycleDetector 项目常见问题解决方案

FBRetainCycleDetector 是一个由 Facebook 开发的开源项目,旨在帮助 iOS 开发者在运行时检测 retain cycles(保留周期),这是导致内存泄漏的常见原因之一。通过使用这个库,开发者可以更容易地识别出并修复那些难以发现的内存泄漏问题。该项目主要使用 Objective-C 语言编写,同时也支持 Swift 语言。 ## 2. 新手常见问题及解决步骤 ###...

gitblog_00386的博客 507

ExplorerPatcher任务栏属性窗口打不开:按症状分诊,分三层修复

装了 ExplorerPatcher 后遇到任务栏属性窗口无法打开,右键没反应,或者窗口一闪就消失,设置全调不了。下面先按症状分诊,再给出 30 秒到 5 分钟共三层修复步骤,几分钟内定位原因并恢复。 ## 症状分诊:先判断属性窗口打不开属于哪一类 右键任务栏观察一下,下面四种症状各指向不同成因,对号入座后直接跳到对应层级: | 症状 | 最可能成因 | 建议修复层级 | |---|---|

gitblog_01053的博客 435
上一篇: methodForSelector 和 instanceMethodForSelector
下一篇: iOS最佳实践
直隶码农
博客等级 码龄19年 95粉丝 103原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值