如何在不比较内存地址的情况下区分Objective-C对象?

如何在不比较内存地址的情况下区分Objective-C对象?,objective-c,memory,memory-management,singleton,memory-address,Objective C,Memory,Memory Management,Singleton,Memory Address,在对a的回答中,我学习了如何有效地创建一个缓存的单例对象:在任何时候只有一个存在,但是如果不需要,它就会被释放 为了测试它,我编写了以下单元测试: - (void)testThingInstance { MyThing *thing1 = [MyThing new]; MyThing *thing2 = [MyThing new]; XCTAssertEqual(thing1, thing2, @"Should have single thing"); // L

在对a的回答中,我学习了如何有效地创建一个缓存的单例对象:在任何时候只有一个存在,但是如果不需要,它就会被释放

为了测试它,我编写了以下单元测试:

- (void)testThingInstance {
    MyThing *thing1 = [MyThing new];
    MyThing *thing2 = [MyThing new];
    XCTAssertEqual(thing1, thing2, @"Should have single thing");

    // Let's release the thing, but keep its address.
    uintptr_t pointer_as_integer = (uintptr_t)thing1;
    thing1 = thing2 = nil;

    // Now get a new thing. It should be a brand new object.
    thing1 = [MyThing new];
    XCTAssertNotEqual(
        (uintptr_t)thing1, pointer_as_integer,
        @"Should have a new thing, not a permanent singleton"
    );
}
问题是,最后一个断言有一半的时间是失败的。我将NSLog调用放在代码中的不同位置,以确保在其他两个引用被释放后,实际上分配了一个新对象。我唯一能猜到的是,编译器注意到最近释放了一个大小刚好合适的内存空间,因此决定利用它。即使我在代码中尝试在两者之间分配其他内容,它仍然经常使用相同的内存地址


我有没有办法不那样做?或者,最好是,除了比较内存地址之外,还有更好的方法来确保分配新对象吗?

我使用的是稍微不同的单例定义:它不是一次一个对象,而是整个应用程序执行过程中的一个对象。我从没想过要把它卖掉

但我能想到的唯一方法是让单身者在dealloc上假装自己的死亡,然后在下一次分配中纠正错误

static MyThing *_zombieInstance;

- (void)dealloc {
     _zombieInstance = self;
}
。。。然后,在创建一个新的时:

+ (MyThing *)newMyThing {
    MyThing *thing = [MyThing new];  // assuming you implement single-ness somehow here
    _zombieInstance = nil;
    return thing;
}

好的,我想出了一种方法,通过添加一个注入属性的类别,不修改缓存类的源代码。在MyThingTests.m中,我将此添加到顶部:

#import <objc/runtime.h>

@interface MyThing (ioThingTest)
@property (nonatomic, strong) NSObject *sentinel;
@end

@implementation MyThing (ioThingTest)

- (NSObject *)sentinel {
    return objc_getAssociatedObject(self, @selector(sentinel));
}

- (void)setSentinel:(NSObject *)value {
    objc_setAssociatedObject(
        self, @selector(sentinel), value,
        OBJC_ASSOCIATION_RETAIN_NONATOMIC
    );
}

@end

因此,我可以判断新对象是由sentinel属性的存在或不存在创建的。

如果不了解您是如何实现神话的,没有人可以帮助您解决问题。@rmaddy请参阅参考资料,特别是关于神话的实现。好的,为什么您关心下一个实例是否使用相同的地址?没关系。您确定在将thing1和thing2设置为nil并再次重新分配thing1之间的问题代码中调用了dealloc吗?@rmaddy以便我可以测试它。是的。我只是追查了一些地方,在那里它被保留在我的测试中,需要发布。但它不是一个有效的测试。如果解除分配了一个对象,然后创建了一个新对象,那么为新实例提供与先前解除分配的实例相同的内存地址是完全有效的。这仍然是一个新的实例。是的,我的代码中有一个永久的单例在另一个类中。是的,我可以加入一些只调试的代码来捕捉旧的单例,但如果我能找到其他方法来区分对象,我真的不愿意。我明白了,但如果你想一想你说的话:区分对象彼此。。。您考虑的两个对象不是两个对象。它们只是一个对象,在任何意义上都与自身相同——相同的指针指向包含相同位的相同内存块。因为它们是同一事物,所以不能从定义上加以区分。我明白这有多不好,但我很确定某种形式的保留旧实例是唯一的解决方案。是的,这是有道理的。我认为我能做的最好的事情就是我在下面的答案中所做的:只在测试中注入一个属性,并检查它是否存在。工作良好,可靠地测试了我需要测试的内容。
@implementation MyThingTests

- (void)testThingInstance {
    MyThing *thing1 = [MyThing new];
    thing1.sentinel = NSNull.null;
    MyThing *thing2 = [MyThing new];
    XCTAssertEqual(thing2.sentinel, thing1.sentinel, @"Should have single thing");

    // Let's force a release.
    thing1 = thing2 = nil;

    // Now get a new thing. It should be a brand new object with no sentinel.
    thing1 = [MyThing new];
    XCTAssertNil(thing1.sentinel, @"Should have a new thing with no sentenel");
}

@end