在大约 100,000 次 setDelegate 调用后,我的 iOS 应用程序在 setDelegate 上停止了大约 15 秒。 将委托(delegate)属性从 weak 更改为 assign 可以解决问题。知道为什么 weak 属性有如此多的开销并使应用程序停止运行吗?
我怀疑弱引用保存在一个数组中,因此运行时可以循环遍历它们,并在委托(delegate)对象被释放时将指针设置为 nil。数组是否有最大尺寸?当 n 接近 100,000 时,摊位会变得更长。
示例代码如下:
头文件:
#import <Foundation/Foundation.h>
@protocol GraphDataPointDelegate <NSObject>
- (BOOL)drawGraphByDistance;
@end
@interface GraphDataPoint : NSObject
- (id)initWithYValue:(NSNumber *)yValue withTime:(NSNumber *)time withDistance:(NSNumber *)distance withDelegate:(id <GraphDataPointDelegate> )delegate;
@end
@interface Graph : NSObject <GraphDataPointDelegate>
@end
M文件
#import "Graph.h"
@interface GraphDataPoint ()
@property (nonatomic, weak, readwrite) id <GraphDataPointDelegate> delegate;
@property (nonatomic, strong, readwrite) NSNumber *yValue;
@property (nonatomic, strong, readwrite) NSNumber *time;
@property (nonatomic, strong, readwrite) NSNumber *distance;
@end
@implementation GraphDataPoint
- (id)initWithYValue:(NSNumber *)yValue withTime:(NSNumber *)time withDistance:(NSNumber *)distance withDelegate:(id<GraphDataPointDelegate>)delegate {
self = [super init];
if (self) {
self.yValue = yValue;
self.time = time;
self.distance = distance;
self.delegate = delegate;
}
return self;
}
- (id)graphXValue {
if ([_delegate drawGraphByDistance]) {
return _distance;
} else {
return _time;
}
}
@end
@implementation Graph
- (id)init {
self = [super init];
if (self) {
NSMutableArray *array = [NSMutableArray array];
NSLog(@"before");
for (int i = 0; i < 100000; i++) {
GraphDataPoint *graphData = [[GraphDataPoint alloc] initWithYValue:@1 withTime:@1 withDistance:@1 withDelegate:self];
[array addObject:graphData];
}
NSLog(@"after");
}
return self;
}
- (BOOL)drawGraphByDistance {
return YES;
}
@end
最佳答案
系统需要跟踪存储指向对象的弱指针的每个内存地址。为什么?因为如果对象要被释放(它的内存将被释放),所有这些指针必须首先设置为 nil
。这就是弱指针的特殊之处:它们不保留对象(不让它们保持事件状态),但它们也从不悬挂指针(从不指向以前的、现在死了的对象的内存地址);如果对象死亡,它们将变为 nil
。所以每当一个弱引用的值发生变化时,系统必须首先告诉一个全局弱指针跟踪管理器删除过去为这个内存地址记录的信息(如果有的话),然后记录新的信息更改对象后。不用说,整个事情必须是线程安全的,因此涉及(稍微昂贵的)锁定。
__weak id x;
// ...
x = anObject;
// ...
x = anotherObject;
// ....
x = nil;
事实上(不是真的,只是为了让概念更清楚):
__weak id x;
// ...
[WeakPointerTrackingManager lock];
x = anObject;
[WeakPointerTrackingManager var:&x pointsTo:anObject];
[WeakPointerTrackingManager unlock];
// ...
[WeakPointerTrackingManager lock];
x = anotherObject;
[WeakPointerTrackingManager var:&x pointsTo:anotherObject];
[WeakPointerTrackingManager unlock];
// ...
[WeakPointerTrackingManager lock];
x = nil;
[WeakPointerTrackingManager deleteInfoForVar:&x];
[WeakPointerTrackingManager unlock];
assign
不会那样做。只是存储对对象的引用而不增加对象保留计数器。但是,如果对象死亡,assign
变量仍然指向对象曾经 存在的内存地址。如果您现在向这个不存在的对象发送消息,您的应用可能会崩溃或发生其他未定义的事情。
但严重的是,恕我直言,每个执行 100,000 次 setDelegate 调用的应用程序都被设计破坏了。我想不出任何有意义的严肃用例。可能有更好的方法来完成您想在这里做的任何事情。
仅作记录,访问弱变量也很昂贵。
__weak id x;
// ...
[x sendMessage];
// ...
__strong id y; // Strong is optional,
// vars are strong by default
y = x;
事实上(不是真的):
__weak id x;
// ...
__strong id tmp;
[WeakPointerTrackingManager lock];
tmp = [x retain];
[WeakPointerTrackingManager unlock];
[tmp sendMessage];
[tmp release];
// ...
__strong id y;
[WeakPointerTrackingManager lock];
y = [x retain];
[WeakPointerTrackingManager unlock];
关于ios - 弱属性的大量开销?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23689155/