您的位置:首页 > 其它

weakself and strongself

2016-03-29 18:26 597 查看


Why you should start using @weakify and @strongify macros

By Arkadiusz Holko, 31 May 2015
I have to admit something. I haven't used @weakify
and @strongify macros in any of my projects yet. A recent discussion in my team at Macoscope and adiscussion
on Twitter started by Peter Steinberger sparked my interest in these macros. Read on to learn what I found out and why I think they're the way to go in most scenarios.


Standard approach of breaking retain cycles

Let's say we have a view controller with a property called 
model
.
We want to have a label's text update when the data inside the model changes. To do that we set up a model:

- (void)setUpModel
{
Model *model = [Model new];

// this block is called by the model when its underlying data changes
model.dataChanged = ^(NSString *title) {
self.label.text = title;
};

self.model = model;
}


With these few innocent lines of code we introduced a retain
cycle. Our view controller retains the model, which in turn retains a block holding the view controller. We can easily break this retain cycle, by introducing new local variables with 
__weak
 and 
__strong
1 storage
type modifiers:

Model *model = [Model new];

__weak typeof(self) weakSelf = self;
model.dataChanged = ^(NSString *title) {
__strong typeof(self) strongSelf = weakSelf;
strongSelf.label.text = title;
};

self.model = model;


This so called weak/strong dance can be found in most Objective-C codebases. It works fine, but it's error prone. When the new features are introduced and the block's definition gets bigger
someone will eventually use 
self
 within it. We won't notice when
it happens — the compiler helps only in the most simple cases. This is a part when weakify and strongify macros come in handy.


weakify and strongify

Original implementation of @weakify and @strongify macros is complex because they accept more than one
parameter. To make the analysis simpler, we'll introduce our own versions, accepting only one parameter each:

#define weakify(var) __weak typeof(var) AHKWeak_##var = var;

#define strongify(var) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
__strong typeof(var) var = AHKWeak_##var; \
_Pragma("clang diagnostic pop")


With these macros in-place, our example takes the following form:

Model *model = [Model new];

weakify(self);
model.dataChanged = ^(NSString *title) {
strongify(self);
self.label.text = title;
};

self.model = model;


which—with pragmas omitted—translates to:

Model *model = [Model new];

__weak typeof(self) AHKWeak_self = self;
model.dataChanged = ^(NSString *title) {
__strong typeof(self) self = AHKWeak_self;
self.label.text = title;
};

self.model = model;


In the block, 
self
 is
overshadowed by a local variable with the same name. Then, 
self
 can
be used safely inside the block, because it references that local variable, which is held strongly, but lives only until the block ends executing. Even the code not written by us can use 
self
 safely,
e.g. 
NSAssert
 macro
.
It's still possible to reference the real 
self
,
by using an ivar. It leads to a warning, though, so it's a mistake that's easy to spot:

Block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior

Now, you may ask: what if I forget to use 
strongify
?
This is a cool part:
weakify
 creates a new local variable, so if it's
not used at all we get a warning:

Unused variable 'AHKWeak_self'

This warning won't help us, if a strongified self is used in more than one block. This can be worked around by introducing a new scope for each block definition, e.g.:

{
weakify(self);
model.dataChanged = ^(NSString *title) {
strongify(self);
self.label.text = title;
};
}

{
weakify(self);
model.syncFinished = ^(BOOL success) {
strongify(self);
[self update];
};
}


It's not pretty, and I would hesitate before doing things this way, but I think it can be useful in some cases.
As you can imagine, if you forget to use 
weakify
,
but 
strongify
 is in its place, the compiler shows an error:

Use of undeclared identifier 'AHKWeak_self'


Final thoughts

To sum up: we still have to think about a possibility of retaining 
self
 (or
some other object) when creating a new block. That's not something any macro can help us with. However, once weakify and strongify macros are used, any future changes to blocks' definitions are safer than with the standard weak/strong dance.
As for Swift, a similar construct called capture list is embedded
into the language itself.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: