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

时间:2014-11-03 23:27:31

标签: objective-c memory memory-management singleton memory-address

previous question的答案中,我学会了如何有效地创建我所描述的缓存单例对象:任何时候只有一个存在,但如果它& #39;不需要,它已被解除分配。

为了测试它,我写了这个单元测试:

- (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()调用放在我的代码中的不同位置,以确保在释放其他两个引用之后实际上分配了一个新对象。我唯一可以猜到的是编译器注意到最近释放了大小合适的内存空间,因此决定使用它。即使我坚持使用代码来尝试在其间分配其他东西,它仍然经常使用相同的内存地址。

有什么方法可以让我不这样做吗?或者,最好是否有更好的方法来确保分配新对象而不是比较内存地址?

2 个答案:

答案 0 :(得分:0)

我使用稍微不同的单例定义进行操作:它不仅一次只是一个对象,而是整个应用程序执行的单个对象。我不希望将其解除分配。

但是我能想到实现你所要做的唯一方法就是让单身人士在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;
}

答案 1 :(得分:0)

好的,我已经提出了一种方法来实现它,而无需通过添加注入属性的类来修改缓存类的源。在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的属性。当然,它仅在测试中可用。使用它,测试变为:

@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

所以我能够通过sentinel属性的存在或不存在来创建一个新对象。