菜鸟教程小白 发表于 2022-12-12 10:55:49

ios - ARC block ,弱和保留计数


                                            <p>我以为我已经很了解弱引用和 block ,但是在尝试下面的代码片段时,有一些我不明白的地方。<br><br>方法<strong>测试 1 </strong> : 一切正常,对象不保留<br><br>方法<strong>测试 2 </strong> : 我不明白为什么对象似乎一直保留到方法结束 <strong>测试3 </strong> !甚至明确设置 <code>object = nil</code>在方法结束时 <strong>测试 2 </strong>不会改变任何东西。<br><br>方法<strong>测试3 </strong> : 对象没有保留。为什么是方法<strong>测试 2 </strong>不是这样吗?<br><br>作为一个附带问题,我实际上想知道弱变量是否是线程安全的?即,如果我在尝试从不同线程访问弱变量时永远不会得到任何 BAD_ACCESS 异常。<br><pre><code>@interface Object : NSObject
@property (nonatomic) NSInteger index;
@end

@implementation Object

- (id)initWithIndex:(NSInteger) index {
    if (self = ) {
      _index = index;
    }
    return self;
}

- (void)dealloc {
    NSLog(@&#34;Deallocating object %d&#34;, _index);
}

@end
</code></pre><br>测试方法<br><pre><code>- (void) test1 {
    NSLog(@&#34;test1&#34;);
    Object* object = [ initWithIndex:1];
    NSLog(@&#34;Object: %@&#34;, object);
    __weak Object* weakObject = object;
    dispatch_async(dispatch_queue_create(NULL, NULL), ^{
      //NSLog(@&#34;Weak object: %@&#34;, weakObject);
      ;
      NSLog(@&#34;Exiting dispatch&#34;);
    });
    ;
    NSLog(@&#34;Exiting method&#34;);
}

- (void) test2 {
    NSLog(@&#34;test2&#34;);
    Object* object = [ initWithIndex:2];
    NSLog(@&#34;Object: %@&#34;, object);
    __weak Object* weakObject = object;
    dispatch_async(dispatch_queue_create(NULL, NULL), ^{
      NSLog(@&#34;Weak object: %@&#34;, weakObject);
      ;
      NSLog(@&#34;Exiting dispatch&#34;);
    });
    ;
    NSLog(@&#34;Exiting method&#34;);
}

- (void) test3 {
    NSLog(@&#34;test3&#34;);
    Object* object = [ initWithIndex:3];
    NSLog(@&#34;Object: %@&#34;, object);
    NSValue *weakObject = ;
    dispatch_async(dispatch_queue_create(NULL, NULL), ^{
      NSLog(@&#34;Weak object: %@&#34;, );
      ;
      NSLog(@&#34;Exiting dispatch&#34;);
    });
    ;
    NSLog(@&#34;Exiting method&#34;);
}

- (void) test {
    ;
    ;
    ;
    ;
    ;
}
</code></pre><br>上面的输出是:<br><pre><code>2013-05-11 19:09:56.753 test test1
2013-05-11 19:09:56.754 test Object: &lt;Object: 0x7565940&gt;
2013-05-11 19:09:57.755 test Exiting method
2013-05-11 19:09:57.756 test Deallocating object 1
2013-05-11 19:09:58.759 test Exiting dispatch
2013-05-11 19:10:00.758 test test2
2013-05-11 19:10:00.758 test Object: &lt;Object: 0x71c8260&gt;
2013-05-11 19:10:00.759 test Weak object: &lt;Object: 0x71c8260&gt;
2013-05-11 19:10:01.760 test Exiting method
2013-05-11 19:10:02.760 test Exiting dispatch
2013-05-11 19:10:04.761 test test3
2013-05-11 19:10:04.762 test Object: &lt;Object: 0x71825f0&gt;
2013-05-11 19:10:04.763 test Weak object: &lt;Object: 0x71825f0&gt;
2013-05-11 19:10:05.764 test Exiting method
2013-05-11 19:10:05.764 test Deallocating object 3
2013-05-11 19:10:05.767 test Deallocating object 2
2013-05-11 19:10:06.764 test Exiting dispatch
</code></pre></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p>在我触及你的一些问题之前,我对你的三个测试有两个观察:<br><li>您的测试很复杂,因为您连续运行所有三个测试,而不是回到运行循环,因此您的自动释放池没有被刷新(因此它使事情看起来像它们持续的时间比它们长通常会)。您应该进行测试,一次一个测试,以真正了解发生了什么。如果您对某个对象的生命周期得出结论是不好的,而您确实可能只是遇到了一些事实,即您没有让自动释放池被刷新。 </li><li>您正在以 <code>dispatch_async</code> 的身份进行所有这些测试。 ,它非常快速地启动分派(dispatch) block ,有时比底层对象超出范围更快,并且您经常访问 <code>weakObject</code>作为调度 block 中的第一步。我建议使用 <code>dispatch_after</code> (所以你真的给了调用方法一个让变量超出范围的机会),所以你最好看看发生了什么。 </li><br>您的测试是一个很好的数据点,但我认为使用 <code>dispatch_after</code> 测试相同的东西也很有用一次做一个测试,少一些 <code>sleepForTimeInterval</code> .感觉就像你测试的一些特性正在伪造一些关键行为。<br><br>反正你问:<br><br> <blockquote>
<p>Method test2: I don&#39;t understand why the object seems to get retained until the end of method test3! Even explicitly setting object = nil at the end of method test2 does not change anything.</p>
</blockquote><br><br>毫无疑问掉进了自动释放池,直到<code>test</code>才会被排空。方法完成。<br><br>对于我之前的观点,尝试做 <code>test2</code>再次,但在访问 <code>weakObject</code> 之前让操作等待两秒钟(或者去掉所有这些 <code>sleepForTimeInterval</code> 语句并使用 <code>dispatch_after</code> 而不是 <code>dispatch_sync</code> ):<br><pre><code>- (void) test2 {
    NSLog(@&#34;test2&#34;);
    Object* object = [ initWithIndex:2];
    NSLog(@&#34;Object: %@&#34;, object);
    __weak Object* weakObject = object;
    dispatch_async(dispatch_queue_create(NULL, NULL), ^{
      ;      // new sleep
      NSLog(@&#34;Weak object: %@&#34;, weakObject);
      ;
      NSLog(@&#34;Exiting dispatch&#34;);
    });
    // ;       // not really necessary
    NSLog(@&#34;Exiting method&#34;);
}
</code></pre><br>您会发现这更符合您的预期。<br><br> <blockquote>
<p>Method test3: the object is not retained. Why is method test2 not behaving like this?</p>
</blockquote><br><br>不用说,您的 <code>test3</code>是严重的坏消息,很容易崩溃。例如,尝试注释掉 sleep 行:<br><pre><code>- (void) test3 {
    NSLog(@&#34;test3&#34;);
    Object* object = [ initWithIndex:3];
    NSLog(@&#34;Object: %@&#34;, object);
    NSValue *weakObject = ;
    dispatch_async(dispatch_queue_create(NULL, NULL), ^{
      NSLog(@&#34;Weak object: %@&#34;, );
      ;
      NSLog(@&#34;Exiting dispatch&#34;);
    });
//    ;
    NSLog(@&#34;Exiting method&#34;);
}
</code></pre><br>我觉得它的行为不像 <code>weak</code>和更多类似 <code>unsafe_unretained</code> .<br><br> <blockquote>
<p>As a side question, I was actually wondering if weak variables are thread safe? ie if I will never get any BAD_ACCESS exception when trying to access a weak variable from different threads.</p>
</blockquote><br><br>您可以通过多种方式获得异常。如果您通过 <code>weakObject</code>某些方法要求它不是 <code>nil</code> (例如 <code>NSMutableArray</code> 方法 <code>addObject</code> ),你会得到一个异常(exception)。如果您为 <code>nil</code> 取消引用 ivars,您也可以获得异常。对象指针,例如<code>obj-&gt;objectIvar</code> .例如,想象一个 <code>Object</code>实例方法,<code>doSomethingLater</code> ,它使用弱引用来确保它不保留 <code>Object</code> ,但随后有一个本地强引用,因此它可以取消引用 ivar:<br><pre><code>- (void)doSomethingLater
{
    __weak Object *weakSelf = self;

    double delayInSeconds = 10.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
      Object *strongSelf = weakSelf;
      NSLog(@&#34;%d&#34;, strongSelf-&gt;_index); // **BAD** - this can crash of `self` has been released by this point
    });
}
</code></pre><br>因此,您通常将上述内容替换为:<br><pre><code>- (void)doSomethingLater
{
    __weak Object *weakSelf = self;

    double delayInSeconds = 10.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
      Object *strongSelf = weakSelf;
      if (strongSelf) {
            NSLog(@&#34;%d&#34;, strongSelf-&gt;_index);
      }
    });
}
</code></pre><br>不过,老实说,为什么第一个代码示例会崩溃而第二个代码示例不会崩溃的详细信息不如在异步编程中明智地使用对象引用很重要这一显而易见的事实以及未能仔细处理这些情况重要可能导致异常。经常检查 <code>weakObject</code>不是 <code>nil</code>可以防止许多此类问题(有一些我不打算讨论的警告)。这在调用对象方法时不太重要(因为向 <code>nil</code> 发送任何消息都会导致 <code>nil</code>),但在您的 <code>weakObject</code> 时很重要。是一个参数或正在为 ivar 取消引用。<br><br>但要明确的是,尽管如此,这些都与线程安全没有任何关系。您可以通过正确处理同步来实现线程安全,例如 <a href="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-SW1" rel="noreferrer noopener nofollow">locking mechanisms</a>或通过明智的<a href="https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/ThreadMigration/ThreadMigration.html#//apple_ref/doc/uid/TP40008091-CH105-SW3" rel="noreferrer noopener nofollow">use of queues</a> (串行队列;或并发队列的读写器模式,<code>dispatch_barrier_async</code> 用于写入,<code>dispatch_sync</code> 用于读取)。<br><br>仅仅因为您在代码中仔细处理对象引用以免出现异常,并不意味着您已经实现了线程安全。线程安全还涉及另一层问题。</p>
                                   
                                                <p style="font-size: 20px;">关于ios - ARCblock ,弱和保留计数,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/16499739/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/16499739/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: ios - ARC block ,弱和保留计数