弱属性的大量开销? [英] Lots of overhead for weak property?

查看:20
本文介绍了弱属性的大量开销?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在大约 100,000 次 setDelegate 调用后,我的 iOS 应用在 setDelegate 上停滞了大约 15 秒.将委托属性从弱更改为分配可解决此问题.知道为什么弱属性有如此多的开销并使应用程序停顿吗?

My iOS app stalls on setDelegate for about 15 seconds after about 100,000 setDelegate calls. Changing the delegate property from weak to assign fixes the problem. Any idea why the weak property has so much overhead and stalls the app?

我怀疑弱引用保存在一个数组中,因此运行时可以通过它们循环并在委托对象被释放时将指针设置为 nil.数组是否有最大大小?当 n 接近 100,000 时,停顿会变得更长.

I suspect the weak references are maintained in an array so the runtime can loop thru them and set the pointers to nil when the delegate object gets deallocated. Does the array have a max size? The stall gets much longer as n nears 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.所以每当一个弱引用的值发生变化时,系统必须首先告诉全局弱指针跟踪管理器删除过去为这个内存地址记录的信息(如果有的话),然后记录新的信息对象改变后.不用说,整个过程必须是线程安全的,因此涉及(稍微昂贵的)锁定.

The system needs to keep track of every memory address where a weak pointer to an object is stored. Why? Because if the object is to be dealloc'ed (its memory will be freed), all those pointers must be set to nil first. That's what makes weak pointers special: They do not retain objects (not keep them alive), but they are also never dangling pointers (never pointing to memory addresses of former, now dead objects); if the object dies, they become nil. So whenever a weak reference is changed in value, the system must first tell a global weak pointer tracking manager to delete the information recorded for this memory address in the past (if any) and then record the new information after the object was changed. Needless to say that the whole thing must be thread-safe and thus involves (slightly expensive) locking.

__weak id x;
// ...
x = anObject;
// ...
x = anotherObject;
// ....
x = nil;

事实上(不是真的,只是为了理解这个概念):

is in fact (not really, just to get the concept across):

__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 变量仍然指向对象使用生存的内存地址.如果您现在向这个不存在的对象发送消息,您的应用可能会崩溃或发生其他未定义的事情.

assign does nothing like that. Is just stores a reference to the object without increasing the object retain counter. However, if the object dies, the assign variable still points to the memory address where the object used to live. If you now send a message to this non-existing object, your app may crash or other undefined things may happen.

但说真的,恕我直言,每个执行 100,000 次 setDelegate 调用的应用程序都被设计破坏了.我想不出任何有意义的严重用例.在这里可能有更好的方法来做您想做的任何事情.

But seriously, every app that performs 100,000 setDelegate calls is broken by design IMHO. I can't think of any serious use case where this would be meaningful. There is probably a much better way to do whatever you intend to do here.

就记录而言,访问弱变量也很昂贵.

Just for the records, accessing a weak variable is expensive, too.

__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];

这篇关于弱属性的大量开销?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆