在大约100,000次setDelegate调用之后,我的iOS应用程序在setDelegate上停顿了大约15秒。 将委托属性从弱更改为分配可以解决问题。任何想法为什么弱的属性有这么多的开销并拖延应用程序?
我怀疑弱引用是在数组中维护的,因此运行时可以循环它们并在委托对象被释放时将指针设置为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
答案 0 :(得分:6)
系统需要跟踪存储指向对象的弱指针的每个内存地址。为什么?因为如果要释放对象(其内存将被释放),所有这些指针必须首先设置为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];
答案 1 :(得分:1)
weak
有一些assign
没有的开销。
他们与strong
的不同之处在于他们不会增加ARC的保留计数。
但至于weak
和assign
之间的差异,当weak
引用指向的对象被释放时,weak
指针自动归零到{{ 1}}。 nil
不会发生这种情况。这意味着如果您尝试在取消分配的代理上调用assign
属性的方法,则会导致崩溃,但assign
也没有额外的开销。< / p>