Search code examples
iosobjective-cdealloc

Avoid Object Being Deallocated Without Adding Property/iVar to UIViewController


I have a custom class/object that handles gestures and conducts animations for a given view using a CADisplayLink. In its simplest form my class looks something like follows:

@interface SomeClass : NSObject

@property (strong) UIView *someView;

@end

When I add the following code to my view controller....

- (void)viewDidLoad
{
    [super viewDidLoad];

    SomeClass *someClass = [[SomeClass alloc] init];
    someClass.someView = someView;
}

... I was anticipating my someClass object would be retained for the life of the view controller, since I am using a strong reference to someView.

However someClass is immediately deallocated.

I am already aware that I can overcome the deallocation simply by adding someClass as a property (or indeed iVar) of the view controller however I would ideally like to avoid this extra work...

so is there anyway I can have my class retained until either the view or view controller its associated with are deallocated?

EDIT

UIGestureRecognizer objects are an exmaple of a class that doesn't get deallocated when I associate them with a view...

- (void)viewDidLoad
{
    UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] init];
    [someView addGestureRecognizer:gestureRecognizer];
}
// tapGestureRecognizer still lives

Presumably this is because the UIView takes owner ship of the UIGestureRecognizer object. Is there anyway to achieve this with my class and a UIView category? I.e....

 - (void)viewDidLoad
{
    [super viewDidLoad];

    SomeClass *someClass = [[SomeClass alloc] init];
    [someView addSomeClass:someClass];
}

Solution

  • If you want to associate the object with a UIView in the same way a UIGestureRecognizer does then this is technically possible using associatedObjects as follows (but I'm not sure I'd advocate this approach since associatedObjects are often frowned upon)...

    SomeClass.h

    @class SomeClass;
    
    
    @interface UIView (SomeClass)
    
    - (void)addSomeClass:(SomeClass *)someClass;
    - (void)removeSomeClass:(SomeClass *)someClass;
    
    @end
    
    
    @interface SomeClass : NSObject
    
    @property (strong) UIView *someView;
    
    @end
    

    SomeClass.m

    #import "SomeClass.h"
    #import <objc/runtime.h>
    
    
    @implementation UIView (AssociatedObject)
    
    - (void)addSomeClass:(SomeClass *)someClass
    {
        NSMutableArray *someClasses = [self someClasses];
        if (someClasses == nil) {
            someClasses = [[NSMutableArray alloc] init];
            [self setSomeClasses:someClasses];
        }
        [someClasses addObject:someClass];
    }
    
    - (void)removeSomeClass:(SomeClass *)someClass
    {
        NSMutableArray *someClasses = [self someClasses];
        if (someClasses != nil) {
            [someClasses removeObject:someClass];
            if (someClasses.count == 0) {
                [self setSomeClasses:nil];
            }
        }
    }
    
    #pragma mark - Private Methods
    
    - (NSMutableArray *)someClasses
    {
        return (NSMutableArray *)objc_getAssociatedObject(self, @selector(someClasses));
    }
    
    - (void)setSomeClasses:(NSMutableArray *)someClasses
    {
        objc_setAssociatedObject(self, @selector(someClasses), someClasses, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    @end
    
    
    @implementation SomeClass
    
    @end
    

    Implementation

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        SomeClass *someClass = [[SomeClass alloc] init];
        someClass.someView = someView;
        [someView addSomeClass:someClass];
    }
    

    Some further reading on associatedObjects from NSHipster...

    http://nshipster.com/associated-objects/