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