NSArray对ARC下的对象的弱引用(__unsafe_unretained)

Emi*_*ier 68 objective-c ios4 ios5 automatic-ref-counting

我需要在NSArray中存储对象的弱引用,以防止保留周期.我不确定使用正确的语法.这是正确的方法吗?

Foo* foo1 = [[Foo alloc] init];
Foo* foo2 = [[Foo alloc] init];

__unsafe_unretained Foo* weakFoo1 = foo1;
__unsafe_unretained Foo* weakFoo2 = foo2;

NSArray* someArray = [NSArray arrayWithObjects:weakFoo1, weakFoo2, nil];
Run Code Online (Sandbox Code Playgroud)

请注意,我需要支持iOS 4.x,__unsafe_unretained而不是__weak.


编辑(2015-02-18):

对于那些想要使用真__weak指针(不是__unsafe_unretained)的人,请查看这个问题:在ARC下归零弱引用

yuj*_*uji 73

正如杰森所说,你不能使NSArray商店弱参考.实现Emile建议将对象包装在另一个存储弱引用的对象内的最简单方法如下:

NSValue *value = [NSValue valueWithNonretainedObject:myObj];
[array addObject:value];
Run Code Online (Sandbox Code Playgroud)

另一种选择:一个类别,使得NSMutableArray可选地存储弱引用.

请注意,这些是"不安全的未保留"引用,而不是自置零弱引用.如果在释放对象后数组仍然存在,那么你将拥有一堆垃圾指针.

  • 注意:这些不是自我归零的弱引用.他们只是没有得到保留. (15认同)

Tho*_*ann 55

使用NSValue帮助程序或创建集合(数组,集合,字典)对象并禁用其Retain/Release回调的解决方案都不是关于使用ARC的100%故障安全解决方案.

正如对这些建议的各种评论所指出的,这样的对象引用不会像真正的弱引用那样工作:

ARC支持的"适当"弱属性有两种行为:

  1. 对目标对象没有强烈的引用.这意味着如果对象没有指向它的强引用,则该对象将被释放.
  2. 如果ref'd对象被释放,则弱引用将变为零.

现在,虽然上述解决方案符合行为#1,但它们没有展示#2.

要获得行为#2,您必须声明自己的帮助程序类.它只有一个弱的属性来保持你的参考.然后,将此辅助对象添加到集合中.

哦,还有一件事:iOS6和OSX 10.8据称可以提供更好的解决方案:

[NSHashTable weakObjectsHashTable]
[NSPointerArray weakObjectsPointerArray]
[NSPointerArray pointerArrayWithOptions:]
Run Code Online (Sandbox Code Playgroud)

这些应该给你容纳弱引用的容器(但请注意下面的matt的评论).

  • 如果当对象被dealloc'd时它们变为NULL,那么它们适合我的行为#2,然后一切都很好.在ARC之前,弱引用只是一个指针(类型id),一旦引用的对象被释放,它就会悬空.ARC的弱引用在它变为NULL的方式上变得更加智能,使其更安全.所以,如果这是你看到的行为,那么文档中的警告框确实是一个错误.我也发了一个关于这个的反馈,要求他们仔细检查.不幸的是,他们(Apple)没有给你反馈:( (2认同)
  • 顺便说一句,如果你查看`NSHashTable`标题为`+(NSHashTable*)weakObjectsHashTable;`你会发现一个注释:`//当回收弱对象时,不一定要立即清除条目. (2认同)

Ric*_*ges 25

在编写c ++ 20年后,我是Objective-C的新手.

在我看来,Objective-C在松散耦合的消息传递方面非常出色,但在数据管理方面却很糟糕.

想象一下,我发现xcode 4.3支持objective-c ++是多么高兴!

所以现在我将所有.m文件重命名为.mm(编译为objective-c ++)并使用c ++标准容器进行数据管理.

因此,"弱指针数组"问题成为__weak对象指针的std :: vector:

#include <vector>

@interface Thing : NSObject
@end

// declare my vector
std::vector<__weak Thing*> myThings;

// store a weak reference in it
Thing* t = [Thing new];
myThings.push_back(t);

// ... some time later ...

for(auto weak : myThings) {
  Thing* strong = weak; // safely lock the weak pointer
  if (strong) {
    // use the locked pointer
  }
}
Run Code Online (Sandbox Code Playgroud)

这等同于c ++习语:

std::vector< std::weak_ptr<CppThing> > myCppThings;
std::shared_ptr<CppThing> p = std::make_shared<CppThing>();
myCppThings.push_back(p);

// ... some time later ...

for(auto weak : myCppThings) {
  auto strong = weak.lock(); // safety is enforced in c++, you can't dereference a weak_ptr
  if (strong) {
    // use the locked pointer
  }
}
Run Code Online (Sandbox Code Playgroud)

概念证明(根据Tommy对矢量重新分配的担忧):

main.mm:

#include <vector>
#import <Foundation/Foundation.h>

@interface Thing : NSObject
@end

@implementation Thing


@end

extern void foo(Thing*);

int main()
{
    // declare my vector
    std::vector<__weak Thing*> myThings;

    // store a weak reference in it while causing reallocations
    Thing* t = [[Thing alloc]init];
    for (int i = 0 ; i < 100000 ; ++i) {
        myThings.push_back(t);
    }
    // ... some time later ...

    foo(myThings[5000]);

    t = nullptr;

    foo(myThings[5000]);
}

void foo(Thing*p)
{
    NSLog(@"%@", [p className]);
}
Run Code Online (Sandbox Code Playgroud)

示例日志输出:

2016-09-21 18:11:13.150 foo2[42745:5048189] Thing
2016-09-21 18:11:13.152 foo2[42745:5048189] (null)
Run Code Online (Sandbox Code Playgroud)

  • 是的,但这不是一个真正的客观C解决方案.你喜欢它,因为你是一个c ++人. (14认同)
  • 阿兰,我是一个寻求使用最好的工具来工作的人.objective-c没有良好的数据处理能力,本质上是C,有一些消息传递黑客和庞大的支持库.这是一种过时和过时的语言(因此苹果试图用SWIFT取而代之).既然我们有机会使用xcode附带的优秀c ++编译器,为什么不接受呢? (9认同)
  • 这不是Objective-c,因为Objective-C不是C.它由编译器本机支持并具有更好的内存管理功能 (2认同)
  • 我将所有的objective-c 对象包装在轻量级的c++ 包装器中。它使代码安全并完全避免了 Apple 最新的愚蠢行为 - Swift。 (2认同)

Eri*_*ner 13

如果您不需要特定订单,则可以使用NSMapTable特殊键/值选项

NSPointerFunctionsWeakMemory

使用适合ARC或GC的弱读写屏障.使用NSPointerFunctionsWeakMemory对象引用将在上次发布时变为NULL.


Yan*_*der 11

我相信最好的解决方案是使用NSHashTable或NSMapTable.键或/和值可能很弱.你可以在这里阅读更多相关信息:http://nshipster.com/nshashtable-and-nsmaptable/