Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions Test/AutoreleasedReturnValue_arc.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <assert.h>
#include "Test.h"

// A getter synthesized for a nonatomic strong property returns its ivar
// without retaining it, and its caller still calls
// objc_retainAutoreleasedReturnValue(). That must retain the value, not
// take over an autorelease of the same object that was made for another
// reason and happens to be on top of the pool.

static int deallocated;

@interface Canary : Test
@end
@implementation Canary
- (void)dealloc
{
deallocated++;
}
@end

@interface Holder : Test
@property (nonatomic, strong) id value;
@end
@implementation Holder
- (id)storeValue: (__autoreleasing id *)out
{
*out = self.value;
return self.value ? nil : self;
}
@end

int main(void)
{
__autoreleasing id out = nil;
@autoreleasepool
{
Holder *holder = [Holder new];
holder.value = [Canary new];
[holder storeValue: &out];
holder = nil;
assert(deallocated == 0);
}
assert(deallocated == 1);
return 0;
}
1 change: 1 addition & 0 deletions Test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ endif ()
if (ENABLE_ALL_OBJC_ARC_TESTS)
list(APPEND TESTS
ARCTest_arc.m
AutoreleasedReturnValue_arc.m
PropertyIntrospectionTest2_arc.m
)
endif()
Expand Down
25 changes: 20 additions & 5 deletions arc.mm
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ - (void)release;
{
struct arc_autorelease_pool *pool;
id returnRetained;
/**
* The pool slot filled by the last objc_autoreleaseReturnValue(), which
* objc_retainAutoreleasedReturnValue() may take back.
*/
id *returnSlot;
};

/**
Expand Down Expand Up @@ -712,17 +717,24 @@ static inline id autorelease(id obj)

extern "C" OBJC_PUBLIC id objc_autoreleaseReturnValue(id obj)
{
struct arc_tls* tls = getARCThreadData();
if (!useARCAutoreleasePool)
{
struct arc_tls* tls = getARCThreadData();
if (NULL != tls)
{
objc_autorelease(tls->returnRetained);
tls->returnRetained = obj;
return obj;
}
}
return objc_autorelease(obj);
obj = objc_autorelease(obj);
if ((NULL != tls) && useARCAutoreleasePool && (NULL != obj) &&
(NULL != tls->pool) && (tls->pool->insert > tls->pool->pool) &&
(*(tls->pool->insert-1) == obj))
{
tls->returnSlot = tls->pool->insert-1;
}
return obj;
}

extern "C" OBJC_PUBLIC id objc_retainAutoreleasedReturnValue(id obj)
Expand All @@ -738,11 +750,14 @@ static inline id autorelease(id obj)
struct arc_tls* tls = getARCThreadData();
if (NULL != tls)
{
// If we're using our own autorelease pool, just pop the object from the top
// If we're using our own autorelease pool, pop the object from the top,
// but only if objc_autoreleaseReturnValue() put it there.
if (useARCAutoreleasePool)
{
if ((NULL != tls->pool) &&
(*(tls->pool->insert-1) == obj))
id *slot = tls->returnSlot;
tls->returnSlot = NULL;
if ((NULL != tls->pool) && (NULL != slot) &&
(slot == tls->pool->insert-1) && (*slot == obj))
{
tls->pool->insert--;
return obj;
Expand Down