-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathautoreleasepool-in-swift.html
331 lines (292 loc) · 18.4 KB
/
autoreleasepool-in-swift.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://use.fontawesome.com/afd448ce82.js"></script>
<!-- Meta Tag -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- SEO -->
<meta name="author" content="Bruno Rocha">
<meta name="keywords" content="Software, Engineering, Blog, Posts, iOS, Xcode, Swift, Articles, Tutorials, OBJ-C, Objective-C, Apple">
<meta name="description" content="NSAutoreleasePool was very important during the Obj-C era. Let's see cases where it's still useful in Swift.">
<meta name="title" content="autoreleasepool uses in Swift">
<meta name="url" content="https://swiftrocks.com/autoreleasepool-in-swift">
<meta name="image" content="https://swiftrocks.com/images/thumbs/thumb.jpg?4">
<meta name="copyright" content="Bruno Rocha">
<meta name="robots" content="index,follow">
<meta property="og:title" content="autoreleasepool uses in Swift"/>
<meta property="og:image" content="https://swiftrocks.com/images/thumbs/thumb.jpg?4"/>
<meta property="og:description" content="NSAutoreleasePool was very important during the Obj-C era. Let's see cases where it's still useful in Swift."/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="https://swiftrocks.com/autoreleasepool-in-swift"/>
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://swiftrocks.com/images/thumbs/thumb.jpg?4"/>
<meta name="twitter:image:alt" content="Page Thumbnail"/>
<meta name="twitter:title" content="autoreleasepool uses in Swift"/>
<meta name="twitter:description" content="NSAutoreleasePool was very important during the Obj-C era. Let's see cases where it's still useful in Swift."/>
<meta name="twitter:site" content="@rockbruno_"/>
<!-- Favicon -->
<link rel="icon" type="image/png" href="images/favicon/iconsmall2.png" sizes="32x32" />
<link rel="apple-touch-icon" href="images/favicon/iconsmall2.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap" rel="stylesheet">
<link rel="canonical" href="https://swiftrocks.com/autoreleasepool-in-swift"/>
<!-- Bootstrap CSS Plugins -->
<link rel="stylesheet" type="text/css" href="css/bootstrap.css">
<!-- Prism CSS Stylesheet -->
<link rel="stylesheet" type="text/css" href="css/prism4.css">
<!-- Main CSS Stylesheet -->
<link rel="stylesheet" type="text/css" href="css/style48.css">
<link rel="stylesheet" type="text/css" href="css/sponsor4.css">
<!-- HTML5 shiv and Respond.js support IE8 or Older for HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://swiftrocks.com/autoreleasepool-in-swift"
},
"image": [
"https://swiftrocks.com/images/thumbs/thumb.jpg"
],
"datePublished": "2019-05-22T18:00:00+00:00",
"dateModified": "2020-04-12T14:00:00+02:00",
"author": {
"@type": "Person",
"name": "Bruno Rocha"
},
"publisher": {
"@type": "Organization",
"name": "SwiftRocks",
"logo": {
"@type": "ImageObject",
"url": "https://swiftrocks.com/images/thumbs/thumb.jpg"
}
},
"headline": "autoreleasepool uses in Swift",
"abstract": "NSAutoreleasePool was very important during the Obj-C era. Let's see cases where it's still useful in Swift."
}
</script>
</head>
<body>
<div id="main">
<!-- Blog Header -->
<!-- Blog Post (Right Sidebar) Start -->
<div class="container">
<div class="col-xs-12">
<div class="page-body">
<div class="row">
<div><a href="https://swiftrocks.com">
<img id="logo" class="logo" alt="SwiftRocks" src="images/bg/logo2light.png">
</a>
<div class="menu-large">
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-large">
<div class="menu-item">
<a href="blog">blog</a>
</div>
<div class="menu-item">
<a href="about">about</a>
</div>
<div class="menu-item">
<a href="talks">talks</a>
</div>
<div class="menu-item">
<a href="projects">projects</a>
</div>
<div class="menu-item">
<a href="software-engineering-book-recommendations">book recs</a>
</div>
<div class="menu-item">
<a href="games">game recs</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
</div>
<div class="menu-small">
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-small-1">
<div class="menu-item">
<a href="blog">blog</a>
</div>
<div class="menu-item">
<a href="about">about</a>
</div>
<div class="menu-item">
<a href="talks">talks</a>
</div>
<div class="menu-item">
<a href="projects">projects</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-small-2">
<div class="menu-item">
<a href="software-engineering-book-recommendations">book recs</a>
</div>
<div class="menu-item">
<a href="games">game recs</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
</div>
</div>
<div class="content-page" id="WRITEIT_DYNAMIC_CONTENT">
<!--WRITEIT_POST_NAME=autoreleasepool uses in Swift-->
<!--WRITEIT_POST_HTML_NAME=autoreleasepool-in-swift-->
<!--WRITEIT_POST_SITEMAP_DATE_LAST_MOD=2020-04-12T14:00:00+02:00-->
<!--WRITEIT_POST_SITEMAP_DATE=2019-05-22T18:00:00+00:00-->
<!--Add here the additional properties that you want each page to possess.-->
<!--These properties can be used to change content in the template page or in the page itself as shown here.-->
<!--Properties must start with 'WRITEIT_POST'.-->
<!--Writeit provides and injects WRITEIT_POST_NAME and WRITEIT_POST_HTML_NAME by default.-->
<!--WRITEIT_POST_SHORT_DESCRIPTION=NSAutoreleasePool was very important during the Obj-C era. Let's see cases where it's still useful in Swift.-->
<title>autoreleasepool uses in Swift</title>
<div class="blog-post">
<div class="post-title-index">
<h1>autoreleasepool uses in Swift</h1>
</div>
<div class="post-info">
<div class="post-info-text">
Published on 22 May 2019
</div>
</div>
<p>Foundation's <code>NSAutoreleasePool</code> type, later abstracted to the <code>@autoreleasepool</code> block, is a very old concept in iOS development. During the Obj-C era of iOS, usage of this type was important to prevent your app's memory from blowing up in specific cases. As ARC and Swift came around and evolved, very few people still have to manually play around with memory, making seeing it become a rare occurrence.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>Having developed tools that need to allocate enourmous amounts of memory, I asked myself if there's still a case where <code>@autoreleasepool</code> is useful in Swift. Here are the answers.</p>
<h2>What is <code>@autoreleasepool</code>? (Objective-C)</h2>
<p>In the pre-ARC Obj-C days of manual memory management, <code>retain()</code> and <code>release()</code> had to be used to control the memory flow of an iOS app. As iOS's memory management works based on the retain count of an object, users could use these methods to signal how many times an object is being referenced so it can be safely dealloced if this value ever reaches zero.</p>
<p>However, consider the following situation where we have a <code>getCoolLabel</code> method that someone can use to get a really cool label:</p>
<pre><code>-(NSString *)getCoolLabel {
NSString *label = [[NSString alloc] initWithString:@"SwiftRocks"];
return label;
}</code></pre>
<p><code>NSString alloc</code> automatically calls <code>retain()</code> to make sure <code>label</code> is able to exist (retain count of 1), and the label is then returned to someone else that wants to reference it, retaining it again (retain count 2) until nobody uses it anymore.</p>
<p>But there's a huge problem here. After the stack that called <code>getCoolLabel</code> calls <code>release()</code> to signal that it doesn't need it anymore, the retain count won't be 0, but 1. The internal <code>NSString *label</code> that was created to hold is <b>also</b> retaining it, and it needs to be released as well if we want the NSString itself to dealloc. The thing is, as <code>label</code> is unreachable outside of this method, there's no way we can release it:</p>
<pre><code>-(NSString *)getCoolLabel {
NSString *label = [[NSString alloc] initWithString:@"SwiftRocks"];
[label release]; // Oopsie, nope!
return label;
[label release]; // Oopsie, nope!
}</code></pre>
<p>If <code>release()</code> is called before <code>return</code>, the NSString will dealloc before it can be used which will crash the app, and calling after <code>return</code> means it will never be executed, causing a memory leak.</p>
<p>The solution for this edge case is to use a neat method called <code>autorelease()</code>:</p>
<pre><code>-(NSString *)getCoolLabel {
NSString *label = [[NSString alloc] initWithString:@"SwiftRocks"];
return [label autorelease];
}</code></pre>
<p>Instead of instantly reducing the retain count of an object, <code>autorelease()</code> adds the object to a pool of objects that need to be released <b>sometime in the future, but not now.</b> By default, the pool will release these objects at the end of the run loop of the thread being executed, which is more than enough time to cover all usages of <code>getCoolLabel()</code> without causing memory leaks. Great, right?</p>
<p>Well, kinda. This will indeed solve your problem in 99% of the times, but consider this:</p>
<pre><code>-(void)emojifyAllFiles {
int numberOfFiles = 1000000;
for(i=0;i<numberOfFiles;i++) {
NSString *contents = [self getFileContents:files[i]];
NSString *emojified = [contents emojified];
[self writeContents:contents toFile:files[i]];
}
}</code></pre>
<p>Assuming that <code>getFileContents</code> and <code>emojified</code> return autoreleased instances, the app will be holding <b>two million</b> instances of NSStrings <b>at once</b> even though the individual properties could be safely released after their respective loops!</p>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/ztWib4b.png" alt="">
</div>
<p>Because <code>autorelease</code> defers the release of these objects, they will only be released after the run loop ends -- which is way after the execution of <code>emojifyAllFiles</code>. If the contents of these files are large, this would cause serious issues if not crash the app entirely.</p>
<p>The solution to prevent this is the <code>@autoreleasepool</code> block; when used, every autoreleased property defined inside of it will be released <b>exactly at the end of block</b>:</p>
<pre><code>-(void)emojifyAllFiles {
int numberOfFiles = 1000000;
for(i=0;i<numberOfFiles;i++) {
@autoreleasepool {
NSString *contents = [self getFileContents:files[i]];
NSString *emojified = [contents emojified];
[self writeContents:contents toFile:files[i]];
}
}
}</code></pre>
<p>Instead of waiting until the end of the thread's run loop, the two NSStrings now receive a <code>release</code> message <b>right after</b> <code>writeContents</code> is called, keeping the memory usage stable.</p>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/RoLTwYW.png" alt="">
</div>
<p>In fact, "releasing after the thread's run loop" isn't compiler magic, this is simply because the threads themselves are surrounded by <code>@autoreleasepools</code>! You can see this partially in the main.m of any Obj-C project.</p>
<pre><code>int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}</code></pre>
<p>What about Swift though?</p>
<h2>Is @autoreleasepool needed in ARC-era Swift?</h2>
<p>The answer is <b>it depends.</b></p>
<p>In theory, yes, as the problem shown still exists since <code>autorelease</code> is still a thing, but these problems are harder to come by. The ARC optimizations for Swift evolved a lot in the past few years, and as far as I tested it seems that ARC for Swift is now smart enough to simply never call <code>autorelease</code>, editing your code so that objects call <code>release</code> multiple times instead. The language itself doesn't even appear to have a definition for <code>autorelease</code> outside of the Obj-C bridging -- the one we can use in Swift is in fact the one from Obj-C. This means that for <b>pure Swift objects</b>, <code>@autoreleasepool</code> appears to be useless as nothing is never going to be <code>autoreleased</code>. The following code stays at a stable memory level even though it's looping millions of times.</p>
<pre><code>for _ in 0...9999999 {
let obj = getGiantSwiftClass()
}</code></pre>
<h3>Swift code calling Foundation / Legacy Objective-C code</h3>
<p>However, it's a different story if your code is dealing with legacy Obj-C code, specially old Foundation classes in iOS. Consider the following code that loads a big image tons of times:</p>
<pre><code>func run() {
guard let file = Bundle.main.path(forResource: "bigImage", ofType: "png") else {
return
}
for i in 0..<1000000 {
let url = URL(fileURLWithPath: file)
let imageData = try! Data(contentsOf: url)
}
}</code></pre>
<p>Even though we're in Swift, this will result in the same absurd memory spike shown in the Obj-C example! This is because the <code>Data</code> init is a bridge to the original Obj-C <code>[NSData dataWithContentsOfURL]</code> -- which unfortunately still calls <code>autorelease</code> somewhere inside of it. Just like in Obj-C, you can solve this with the Swift version of <code>@autoreleasepool</code>; <code>autoreleasepool</code> without the @:</p>
<pre><code>autoreleasepool {
let url = URL(fileURLWithPath: file)
let imageData = try! Data(contentsOf: url)
}</code></pre>
<p>The memory usage will now again be at a low, stable level.</p>
<h2>How to know if a legacy UIKit/Foundation init/method returned an autoreleased instance?</h2>
<p>Because these frameworks are closed source at Apple, there's no way to look their source code to see where <code>autorelease</code> is used.</p>
<p>I tried many ways to automate this inside Swift with no success. My attempts included expanding <code>NSData</code> to contain an associated object to a dummy Swift class, but ARC was still smart enough to deinit these objects right after their loops even though the Data object is technically still alive.</p>
<p>I also tried to create a <code>weak</code> reference to the <code>Data</code> instance hoping that it would be still alive after its loop, but it was too set to <code>nil</code>. This makes me think that it's possible that the <code>Data</code> object is indeed deiniting after the loop and what's autoreleasing in that init is something else entirely, but there's way to confirm this without looking the source code; which we unfortunately can't. I believe that there must be an internal property in <code>NSAutoreleasePool</code> or <code>NSThread</code> that you can inspect autoreleased properties, but for now the only way I found really is to manually look the Allocations instrument.</p>
<h2>Conclusion</h2>
<div class="sponsor-article-ad-auto hidden"></div>
<p>To put it short, <code>autoreleasepool</code> is still useful in iOS/Swift development as there are still legacy Obj-C classes in UIKit and Foundation that call <code>autorelease</code>, but you likely don't need to worry about it when dealing with Swift classes due to ARC's optimizations.</p>
<p>Follow me on my Twitter (<a href="https://twitter.com/rockbruno_">@rockbruno_</a>), and let me know of any suggestions and corrections you want to share.</p>
</div></div>
<div class="blog-post footer-main">
<div class="footer-logos">
<a href="https://swiftrocks.com/rss.xml"><i class="fa fa-rss"></i></a>
<a href="https://twitter.com/rockbruno_"><i class="fa fa-twitter"></i></a>
<a href="https://github.com/rockbruno"><i class="fa fa-github"></i></a>
</div>
<div class="footer-text">
© 2025 Bruno Rocha
</div>
<div class="footer-text">
<p><a href="https://swiftrocks.com">Home</a> / <a href="blog">See all posts</a></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Blog Post (Right Sidebar) End -->
</div>
</div>
</div>
<!-- All Javascript Plugins -->
<script type="text/javascript" src="js/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script type="text/javascript" src="js/prism4.js"></script>
<!-- Main Javascript File -->
<script type="text/javascript" src="js/scripts30.js"></script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-H8KZTWSQ1R"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-H8KZTWSQ1R');
</script>
</body>
</html>