【问题标题】:Avoid Object Being Deallocated Without Adding Property/iVar to UIViewController避免在不向 UIViewController 添加 Property/iVar 的情况下释放对象
【发布时间】:2014-06-09 08:17:45
【问题描述】:

我有一个自定义类/对象,它使用 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];
}

【问题讨论】:

    标签: ios objective-c dealloc


    【解决方案1】:

    如果您想以与 UIGestureRecognizer 相同的方式将对象与 UIView 关联,那么 技术上 可以使用如下的关联对象(但我不确定我是否会提倡这种方法,因为associatedObjects 经常不受欢迎)...

    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 中的 associatedObjects...

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

    【讨论】:

    • 感谢您实际回答问题。
    【解决方案2】:

    但是你可以声明 SomeClass 实例而不是这样的属性:

    @implementation ViewController
    {
        SomeClass* _someClass;
    }
    
    ...
    
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        _someClass = [[SomeClass alloc] init];
        _someClass.someView = someView;
    }
    

    【讨论】:

    • 抱歉,应该特别说明属性或 iVar(我认为这很明显)。这正是我想要避免的。
    【解决方案3】:

    您的SomeClass 实例持有对someView 的强引用,但除了viewDidLoad 消息中的局部变量外,没有任何东西持有对SomeClass 实例的引用,所以只要方法退出,该实例可以被释放。因为那是持有对您的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;
    }
    

    【讨论】:

    • 感谢您提供信息丰富的评论,但这并不能回答我的问题。
    • 好的,那么简短的回答是“不”。是的,您可以修改视图以保存引用,但与简单属性相比似乎不必要地复杂
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-23
    相关资源
    最近更新 更多