更好的方式:
NSHashTable == 弱引用版的NSSet
NSMapTable == 弱引用版的NSDictionary
旧文
背景:
有一个单例类, 提供了定位服务, 然后通过数组来持有代理对象, 以达到多个代理效果.
问题:
那么问题来了, 数组是强引用持有对象的, 所以会造成持有的对象不能释放问题. (例如VC已经关闭, 但是还被数组持有, 所以不会走dealloc方法)
解决思路:
创建一个NSObject对象(暂命名为weakObject), weak持有代理对象(vc), 然后单例类的数组持有该weakObject对象. 这样vc就能正常释放了.
注意点:
-
其实是把强持有vc的问题转换给了weakObject, 所以数组中weakObject对象还是会越来越多. 所以需要在调用weakObject.delegate时进行判断. 如果weakObject.delegate==nil,则把weakObject从单例类的数组中移除.
-
从数组中移除元素时, 不能再for循环内进行, 否则会crash. 可使用NSArray的enumerateObjectsUsingBlock方法来进行遍历移除. 参考: http://blog.csdn.net/wzf906819823/article/details/47069205
DEMO:
单例类:
@interface SYLocationManager : NSObject
/**
* 添加监听者
*
* @param listener 监听者
*/
- (void)addListener:(id<SYLocationListener>)listener;
@end
@implementation SYLocationManager
- (void)addListener:(id<SYLocationListener>)listener {
if (!_listenerList) {
_listenerList = [[NSMutableArray alloc] init];
}
SYLocationWeakTarget *weakTarget = [[SYLocationWeakTarget alloc] init];
weakTarget.delegate = listener;
[_listenerList addObject:weakTarget];
}
// 事件回调
- (void)notifyLocateSuccess {
[self removeAllIncalidDelegate];
for (SYLocationWeakTarget *weakTarget in _listenerList) {
if ([weakTarget.delegate respondsToSelector:@selector(locateResult:)]) {
[weakTarget.delegate locateResult:TRUE];
}
}
}
// 移除已释放的代理对象
- (void)removeAllIncalidDelegate {
[_listenerList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
SYLocationWeakTarget *target = (SYLocationWeakTarget *)obj;
if (target.delegate == nil) {
[_listenerList removeObject:target];
}
}];
}
@end
弱引用类
@interface SYLocationWeakTarget : NSObject
@property (nonatomic, weak) id<SYLocationListener> delegate;
@end