]> git.saurik.com Git - apple/objc4.git/blob - test/association.m
objc4-818.2.tar.gz
[apple/objc4.git] / test / association.m
1 // TEST_CONFIG
2
3 #include "test.h"
4 #include <Foundation/NSObject.h>
5 #include <objc/runtime.h>
6 #include <objc/objc-internal.h>
7 #include <Block.h>
8
9 static int values;
10 static int supers;
11 static int subs;
12
13 static const char *key = "key";
14
15
16 @interface Value : NSObject @end
17 @interface Super : NSObject @end
18 @interface Sub : NSObject @end
19
20 @interface Super2 : NSObject @end
21 @interface Sub2 : NSObject @end
22
23 @implementation Super
24 -(id) init
25 {
26 // rdar://8270243 don't lose associations after isa swizzling
27
28 id value = [Value new];
29 objc_setAssociatedObject(self, &key, value, OBJC_ASSOCIATION_RETAIN);
30 RELEASE_VAR(value);
31
32 object_setClass(self, [Sub class]);
33
34 return self;
35 }
36
37 -(void) dealloc
38 {
39 supers++;
40 SUPER_DEALLOC();
41 }
42
43 @end
44
45 @implementation Sub
46 -(void) dealloc
47 {
48 subs++;
49 SUPER_DEALLOC();
50 }
51 @end
52
53 @implementation Super2
54 -(id) init
55 {
56 // rdar://9617109 don't lose associations after isa swizzling
57
58 id value = [Value new];
59 object_setClass(self, [Sub2 class]);
60 objc_setAssociatedObject(self, &key, value, OBJC_ASSOCIATION_RETAIN);
61 RELEASE_VAR(value);
62 object_setClass(self, [Super2 class]);
63
64 return self;
65 }
66
67 -(void) dealloc
68 {
69 supers++;
70 SUPER_DEALLOC();
71 }
72
73 @end
74
75 @implementation Sub2
76 -(void) dealloc
77 {
78 subs++;
79 SUPER_DEALLOC();
80 }
81 @end
82
83 @implementation Value
84 -(void) dealloc {
85 values++;
86 SUPER_DEALLOC();
87 }
88 @end
89
90 @interface Sub59318867: NSObject @end
91 @implementation Sub59318867
92 + (void)initialize {
93 objc_setAssociatedObject(self, &key, self, OBJC_ASSOCIATION_ASSIGN);
94 }
95 @end
96
97 @interface CallOnDealloc: NSObject @end
98 @implementation CallOnDealloc {
99 void (^_block)(void);
100 }
101 - (id)initWithBlock: (void (^)(void))block {
102 _block = (__bridge id)Block_copy((__bridge void *)block);
103 return self;
104 }
105 - (void)dealloc {
106 _block();
107 _Block_release((__bridge void *)_block);
108 SUPER_DEALLOC();
109 }
110 @end
111
112 void TestReleaseLater(void) {
113 int otherObjsCount = 100;
114 char keys1[otherObjsCount];
115 char keys2[otherObjsCount];
116 char laterKey;
117
118 __block int normalDeallocs = 0;
119 __block int laterDeallocs = 0;
120
121 {
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);
127 }
128 {
129 id laterValue = [[CallOnDealloc alloc] initWithBlock: ^{
130 testassertequal(laterDeallocs, 0);
131 testassertequal(normalDeallocs, otherObjsCount * 2);
132 laterDeallocs++;
133 }];
134 objc_setAssociatedObject(target, &laterKey, laterValue, (objc_AssociationPolicy)(OBJC_ASSOCIATION_RETAIN | _OBJC_ASSOCIATION_SYSTEM_OBJECT));
135 RELEASE_VALUE(laterValue);
136 }
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);
141 }
142 RELEASE_VALUE(target);
143 }
144 testassertequal(laterDeallocs, 1);
145 testassertequal(normalDeallocs, otherObjsCount * 2);
146 }
147
148 void TestReleaseLaterRemoveAssociations(void) {
149
150 char normalKey;
151 char laterKey;
152
153 __block int normalDeallocs = 0;
154 __block int laterDeallocs = 0;
155
156 @autoreleasepool {
157 id target = [NSObject new];
158 {
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);
165 }
166 testassertequal(normalDeallocs, 0);
167 testassertequal(laterDeallocs, 0);
168
169 objc_removeAssociatedObjects(target);
170 testassertequal(normalDeallocs, 1);
171 testassertequal(laterDeallocs, 0);
172
173 id normalValue = objc_getAssociatedObject(target, &normalKey);
174 id laterValue = objc_getAssociatedObject(target, &laterKey);
175 testassert(!normalValue);
176 testassert(laterValue);
177
178 RELEASE_VALUE(target);
179 }
180
181 testassertequal(normalDeallocs, 1);
182 testassertequal(laterDeallocs, 1);
183 }
184
185 int main()
186 {
187 testonthread(^{
188 int i;
189 for (i = 0; i < 100; i++) {
190 RELEASE_VALUE([[Super alloc] init]);
191 }
192 });
193 testcollect();
194
195 testassert(supers == 0);
196 testassert(subs > 0);
197 testassert(subs == values);
198
199
200 supers = 0;
201 subs = 0;
202 values = 0;
203
204 testonthread(^{
205 int i;
206 for (i = 0; i < 100; i++) {
207 RELEASE_VALUE([[Super2 alloc] init]);
208 }
209 });
210 testcollect();
211
212 testassert(supers > 0);
213 testassert(subs == 0);
214 testassert(supers == values);
215
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
221
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);
226
227 TestReleaseLater();
228 TestReleaseLaterRemoveAssociations();
229
230 succeed(__FILE__);
231 }