Variable Capturing


#1

The example in “Variable Capturing” seems to have several errors, and the discussion of the issue is quite confusing and inadequate.

The line BNRItemCell *strongCell = weakCell; is trying to solve a problem that doesn’t exist.

The block is called by the cell, on the main thread; any recycling or deallocing of the cell would also happen on the main thread—thus the block has to complete before this can happen! The scenario that the strongCell reference is trying to solve just doesn’t exist. Moreover, this is the norm in this kind of code. Because all of the critical sections are on the main thread, concurrency is typical not a major concern—it has to be executed serially.

Furthermore, the strongCell is never checked for nil, so if there was a race condition, the presented solution wouldn’t solve it! Creating the strong reference earlier in the block than the call to convertRect:fromView: that references it does nothing to ensure that the weak reference was still intact when the strong reference was created. If there were a race condition, either a lock needs to be taken prior to entering the critical section—standard concept for dealing with race conditions; or—the likely solution in this case—the strong reference needs to be checked after it ensures that the cell cannot be dealloced.

Of course, in almost all cases (possibly in all cases—in practice that seems to be the case), a reusable table view cell is only dealloced when the table view is; in this case, the block has a strong reference to the view controller, which has a strong reference to the table view. So, main-thread issues aside, what could cause the cell do be dealloced is a total mystery. That said, the more likely scenario, if there wasn’t the whole main-thread synchronization issue, is that the cell is recycled—and a strong reference would do nothing to prevent or detect this.

Finally, in context for someone just seeing these issues for the first time, the example is just confusing. It is in no way explicit that the concern is a race condition, or anything having to do with concurrency. Those are involved topics, so covering them in depth seems impractical. But treating them as issues that are typically a concern is also problematic; in practice, these are rarely problems for code that is dealing with anything UIKit-related—it’s all serialized on the main thread.

Simply introducing the weak reference on its own and talking about the retain cycle seems like enough information; no need to muddy the waters with an advanced, tangential discussion. That could be relegated to a “For the More Curious”, where several paragraphs could be used to talk about race conditions. Also, mentioning that anything UI-related typically depends on the main thread and so doesn’t need to concern itself with these race conditions seems very appropriate.


#2

I agree with your general assessment, but this is more of a “general pattern for blocks” which is extremely important. Although it doesn’t matter much in this specific circumstance, the pattern comes up frequently. Here is a blog post I found with a quick search that dives into this pattern: blog.random-ideas.net/?p=160

So yes, the discussion might need some work, but the concept is still very important.


#3

To be clear, there seem to be two concepts at work here:

  1. A block capturing weak-qualified auto variables set to pointers to objects which retain the block–this avoids the retain cycle.
  2. Assigning this weak-qualified variable to a strong-qualified variable within the block, so that another thread can’t cause the object to be dealloced during block execution.

When you say this is a “general pattern for blocks”, it’s unclear if you are referring to 1, 2, or both. 1 is definitely a general patter; 2 is definitely not a general pattern. 2 is one of many techniques to try to make a block reentrant.

Number 1 we agree on, and is pretty well discussed.

Number 2 we do not agree on; this has absolutely no place here; the discussion is totally inadequate; and worst of all, the code has a bug so it doesn’t do what it purports to!

The blog you cite actually supports my case: see the end of the article, in the “Update from December 2013”:

Jiva may choose to always choose to take the extra step, but he’s clear that it is only necessary when dealing with threading. He also points out that this is only a small piece of writing reentrant code. Furthermore, It seems like you should agree with me; on page 78 of the book:

So why a pattern that is only necessary when dealing with threads–and only makes sense if you understand multi-threading–when you are explicitly avoiding talking about multi-threading? Simply calling it a “general pattern” seems to miss all of these points–it resembles making all properties atomic because it might occassionally makes things a bit more thread-safe–albiet without any performance implications.

Finally, from your response it is not clear you understand the error in the code. Changing the line:

if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {

to:

if (strongCell && [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {

would fix this. Yes, the blog you cite make the same mistake–but without checking strongCell, there is no “guarantee that the cell hangs around”. As the example is written, the pattern in no way does what it purports to. it’s the single-threaded nature of the application–and much of UI code, even in a multi-threaded application–that prevents the error; and I would argue that is a much more general pattern than the second pattern illustrated in this example.

It seems like the first concept–avoiding the retain cycle–would be a lot clearer without muddling it by including the second–worrying about a threading issue. If the pattern is viewed as important enough to merit inclusion, discussing it in chapter 21 when the main thread and dispatch_async seems like it would be more appropriate; then there would be some context, and there wouldn’t be the danger of confusing the two issues.


#4

Thank you for your thoughtful response; it was very helpful. I should have taken more time to respond to more of your points in my first reply.

I see two main problems here:

  1. The bug in the code. This is a problem and should be resolved. I did recognize it and should have responded to it in my last reply. As with many thing, though, it’s going to depend on the actual code as to whether the check for strongSelf is necessary. It is necessary in the book’s code, but it is not in the (albeit very simple) code in Jiva’s blog post.
  2. The fact that this is casually addressed in the book. You’re absolutely right here that this is part of a larger issue that is outside the scope of the book. I plan on revising this section soon.

I appreciate you stopping by the forums and bringing this up. Cheers!


#5

I appreciate the discussion at hand here. But for someone who is a noobie, could someone dumb it down into a summary for me? I found this section particularly confusing and perhaps something I will only understand later down the road.