简体   繁体   中英

Using weak reference to check if object is deallocated, in Objective-C

I'm using ARC.
Sometimes I wrote the following code to assert a object should be deallocated:

__weak weakVariableOrProperty = someObject;
....

someObject = nil;
// or someObject = anotherObject;
....

if (weakVariableOrProperty) {
    @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Object not deallocated" userInfo:nil];
}

For example, I use this code to check if a view controller is deallocated before creating a new view controller.

I believe that weak variable or weak property is set to nil immediately after last strong variable or property was set to nil or another object.

And this code is working as I expected until now.

Using weak variable or property to check if object is deallocated is a technique commonly used?
Is it possible that this code will cause problem in the future?

I believe that weak variable or weak property is set to nil immediately after last strong variable or property was set to nil or another object.

This is not exactly true, because an object could be autoreleased . In this case, the last strong reference may be gone, but the reference count of the instance would remain positive. In cases like that, the __weak reference would not be nil -ed out until the autorelease process takes place.

Is using weak variable or property to check if object is deallocated a technique commonly used?

I seriously doubt that this technique has gained much popularity, because ARC is a relatively new thing to Objective C. However, the technique appears valid.

Is it possible that this code will cause problem in the future?

This is very hard to guess, because the ARC Specification does not make any specific guarantees about the timing of nil -ing out the references, and because the spec allows compilers to optimize sequences of retain and release messages that they send to ARC objects.

Your explanation code will be prone to a race condition.

The object in weakVariableOrProperty could be released (since it's only referenced by a weak reference) after the if condition has been evaluated. To avoid this, introduce an ordinary variable, set it to weakVariableOrProperty and check it for nil instead.

That said, as @dasblinkenlight says, betting on exactly when an object will be gone is tough. In a reference-counted system you don't know what else is holding onto it. It may go away just after you've checked. You should be able to constrain your environment enough that you know the system's not squirreling things away, but both autorelease and weak references complicate things.

The best way to solve this is simply to have well-defined object lifetimes: view controllers that don't live forever, that you explicitly tell to go away and so on.

So I've tried using this technique to ensure that objects are always deallocated on a background thread. The [dealloc] for some of my classes was moderately heavy weight and could take a long time (10s of ms) which would freeze the main thread ever so slightly.

I decided to add all of these heavy objects to an array before they'd be released on the main thread, and then go through that array later on a backgroundd thread to remove them from the array. The thought was that the array would keep the retainCount alive until it could be removed from the array on the background thread, and then i could guarantee the cost of the [dealloc] wouldn't happen on the main thread.

To do this, i had the code below:

while([objectsToDealloc count] && /* other conditions to prevent infinite loop */){
    __weak id ref = [objectsToDealloc lastObject];
    [objectsToDealloc removeLastObject];
    @synchronized(ref){
        // synchronising on ref will retain it if possible.
        // so if its still around,that means we didn't dealloc it
        // like we were asked to.
        // so insert it back into our array. once the object is deallocd
        // it won't be able to be synchronized, because the weak ref will
        // be nil
        if(ref){
            [objectsToDealloc insertObject:ref atIndex:0];
        }
    }
}

The idea was that if the array didn't contain the last reference (or if there were pending autoreleases on the object, etc), then the weak ref wouldn't nil out. I'd then @synchronize on the object - the synchronized block will retain + release whatever object is being synchronized - which would ensure the ref would stay alive during that block. if it was nil, then it'd been dealloced. if it wasn't nil, then i should add it back to the array and check back again later.

After testing with this code over the past few weeks, I cannot recommend this strategy to check for deallocated objects. I haven't tracked down exactly why yet, but very rarely the object will dealloc but the ref won't be nil yet, so i'll be adding an invalid object back into the array.

i've only caught this in the debugger one time, though i have crash logs of it happening a few times. You can see below that "nil" ends up in my array, even though the code above should protect against it.

在NSArray里面没有

Again, I suggest not using this technique for detecting when/if objects deallocate, and instead focus efforts on clarifying your object graph and relationships.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM