【问题标题】:class method where self if used within a block如果在块中使用 self 的类方法
【发布时间】:2012-06-05 05:55:55
【问题描述】:

我有一个使用dispatch_once 创建静态对象的类方法。在dispatch_once 块内,我使用[self class],想知道是否需要使用对self 的弱引用来避免保留循环?

+ (NSArray *)accountNames{
    static NSArray *names = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        names = [[[self class] accounts] allKeys];
        names = [names sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
    });
    return names;
}

如果我使用对 self 的弱引用,我会收到警告:

+ (NSArray *)accountNames{
    static NSArray *names = nil;
    static dispatch_once_t predicate;
    __weak TUAccount *wself = self;
    dispatch_once(&predicate, ^{
        names = [[[wself class] accounts] allKeys];
        names = [names sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
    });
    return names;
}

使用“const Class”类型的表达式初始化“TUAccount *__weak”的不兼容指针类型

因为我收到一个警告,所以我认为在这种情况下我不需要使用对 self 的弱引用,但我想看看你们的想法。

【问题讨论】:

    标签: objective-c cocoa automatic-ref-counting objective-c-blocks weak-references


    【解决方案1】:

    这里没有理由担心保留周期,因为保留或释放类对象是没有意义的——保留和释放根本没有任何作用。

    您尝试进行弱引用是错误的,因为您正在获取一个类对象self 并尝试将其转换为TUAccount实例。两者是完全不同的东西。

    此外,您还可以简化:

    names = [[[self class] accounts] allKeys];
    

    由于 self 已经是一个类,[self class] == self,所以改为:

    names = [[self accounts] allKeys];
    

    【讨论】:

    • 谢谢,我不知道为什么,但是对某些类方法使用self 不起作用,这就是为什么我使用[self class] 来调用所有类方法。
    • 任何类方法——你的,还是苹果框架中的?你所说的“不起作用”是什么意思?可能是有原因的。
    • 好的,这取决于调用方法的位置。以accounts 类方法为例,这是我自己的类方法。我可以从其他类方法中通过[self accounts] 调用它,但必须在实例方法中使用[[self class] accounts]。所以你在上面的示例代码中是对的,它可能只是[self accounts]
    • @keegan3d: 好吧,你应该做出区分,因为它们调用了两种不同的方法:在类对象上调用class 调用+class(因为+class 存在,它会覆盖-class对于类对象),它只返回它被调用的东西。在非类对象上调用class 调用-class,它返回对象的类。您可以在文档中看到它们是两种不同的方法。
    • @keegan3d:不,它们不一样。 [self class] 返回对象的实际类,它可能是继承此方法的TUAccount 的子类。一般来说,您应该使用[self class],因为(1)它是可移植的,不需要您对类进行硬编码;但更重要的是(2)它允许您对子类使用正确的行为,因为子类可能已经覆盖了某些方法以表现不同。它允许您执行 [[self class] alloc] 之类的操作来创建同一类的另一个对象。
    【解决方案2】:

    我又查了一遍iOS SDK Docs,发现下一个:

    Objective-C 对象

    在手动引用计数的环境中,复制块时会保留块内使用的局部变量。在块中使用实例变量将导致对象本身被保留。如果您希望为特定对象变量覆盖此行为,可以使用 __block 存储类型修饰符对其进行标记。

    如果您使用 ARC,对象变量会在复制块并稍后释放时自动保留和释放。

    • 注意:在垃圾收集环境中,如果将 __weak 和 __block 修饰符都应用于变量,则该块将无法确保它保持活动状态。

    如果在方法的实现中使用块,对象实例变量的内存管理规则就更微妙了:

    • 如果通过引用访问实例变量,则保留self;
    • 如果您按值访问实例变量,则会保留该变量。

    以下示例说明了两种不同的情况:

    dispatch_async(queue, ^{
        // instanceVariable is used by reference, self is retained
        doSomethingWithObject(instanceVariable);
    });
    
    
    id localVariable = instanceVariable;
    dispatch_async(queue, ^{
        // localVariable is used by value, localVariable is retained (not self)
        doSomethingWithObject(localVariable);
    });
    

    结论:我假设在块中使用self 没有问题。会被保留,执行后释放。

    此外,您不会将块存储在内存中并直接使用它。所以它被复制到堆中,执行并从中推送。我没有看到任何保留周期。

    希望我是对的!

    【讨论】:

    • id 是一个指针,那么在第二种情况下,localVariable 是否仍然与instanceVariable 引用相同的对象?
    • @Elliot 如果将新值设置为instanceVariable,则localVariable 将不会更新。如果instanceVariable 是可变的并且已更改,则localVariable 将引用相同的值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-11
    • 2015-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-28
    • 2019-12-06
    相关资源
    最近更新 更多