У меня есть собственный класс / объект, который обрабатывает жесты и выполняет анимацию для данного представления с помощью CADisplayLink. В простейшей форме мой класс выглядит примерно так:

@interface SomeClass : NSObject

@property (strong) UIView *someView;

@end

Когда я добавляю следующий код в свой контроллер представления ...

- (void)viewDidLoad
{
    [super viewDidLoad];

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

... Я ожидал, что мой объект someClass будет сохранен на время жизни контроллера представления, поскольку я использую сильную ссылку на someView.

Однако someClass немедленно освобождается.

Я уже знаю, что могу преодолеть освобождение, просто добавив someClass в качестве свойства (или даже iVar) контроллера представления, однако в идеале я хотел бы избежать этой дополнительной работы ...

Так есть ли способ сохранить мой класс до тех пор, пока не будет освобожден связанный с ним контроллер представления или представления?

ИЗМЕНИТЬ

Объекты UIGestureRecognizer - это пример класса, который не освобождается, когда я связываю их с представлением ...

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

Предположительно это связано с тем, что UIView становится владельцем объекта UIGestureRecognizer. Есть ли способ добиться этого с помощью моего класса и категории UIView? То есть ....

 - (void)viewDidLoad
{
    [super viewDidLoad];

    SomeClass *someClass = [[SomeClass alloc] init];
    [someView addSomeClass:someClass];
}
0
Ollie-Mobile 9 Июн 2014 в 12:17

3 ответа

Лучший ответ

Если вы хотите связать объект с UIView таким же образом, как это делает UIGestureRecognizer, то это технически возможно с использованием связанныхObjects следующим образом (но я не уверен, что я бы поддержал этот подход, поскольку связанные нахмурился) ...

< Сильный > 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

Реализация

- (void)viewDidLoad
{
    [super viewDidLoad];

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

Еще немного о связанных объектах из NSHipster ...

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

0
Oliver Pearmain 10 Июн 2014 в 13:54
Спасибо, что действительно ответили на вопрос.
 – 
Ollie-Mobile
10 Июн 2014 в 14:26

Но вы можете объявить экземпляр SomeClass вместо такого свойства:

@implementation ViewController
{
    SomeClass* _someClass;
}

...


- (void)viewDidLoad
{
    [super viewDidLoad];

    _someClass = [[SomeClass alloc] init];
    _someClass.someView = someView;
}
0
stosha 9 Июн 2014 в 12:39
Извиняюсь, нужно было конкретно указать свойство или iVar (я думал, что это очевидно). Это именно то, чего я хотел бы избежать.
 – 
Ollie-Mobile
10 Июн 2014 в 10:41

Ваш экземпляр SomeClass содержит сильную ссылку на someView, но ничто не содержит ссылки на экземпляр SomeClass, кроме локальной переменной внутри вашего сообщения viewDidLoad, поэтому как только при выходе из метода этот экземпляр может быть освобожден. Поскольку это был объект, содержащий единственную ссылку на ваш UIView, представление также можно освободить.

Единственный вариант - сохранить ссылку на объект SomeClass в переменной экземпляра (или iVar), как предлагается stosha, или в свойстве. Свойства являются предпочтительным методом, и при автоматическом синтезе они не требуют гораздо больших усилий, чем объявление локальной переменной.

Вы можете объявить свойство внутри файла .m, чтобы оно не было видно другим классам, которые ссылаются на ваш класс ViewController -

В вашем файле ViewController.m -

@interface ViewController ()

@property (strong, nonatomic) SomeClass *someClass;

@end

@implementation ViewController

...

 (void)viewDidLoad
{
    [super viewDidLoad];

    self.someClass = [[SomeClass alloc] init];
    self.someClass.someView = someView;
}
0
Paulw11 9 Июн 2014 в 13:12
Спасибо за информативный комментарий, однако это не отвечает на мой вопрос.
 – 
Ollie-Mobile
10 Июн 2014 в 10:41
Хорошо, тогда короткий ответ "нет". Да, вы можете изменить представление для хранения ссылки, но оно кажется излишне сложным по сравнению с простым свойством.
 – 
Paulw11
10 Июн 2014 в 13:53