preface
Hi Coder, I’m CoderStar!
Today, I’m going to introduce you to a pit I met. The process is probably like this, a reuse page through different entry, and so on return, some normal, but some Crash, log information is as follows.
Cannot form weak reference to instance XXXXXX. It is possible that this object was over-released, or is in the process of deallocation.
Then I looked at the call stack during Crash and found that the Crash was in the process of KVO releasing the Observer during deinit. After a period of investigation, a new pit was discovered.
Specific business code is not posted, posted a Bug trigger Demo (does not include the use of rationality, only used to test Crash).
The problem
protocol MyServiceDelegate: AnyObject {}
class MyService {
weak var delegate: MyServiceDelegate?
func stop(a){}}class MyClass: NSObject.MyServiceDelegate {
private lazy var service: MyService = {
let service = MyService()
service.delegate = self
return service
}()
deinit {
service.stop()
}
}
/ / test
func test(a) {
let myClass = MyClass()}Copy the code
What do you think is going to happen to this code? You may have seen the above introduction in the heart of the expected answer. Yes, it is the same as the above Crash message.
So let’s analyze it. What’s the problem? Self is already deinit and cannot be weakly referenced (service.delegate = self).
There are three conditions for a Crash.
- lazy
- weak
- NSObject
The sample code removes any of these three conditions, and Crash does not occur.
Specific cause: A description in automatic-reference-counting is attached.
ARC’s implementation of zeroing weak references requires close coordination between the Objective-C reference counting system and the zeroing weak reference system. This means that any class which overrides retain and release can’t be the target of a zeroing weak reference. While this is uncommon, some Cocoa classes, like NSWindow, suffer from this limitation. Fortunately, if you hit one of these cases, you will know it immediately, as your program will crash with a message like this:
objc[2478]: cannot form weak reference to instance (0x10360f000) of class NSWindow
If you are interested in looking at the source code, look at the Weak_register_no_lock function in ObjC4-680. It threw a Crash.
To solve
The solution can be simple, but here’s a simple one:
Solution 1
Define a variable that determines whether a service is initialized, and then control whether to continue calling service.stop() based on that variable during deinit. Here it is:
class MyClass: NSObject.MyServiceDelegate {
private var isServiceInit = false
private lazy var service: MyService = {
isServiceInit = true
let service = MyService()
service.delegate = self
return service
}()
deinit {
/// In real business, the service is not initialized and does not need to be processed during 'deinit'
if isServiceInit {
service.stop()
}
}
}
Copy the code
Solution 2
If there are many such attributes, the above method may need to define the same number of control variables, which is relatively tedious, so we will simply encapsulate it.
final public class Lazy<T> {
public init(initializer: @escaping() - >T) {
self.initializer = initializer
}
public private(set) var valueIfInitialized: T?
public var wrapped: T {
if let value = valueIfInitialized {
return value
} else {
let value = initializer()
valueIfInitialized = value
return value
}
}
private let initializer: () -> T
}
Copy the code
Use becomes this.
class MyClass: NSObject.MyServiceDelegate {
private lazy var service = Lazy<MyService> {
let service = MyService()
service.delegate = self
return service
}
/// Use service.wrapped
deinit {
service.valueIfInitialized?.stop()
}
}
Copy the code
It’s still ugly, but it’s better than nothing. I haven’t found a better way to write it yet.
The last
This article is relatively brief, Enjoy now!
Try harder!
Let’s be CoderStar!
It is very important to have a technical circle and a group of like-minded people, come to my technical official account, here only talk about technical dry stuff.
Wechat official account: CoderStar