4 #include <Foundation/NSObject.h>
5 #include <objc/runtime.h>
6 #include <objc/objc-internal.h>
13 static const char *key = "key";
16 @interface Value : NSObject @end
17 @interface Super : NSObject @end
18 @interface Sub : NSObject @end
20 @interface Super2 : NSObject @end
21 @interface Sub2 : NSObject @end
26 // rdar://8270243 don't lose associations after isa swizzling
28 id value = [Value new];
29 objc_setAssociatedObject(self, &key, value, OBJC_ASSOCIATION_RETAIN);
32 object_setClass(self, [Sub class]);
53 @implementation Super2
56 // rdar://9617109 don't lose associations after isa swizzling
58 id value = [Value new];
59 object_setClass(self, [Sub2 class]);
60 objc_setAssociatedObject(self, &key, value, OBJC_ASSOCIATION_RETAIN);
62 object_setClass(self, [Super2 class]);
90 @interface Sub59318867: NSObject @end
91 @implementation Sub59318867
93 objc_setAssociatedObject(self, &key, self, OBJC_ASSOCIATION_ASSIGN);
97 @interface CallOnDealloc: NSObject @end
98 @implementation CallOnDealloc {
101 - (id)initWithBlock: (void (^)(void))block {
102 _block = (__bridge id)Block_copy((__bridge void *)block);
107 _Block_release((__bridge void *)_block);
112 void TestReleaseLater(void) {
113 int otherObjsCount = 100;
114 char keys1[otherObjsCount];
115 char keys2[otherObjsCount];
118 __block int normalDeallocs = 0;
119 __block int laterDeallocs = 0;
122 id target = [NSObject new];
123 for (int i = 0; i < otherObjsCount; i++) {
124 id value = [[CallOnDealloc alloc] initWithBlock: ^{ normalDeallocs++; }];
125 objc_setAssociatedObject(target, keys1 + i, value, OBJC_ASSOCIATION_RETAIN);
126 RELEASE_VALUE(value);
129 id laterValue = [[CallOnDealloc alloc] initWithBlock: ^{
130 testassertequal(laterDeallocs, 0);
131 testassertequal(normalDeallocs, otherObjsCount * 2);
134 objc_setAssociatedObject(target, &laterKey, laterValue, (objc_AssociationPolicy)(OBJC_ASSOCIATION_RETAIN | _OBJC_ASSOCIATION_SYSTEM_OBJECT));
135 RELEASE_VALUE(laterValue);
137 for (int i = 0; i < otherObjsCount; i++) {
138 id value = [[CallOnDealloc alloc] initWithBlock: ^{ normalDeallocs++; }];
139 objc_setAssociatedObject(target, keys2 + i, value, OBJC_ASSOCIATION_RETAIN);
140 RELEASE_VALUE(value);
142 RELEASE_VALUE(target);
144 testassertequal(laterDeallocs, 1);
145 testassertequal(normalDeallocs, otherObjsCount * 2);
148 void TestReleaseLaterRemoveAssociations(void) {
153 __block int normalDeallocs = 0;
154 __block int laterDeallocs = 0;
157 id target = [NSObject new];
159 id normalValue = [[CallOnDealloc alloc] initWithBlock: ^{ normalDeallocs++; }];
160 id laterValue = [[CallOnDealloc alloc] initWithBlock: ^{ laterDeallocs++; }];
161 objc_setAssociatedObject(target, &normalKey, normalValue, OBJC_ASSOCIATION_RETAIN);
162 objc_setAssociatedObject(target, &laterKey, laterValue, (objc_AssociationPolicy)(OBJC_ASSOCIATION_RETAIN | _OBJC_ASSOCIATION_SYSTEM_OBJECT));
163 RELEASE_VALUE(normalValue);
164 RELEASE_VALUE(laterValue);
166 testassertequal(normalDeallocs, 0);
167 testassertequal(laterDeallocs, 0);
169 objc_removeAssociatedObjects(target);
170 testassertequal(normalDeallocs, 1);
171 testassertequal(laterDeallocs, 0);
173 id normalValue = objc_getAssociatedObject(target, &normalKey);
174 id laterValue = objc_getAssociatedObject(target, &laterKey);
175 testassert(!normalValue);
176 testassert(laterValue);
178 RELEASE_VALUE(target);
181 testassertequal(normalDeallocs, 1);
182 testassertequal(laterDeallocs, 1);
189 for (i = 0; i < 100; i++) {
190 RELEASE_VALUE([[Super alloc] init]);
195 testassert(supers == 0);
196 testassert(subs > 0);
197 testassert(subs == values);
206 for (i = 0; i < 100; i++) {
207 RELEASE_VALUE([[Super2 alloc] init]);
212 testassert(supers > 0);
213 testassert(subs == 0);
214 testassert(supers == values);
216 // rdar://44094390 tolerate nil object and nil value
217 #pragma clang diagnostic push
218 #pragma clang diagnostic ignored "-Wnonnull"
219 objc_setAssociatedObject(nil, &key, nil, OBJC_ASSOCIATION_ASSIGN);
220 #pragma clang diagnostic pop
222 // rdar://problem/59318867 Make sure we don't reenter the association lock
223 // when setting an associated object on an uninitialized class.
224 Class Sub59318867Local = objc_getClass("Sub59318867");
225 objc_setAssociatedObject(Sub59318867Local, &key, Sub59318867Local, OBJC_ASSOCIATION_ASSIGN);
228 TestReleaseLaterRemoveAssociations();