Do blocks save a loop from a naming convention?

I was surprised to find the following behavior ...

@interface Foo : NSObject - (void)addBar:(id)aBar withCompletion:(void(^)(void))completion; @end @interface AwesomeClass : NSObject @property (strong, nonatomic) Foo *foo; - (void)doSomethingWithBar:(id)bar; @end @implementation AwesomeClass - (void)doSomethingWithBar:(id)bar { [self.foo addBar:bar withCompletion:^{ NSLog(@"%@", self.foo); }]; } 

In Xcode 4.6.1, I get a warning in the implementation of -doSomethingWithBar: that "Capturing" self "strongly in this block is likely to lead to a save loop."

However, if I reorganize the method name -addBar:withCompletion: to -setupBar:withCompletion: this warning will disappear. My surprise at this seems to illustrate that I have a gap in my knowledge regarding Objective-C naming conventions!

+7
source share
1 answer

The code

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

usually does not create a save loop. If someMethod:withCompletion: just calls the block and returns, there is no hold loop at all. ( -[NSArray enumerateObjectsUsingBlock:] - example.)

Only if someMethod:withCompletion: "remembers" a block that will be executed later, is there a possible save loop. Thus, clang uses heuristics to decide whether this method is β€œsetter”, which stores the block in the Foo property, which will be executed later.

-set<Key> and -add<Key> are key-value-encoded access patterns to set a property or add a value to relationships (for many), and this 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 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 setupBar not considered a method similar to the setter-like method because "set" is not followed by an uppercase letter.

+19
source

All Articles