Blocks retain cycle from naming convention?

后端 未结 1 938
天涯浪人
天涯浪人 2020-12-06 03:18

I am surprised to find the following behavior...

@interface Foo : NSObject

- (void)addBar:(id)aBar withCompletion:(void(^)(void))completion;

@end

@interfa         


        
相关标签:
1条回答
  • 2020-12-06 03:27

    The code

    [self.foo someMethod:bar withCompletion:^{
        NSLog(@"%@", self.foo);
    }];
    

    does not generally create a retain cycle. If someMethod:withCompletion: just calls the block and returns, there is no retain cycle at all. (-[NSArray enumerateObjectsUsingBlock:] is an example.)

    Only if someMethod:withCompletion: "remembers" the block to be executed later, there is a possible retain cycle. So clang uses a heuristic to decide if it is a "setter-like" method that stores the block into a property of Foo to be executed later.

    -set<Key> and -add<Key> are accessor patterns in Key-Value Coding to set a property or add a value to a (to-many) relationship, and that is exactly what clang checks for.

    This can be seen in the Clang source code:

    /// Check for a keyword selector that starts with the word 'add' or
    /// 'set'.
    static bool isSetterLikeSelector(Selector sel) {
      if (sel.isUnarySelector()) return false;
    
      StringRef str = sel.getNameForSlot(0);
      while (!str.empty() && str.front() == '_') str = str.substr(1);
      if (str.startswith("set"))
        str = str.substr(3);
      else if (str.startswith("add")) {
        // Specially whitelist 'addOperationWithBlock:'.
        if (sel.getNumArgs() == 1 && str.startswith("addOperationWithBlock"))
          return false;
        str = str.substr(3);
      }
      else
        return false;
    
      if (str.empty()) return true;
      return !islower(str.front());
    }
    

    which is called here:

    /// Check a message send to see if it's likely to cause a retain cycle.
    void Sema::checkRetainCycles(ObjCMessageExpr *msg) {
      // Only check instance methods whose selector looks like a setter.
      if (!msg->isInstanceMessage() || !isSetterLikeSelector(msg->getSelector()))
        return;
    
      /*
       * rest omitted
       */
    
    }
    

    Your setupBar method is not treated as "setter-like" method because "set" is not followed by an uppercase letter.

    0 讨论(0)
提交回复
热议问题