0

I have a tableview, where much like stackoverflow, a user can select an answer as being the best.

I have an NSMutableArray of *answerContainers, which contain an Answer object.

Let's say the question has 10 answers. The user who asked the question choose the 3rd answer as the best.

I fire off a call to mark the answer as the best on the server, and the result is the updated Answer object, which I want to manipulate in my completion block.

So it looks something like this...

- (void)selectBestAnswer {
    for (AnswerContainer *answerContainer in self.answerContainers) {
        if (answerContainer.selected) { //can only be 1 selected
            Answer *answer = answerContainer.answer;
            QuestionDetailTableViewController * __weak weakSelf = self;
            [answer markAsBestAnswer:^(BOOL success, id responseObject, NSInteger statusCode, NSArray *messages, NSArray *errors) {
                if (success) {
                    QuestionDetailTableViewController *strongSelf = weakSelf;
                    Answer *answer = [Answer instanceFromDictionary:responseObject];

                    [strongSelf replaceAnswerWithAnswer:answer];

                    [strongSelf reloadTableView];
                }
            }];
        }
    }
}

Here are my questions

1) Should I make my weak self of the entire controller, like this. Or do you typically only do this for say the answerContainer?

chris P
  • 6,359
  • 11
  • 40
  • 84
  • 1
    _Why_ are you declaring a `weak` variable `weakAnswerContainer`? You don't use it later, so what's it for? Do you even know why you're doing it? Where did `weakSelf` come from? Your code makes no sense. – matt Jul 31 '15 at 17:25
  • You have self as an AnswerContainer and weakSelf as a QuestionDetailTableViewController. What is the class this method is in? – dudeman Jul 31 '15 at 17:27
  • @MikeAtNobel whoops. Was in the middle of switching something in my code when I copied that into the question. I fixed both type to be the same now. – chris P Jul 31 '15 at 17:30
  • The reference to self is the controller. It has nothing to do with the AnswerContainer as far as I can see. Check this [link](http://stackoverflow.com/questions/11373546/when-to-use-strong-or-weak-for-properties) to see when you should use a weak modifier. – Tommie C. Jul 31 '15 at 17:32
  • @TommieC. Had a type on the class names which is now fixed. – chris P Jul 31 '15 at 17:33
  • That makes more sense, but now i'm just wondering why you're even making a weak copy when you don't use it (except to assign to a non-weak variable)? – dudeman Jul 31 '15 at 17:35
  • @MikeAtNobel That is the correct thing to do. – matt Jul 31 '15 at 17:35
  • You do not need to have the weak self defined inside of the loop. Try moving it before you start looping. You can use the weak reference inside of the block. Not sure why you need a strong self variable. That is just self.whatever. You only need to use a weak self inside the loop under certain conditions. It's still a bit unclear of what you are trying to accomplish. Can you try explaining what you expect to happen and why you need to use weak/strong references in this context? Just try to describe it in English. – Tommie C. Jul 31 '15 at 17:35
  • The question is: are you getting a retain cycle? If not, why are you bothering with all this? It's as if you're using `weakself` in a kind of knee-jerk way without knowing what it's for. – matt Jul 31 '15 at 17:36
  • @MikeAtNobel Because I'm referring to self twice within my block. Check: https://dhoerl.wordpress.com/2013/04/23/i-finally-figured-out-weakself-and-strongself/ Is this not correct? – chris P Jul 31 '15 at 17:36
  • @matt Why is that correct? – dudeman Jul 31 '15 at 17:36
  • @MikeAtNobel That is the "weak-strong dance". If you don't do it, the weak reference can vanish out from under you during the block. See my book: http://www.apeth.com/iOSBook/ch12.html#EXstrongWeakDance – matt Jul 31 '15 at 17:37
  • @matt No I'm not getting a retain cycle right now, but shouldn't I be coding defensively to prevent that? – chris P Jul 31 '15 at 17:38
  • @chrisP In a word, no. If it ain't broke don't fix it. – matt Jul 31 '15 at 17:39
  • @matt Can you help me reason through this, as I'm not too sure. Let's say I don't put in this code. What would happen under the following circumstance: User votes on best answer. We don't block the GUI while the server call is made. The user backs out of this window, destroying the controller. The server call returns, and this block code is executed. Do the call to self present an issue? – chris P Jul 31 '15 at 17:42
  • @matt and Chris P. Very interesting. Never seen that before. – dudeman Jul 31 '15 at 17:44
  • @MikeAtNobel Here's a good article if you're interested: https://dhoerl.wordpress.com/2013/04/23/i-finally-figured-out-weakself-and-strongself/ – chris P Jul 31 '15 at 17:44
  • @ChrisP Thanks. I saw it in your previous post, but I was only allowed to tag one user in my comment. – dudeman Jul 31 '15 at 18:04
  • @chrisP "The user backs out of this window, destroying the controller" If you leave the reference to `self` to pass strongly into the block, and if the block is retained so that `self` is retained, the controller (`self`) will _not_ be destroyed. – matt Jul 31 '15 at 19:58

2 Answers2

2

Just throw all the "weak" stuff away. It is needed only in the very special situation where a block retains self and self retains the block, thus causing a retain cycle which makes self leak later on. You are not in that situation so don't use the "weak-strong dance" at all.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • when would I be in this situation? Only if I have an actual strong reference to the block, such as `@property MyCompletionBlock completionBlock`? And then my completion block code, reference self? – chris P Jul 31 '15 at 17:43
  • Basically yes. But see the discussion surrounding the link to my book that I already gave you: it describes a few surprising situations where this can arise. – matt Jul 31 '15 at 17:49
0

If you have only one selected answer, why not - (void)bestAnswerSelected:(Answer *)answer

- (void)sendAndUpdateBestAnswer:(Answer *)answer {
        [answer markAsBestAnswer:^(BOOL success, id responseObject, NSInteger statusCode, NSArray *messages, NSArray *errors) {
            if (success) {

                Answer *answer = [Answer instanceFromDictionary:responseObject];

                [self replaceAnswerWithAnswer:answer];

                [self reloadTableView];
            }
        }];
}

I presume that selectBestAnswer is ins

Klevison
  • 3,342
  • 2
  • 19
  • 32