Weak Strong Dance and swiftweak in Swift
Dear bloggers who are interested in blog posts, I am sorry to tell you a very unfortunate message,
This article will be the last article published by the blogger in the blog Park,
Because the post will only be published here/
The layout of the new blog is better, and the reading experience is better. You are welcome to vomit, leave a message, and subscribe.
I'm about to celebrate the new year again. You can no longer be as shameless as you did at the beginning (in our Guangdong dialect, we mean red packets)
Figure 1
Figure 2
It seems that this year has no benefits.
Who made brother already work.
This year, the company's development tasks have been completed, and Apple has never been held responsible for Christmas, so we should be idle in the last half month.
The bloggers refer to Swift, which has been raised to 2.1 and is also open-source. In this case, Swift is also fat enough, and the syntax will never be changed, it's time to start again.
Why? In fact, in the First Swift Beta version, our project manager tried palyground and it was awesome. "Let's use Swift to develop the next project ", then there is Swift1.0... 2.0. Every update, every syntax change, and every time XCode was opened, it was filled with red. The shocking scenes made the bloggers suffer countless times. Fortunately, this project is dead, oh yeah!
Well, let's pick up Swift again. Today's article is a masterpiece of passionate translation, and my English level has been fully affirmed by the original author:
Figure 3
See it. There is no obstacle to communication !! We had a pleasant chat for a long time ~~ , Passionate, biu ~
Let's go back to the blog topic. This time we will talk about "Weak/Strong Dance"
Solve the 2011 WWDC Session to be pursued by loop reference in block #322 the amazing code at that time was as follows:
- (void)dealloc{ [[NSNotificationCenter defaultCenter] removeObserver:_observer];}- (void)loadView{ [super loadView]; __weak TestViewController *wself = self; _observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"testKey" object:nil queue:nil usingBlock:^(NSNotification *note) { TestViewController *sself = wself; [sself dismissModalViewControllerAnimated:YES]; }];}
You can also see that AFNetWorking uses block as follows:
__weak __typeof(self)weakSelf = self;AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {__strong __typeof(weakSelf)strongSelf = weakSelf;strongSelf.networkReachabilityStatus = status;if (strongSelf.networkReachabilityStatusBlock) {strongSelf.networkReachabilityStatusBlock(status);}};
We are all familiar with "weak/strong dance" in Objective-C, But I suddenly want to know what to do in Swift? Is there a legendary best practice?
Now, start translating!
Original
First, we provide an example of circular references caused by references that do not use weak in the closure.
class C { let name: String var block: (() -> Void)? init(name: String) { self.name = name block = { self.doSomething() } } deinit { print("Destroying \(name)") } func doSomething() { print("Doing something for \(name)") }}var c = C(name: "one")print(c.name)c = C(name: "two")print(c.name)
Output
onetwo
This is an example of a giant base and obvious circular reference. self-> block-> self
So,deinit
The method is never executed, even if youc
Point againnil
Or other instances,c
It will not be destroyed. This is a stubborn and naughty loop reference, especially when youc
Pointnil
After that, you can no longer reference this object, and it will quietly lie in the heap memory, leaving the world independent, and then you will leak the heap memory, then you may feel a little sad from the lower body ~ ~ No.
In fact, the parameter List (Capture List) of the closure in Swift can be used to obtainweak self
To avoid circular references, but this does not meet our requirements, onlyweak
Is not a "weak/strong dance" drop.
Use closure parameter list
Class C {let name: String var block: ()-> Void )? Init (name: String) {self. name = name block = {[weak self] in // <-- Here are some changes to self ?. DoSomething () }}deinit {print ("Destroying \ (name)")} func doSomething () {print ("Doing something for \ (name )")}} var c = C (name: "one") print (c. name) c = C (name: "two") print (c. name)
Output
oneDestroying onetwo
So there is no circular reference ~
Use in closures
self
Use[weak self]
There is a detail, that isself
In the closureOptional
From the code aboveself?.doSomething()
We can see that.
But if you use it in this closureself?
(Multiple Useself?
), The problem arises, becauseself?
Is a weak reference, so you cannot determine allself?
All operations can be completed. After all, if the referencedself
You may get down at any time, and then raise an angry question:
Figure 4
class C { deinit { println("Destroying C") } func log(msg: String) { println(msg) } func doClosure() { dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in self?.log("before sleep") usleep(500) self?.log("after sleep") } }}var c: C? = C() // Optional, so we can set it to nilc?.doClosure()dispatch_async(dispatch_get_global_queue(0, 0)) { usleep(100) c = nil // This will dealloc c}dispatch_main()
Output
before sleepDestroying C
Note: of course, in generaldispatch_async()
You do not have to worry about circular references, because self does not holddispatch_async()
So the above Code does not really lead to circular references. If your closure does not pay much attention to the results, thenself
Isnil
The closure won't be executed any more. This is quite useful.
The above code is not printedafter sleep
Becauseself?
This sentence has been suspended before being printed.
This kind of rootless bug can usually make you half-dead. So we usually encounter multiple trials in this closure.self?
Usuallyself?
Become thick and strongstrong self
, (The blogger is also thick and strong, covering his face ~~) This is the legendary "weak/strong dance". Why is this dance called dance?The US team lifted the ban
Not bad. The U. S. Team Leader in the women's federation is also ledweak
Changestrong
Ah ~, Well, it's too far away, and the chrysanthemums all hurt. We are doing technical translation! Be serious! Respect the original author! Let's call it dance. With this dance, we can ensure that once the closure is executed,self
Notnil
.
However, as mentioned at the beginning of the articleSwift
I am not very sure about the best practices for changing back to strong in weak/strong dance...
Obtain strong references. Use optional binding.
if let
Func doClosure () {dispatch_async (dispatch_get_global_queue (0, 0) {[weak self] in if let strongSelf = self {// <-- here is the essence of strongSelf. log ("before sleep") usleep (500) strongSelf. log ("after sleep") }}// or in Swift 2, using 'Guard Let': dispatch_async (dispatch_get_global_queue (0, 0 )) {[weak self] in guard let strongSelf = self else {return} // <-- this is the essence of strongSelf. log ("before sleep") usleep (500) strongSelf. log ("after sleep ")}
Output
before sleepafter sleepDestroying C
Advantages:
- It is obvious that the entire operation process
- Non-optional local variables are obtained in the closure.
Disadvantages:
- Unfortunately, we cannot
if let self = self
Becauseself
Is a constant, not variable, so we can onlyif let strongSelf = self
Use ugly in the scope of the ClosurestrongSelf
.
- In the closure of swift, if you haven't tried it
strongSelf
Insteadself
In this way, the compiler will warn! Because at this timeself
It is optional. Compared with OC, no warning will be given. (I read this sentence 21 times. Why do I think this is not a disadvantage)
Use
withExtendedLifetime
There is a function in the Swift Standard Library:withExtendedLifetime()
It feels like the goldfish like Apple has intentionally induced us to use this function to implement "weak/strong dance ".
/// Evaluate `f()` and return its result, ensuring that `x` is not/// destroyed before f returns.func withExtendedLifetime<T, Result>(x: T, @noescape _ f: () -> Result) -> Result
Try it.
func doClosure() { dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in withExtendedLifetime(self) { self!.log("before sleep") usleep(500) self!.log("after sleep") } }}
Advantages:
- No longer need to use ugly
strongSelf
Now
Disadvantages:
self
Or is it his mother's choice? Is it necessary to call a method or something !?, The blogger suddenly remembered one of his skills: one-handed solution.
Customize
withExtendedLifetime()
This method is thought by @ jtbandes, which is probably like this:
extension Optional { func withExtendedLifetime(body: Wrapped -> Void) { if let strongSelf = self { body(strongSelf) } }}// Then:func doClosure() { dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] () -> Void in self.withExtendedLifetime { $0.log("before sleep") usleep(500) $0.log("after sleep") } return }}
Advantages:
- Follows naming conventions set by the standard library. (original) feel no advantages ~
Disadvantages:
strongSelf
Changed to use$0
The blogger thinks, oh, it's still ugly, and the readability is worse.
- In this case, I had to add some extra type info to
dispatch_async()
Closure. I'm not totally sure why. I don't know what he said ~
Translation is now complete
Postscript
For the Weak section in Swift "Weak/Strong Dance", you can refer to Meida's article memory management,WeakAndUnowned.
I have been using Swift for more than a week. Although Xcode is writing Swift just like a plain text editor, I still want to say: Swift is true.™Safe! It's hard to think about crash.
And Happy Christmas!
Close the pen and leave.