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.
图的广度优先搜索是树的按层次遍历的推广,它的基本思想是:首先访问初始点vi,并将其标记为已访问过,接着访问vi的所有未被访问过的邻接点vi1,vi2,…, vi t,并均标记已访问过,然后再按照vi1,vi2,…, vi t的次序,访问每一个顶点的所有未被访问过的邻接点,并均标记为已访问过,依次类推,直到图中所有和初始点vi有路径相通的顶点都被访问过为止。
if (typeEncoding[0] == '@') { // It's an object or block
// Let's try to determine if it's a block. Blocks tend to have // @? typeEncoding. Docs state that it's undefined type, so // we should still verify that ivar with that type is a block if (strncmp(typeEncoding, "@?", 2) == 0) { return FBBlockType; }
return FBObjectType; }
return FBUnknownType; }
(2)获取一个对象强引用列表
普通OC对象会通过哪些方式强持用其他对象呢?
ivar 成员变量
集合类中的元素,比如 NSArray、NSCollection 中的元素
associatedObject
注意:通过这三种方式添加的引用都有可能是 弱引用 ,实际处理时需要将弱引用的情况排除掉。
a. ivar 成员变量
在OC中,我们可以直接添加一个 ivar ,也可以通过声明 property 的方式添加 ivar 。 ivar 和 property 都能够以弱引用的方式添加。
/** * Returns a description of the \c Ivar layout for a given class. * * @param cls The class to inspect. * * @return A description of the \c Ivar layout for \e cls. */ OBJC_EXPORT const uint8_t * _Nullable class_getIvarLayout(Class _Nullable cls) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
/** * Returns a description of the layout of weak Ivars for a given class. * * @param cls The class to inspect. * * @return A description of the layout of the weak \c Ivars for \e cls. */ OBJC_EXPORT const uint8_t * _Nullable class_getWeakIvarLayout(Class _Nullable cls) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
所以,拿到 ivar 列表之后,我们只需遍历一遍所有的 ivar ,判断该 ivar 对应的布局位置是否为强引用即可。
// NSMapTable.h /* return an NSPointerFunctions object reflecting the functions in use. This is a new autoreleased object that can be subsequently modified and/or used directly in the creation of other pointer "collections". */ @property (readonly, copy) NSPointerFunctions *keyPointerFunctions; @property (readonly, copy) NSPointerFunctions *valuePointerFunctions;
// 是否强引用 value - (BOOL)_objectRetainsEnumerableValues { if ([self.object respondsToSelector:@selector(valuePointerFunctions)]) { NSPointerFunctions *pointerFunctions = [self.object valuePointerFunctions]; if (pointerFunctions.acquireFunction == NULL) { return NO; } if (pointerFunctions.usesWeakReadAndWriteBarriers) { return NO; } } // 默认为强引用(如 NSArray) return YES; }
// 是否强引用key - (BOOL)_objectRetainsEnumerableKeys { if ([self.object respondsToSelector:@selector(pointerFunctions)]) { // NSHashTable and similar // If object shows what pointer functions are used, lets try to determine // if it's not retaining objects NSPointerFunctions *pointerFunctions = [self.object pointerFunctions]; if (pointerFunctions.acquireFunction == NULL) { return NO; } if (pointerFunctions.usesWeakReadAndWriteBarriers) { // It's weak - we should not touch it return NO; } }
if ([self.object respondsToSelector:@selector(keyPointerFunctions)]) { NSPointerFunctions *pointerFunctions = [self.object keyPointerFunctions]; if (pointerFunctions.acquireFunction == NULL) { return NO; } if (pointerFunctions.usesWeakReadAndWriteBarriers) { return NO; } } // 默认为强引用(如 NSDictionary) return YES; }
总结: 通过遍历集合元素(包括 key 和 value ),并判断其是否为强引用,可以获取集合类强引用的所有元素。
/** * Sets an associated value for a given object using a given key and association policy. * * @param object The source object for the association. * @param key The key for the association. * @param value The value to associate with the key key for object. Pass nil to clear an existing association. * @param policy The policy for the association. For possible values, see “Associative Object Behaviors.” * * @see objc_setAssociatedObject * @see objc_removeAssociatedObjects */ OBJC_EXPORT void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy) OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
其中的 policy 用于设置引用类型,有如下取值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* Associative References */
/** * Policies related to associative references. * These are options to objc_setAssociatedObject() */ typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */ OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. * The association is not made atomically. */ OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied. * The association is not made atomically. */ OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object. * The association is made atomically. */ OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied. * The association is made atomically. */ };