4

For example, I'm using SVInfiniteScrolling (https://github.com/alexanderedge/SVInfiniteScrolling).

I have some code that looks like this...

- (void)initializeInfiniteScrollingForTableView {
    __weak MyViewController *weakSelf = self;

    [self.tableView addInfiniteScrollingWithActionHandler:^{
        MyViewController *strongSelf = weakSelf;
        if (!strongSelf.endReached) {
            [strongSelf fetchData];
        }
        else {
            [strongSelf.tableView.infiniteScrollingView stopAnimating];
        }
    }];
}

What I'm wondering is... do I need to check strongSelf for nil before using like this...

...
[self.tableView addInfiniteScrollingWithActionHandler:^{
    MyViewController *strongSelf = weakSelf;
    if (strongSelf) { // <== ** Is this needed? **
        if (!strongSelf.endReached) {

Here is why I ask. From point #3 on this link (http://www.apeth.com/iOSBook/ch12.html#EXstrongWeakDance) it says "The nil test is because, in a multithreaded situation, our weak reference to self may have vanished out from under us before the previous step; it would then be nil, because it’s an ARC weak reference, and in that case there would be no point continuing."

Is this check needed? I thought the first time you used the reference to weakSelf within the block, it is retained for the duration of the expression?

chris P
  • 6,359
  • 11
  • 40
  • 84
  • @zaph Hmm. I'm trying to think this through. Don't I have a strong reference to this block? If you look at the category which contains function addInfiniteScrollingWithActionHandler for UIScrollView's, it saves the block in a view, then add's the subview to the UIScrollView (in this case my tableview). Meaning my tableview has a strong reference to the block. And I need to break it via the weak-strong dance above. Is this not correct? – chris P Aug 17 '15 at 01:23
  • @zaph I'm hoping you can help me understand the process. This issue always throws me for a loop. So this property block right here (https://github.com/alexanderedge/SVInfiniteScrolling/blob/master/SVInfiniteScrolling/UIScrollView%2BSVInfiniteScrolling.m#L25) which is set here (https://github.com/alexanderedge/SVInfiniteScrolling/blob/master/SVInfiniteScrolling/UIScrollView%2BSVInfiniteScrolling.m#L62). My self.tableView holds this block as long as it exists, correct? And the block would reference strongly back to me (without the dance), right? What am I missing? – chris P Aug 17 '15 at 01:48
  • @zaph So I don't even need to re-assign weak to strong then, as this isn't a multithreaded situation and the block is run on the main thread correct? Just the weakSelf itself will prevent a retain cycle here and I'm good, correct? – chris P Aug 17 '15 at 03:31
  • @zaph I disagree on this retain cycle. The second code block on this link has a good example. https://blackpixel.com/writing/2014/03/capturing-myself.html (from the link: "a retain cycle is created if the block is contained by another object that self maintains a strong reference to"). While self isn't holding on to the block, the infiniteScrolling stuff is holding on to the block, and self is holding on to the infiniteScrolling stuff. The compiler won't notice and warn you, as shown in the link, but it will still create a retain cycle. – chris P Aug 17 '15 at 15:25
  • @zaph There is a strong pointer to the self.tableView in the interface file. It holds on to the block. Just like in that last link, there is no pointer to the block, but there is a pointer to this tableView which holds the block. – chris P Aug 17 '15 at 15:48
  • When the block complete it releases all retains it took as long as there is no strong point to the **block** byt since there is no pointer to the block there is no strong pointer. Completes means the last sttement is executed as in any scope. But have fun. – zaph Aug 17 '15 at 16:05

3 Answers3

2

For the code you posted, checking for nil is unnecessary.

This line of code:

if (!strongSelf.endReached) {

Will eval to false if strongSelf is nil. Further reading: Sending a message to nil?

And then this line of code will execute:

[strongSelf.tableView.infiniteScrollingView stopAnimating];

That line will just do nothing at all (not even an error) if strongSelf is nil.

However, some developers consider it a best practice to check for nil anyway, incase somebody later adds the code and it does care about nil. So you should consider doing:

MyViewController *strongSelf = weakSelf;
if (!strongSelf) {
  return;
}

... the rest of your code ...
Community
  • 1
  • 1
Abhi Beckert
  • 32,787
  • 12
  • 83
  • 110
1

I think in your line:

if (!strongSelf.endReached) {

this will evaluate to YES if self is nil, which is not probably not you intend.

It happens that your code does the correct thing (do nothing) because both clauses only use self to call methods and then nothing will happen. The end result is the same as if you had the nil-check but the execution path is wrong. This might have consequences in the future if someone adds code that doesn't use self.

jeffwong
  • 335
  • 2
  • 9
0

Your source is referring to the fact that weakSelf may have been deallocated and nil'd before execution of the block could even begin; and therefore, before strongSelf acquired a strong reference and retained it. The strongSelf is to ensure that the object referenced by weakSelf is not nil'd during the block's execution.

There is no notion in ARC of "using" a variable making it necessary to retain it when dealing with __weak variables -- __weak is explicitly opting out of this.

With that said, the check isn't strictly speaking necessary in this case, since messages to nil are always no-ops. If you intended to insert strongSelf into an array, or if it being deallocated between messages would put your program into an invalid state, it would be another matter.

To conclude, whenever you need to guarentee that a __weak variable will not be nil'd for a period of time, use __strong, and check before using it that it is not nil.

Ben Pious
  • 4,765
  • 2
  • 22
  • 34