-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy paththe-forbidden-inline-attribute-in-swift.html
More file actions
333 lines (289 loc) · 19.2 KB
/
the-forbidden-inline-attribute-in-swift.html
File metadata and controls
333 lines (289 loc) · 19.2 KB
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
332
333
<!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="The @inline attribute is one of those obscure things in Swift - it's nowhere to be found in Apple's docs, doesn't help you write cleaner code and has no purpose but to help the compiler make optimization decisions, but it's related to a pretty important aspect of your app's performance.">
<meta name="title" content="The Forbidden @inline Attribute in Swift">
<meta name="url" content="https://swiftrocks.com/the-forbidden-inline-attribute-in-swift">
<meta name="image" content="https://swiftrocks.com/images/thumbs/thumb_dark.jpg">
<meta name="copyright" content="Bruno Rocha">
<meta name="robots" content="index,follow">
<meta property="og:title" content="The Forbidden @inline Attribute in Swift"/>
<meta property="og:image" content="https://swiftrocks.com/images/thumbs/thumb_dark.jpg"/>
<meta property="og:description" content="The @inline attribute is one of those obscure things in Swift - it's nowhere to be found in Apple's docs, doesn't help you write cleaner code and has no purpose but to help the compiler make optimization decisions, but it's related to a pretty important aspect of your app's performance."/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="https://swiftrocks.com/the-forbidden-inline-attribute-in-swift"/>
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://swiftrocks.com/images/thumbs/thumb_dark.jpg"/>
<meta name="twitter:image:alt" content="Page Thumbnail"/>
<meta name="twitter:title" content="The Forbidden @inline Attribute in Swift"/>
<meta name="twitter:description" content="The @inline attribute is one of those obscure things in Swift - it's nowhere to be found in Apple's docs, doesn't help you write cleaner code and has no purpose but to help the compiler make optimization decisions, but it's related to a pretty important aspect of your app's performance."/>
<meta name="twitter:site" content="@rockbruno_"/>
<meta name="fediverse:creator" content="@[email protected]">
<!-- Favicon -->
<!-- 16x16 -->
<link rel="shortcut icon" href="images/favicon/favicon.ico" type="image/x-icon">
<link rel="apple-touch-icon" href="images/favicon/favicon_180.png" sizes="180x180">
<link rel="icon" href="images/favicon/favicon_32.png" sizes="32x32" type="image/png">
<link rel="icon" href="images/favicon/favicon_48.png" sizes="48x48" type="image/png">
<link rel="icon" href="images/favicon/favicon_96.png" sizes="96x96" type="image/png">
<link rel="icon" href="images/favicon/favicon_144.png" sizes="144x144" type="image/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/the-forbidden-inline-attribute-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/prism5.css">
<!-- Main CSS Stylesheet -->
<link rel="stylesheet" type="text/css" href="css/style49.css">
<link rel="stylesheet" type="text/css" href="css/sponsor5.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/the-forbidden-inline-attribute-in-swift"
},
"image": [
"https://swiftrocks.com/images/thumbs/thumb_dark.jpg"
],
"datePublished": "2018-10-26T13:42:07+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_dark.jpg"
}
},
"headline": "The Forbidden @inline Attribute in Swift",
"abstract": "The @inline attribute is one of those obscure things in Swift - it's nowhere to be found in Apple's docs, doesn't help you write cleaner code and has no purpose but to help the compiler make optimization decisions, but it's related to a pretty important aspect of your app's performance."
}
</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 class="logo-link" href="https://swiftrocks.com">
<img id="logo" class="logo" alt="SwiftRocks" src="images/bg/logo2dark.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=The Forbidden @inline Attribute in Swift-->
<!--WRITEIT_POST_HTML_NAME=the-forbidden-inline-attribute-in-swift-->
<!--WRITEIT_POST_SITEMAP_DATE_LAST_MOD=2020-04-12T14:00:00+02:00-->
<!--WRITEIT_POST_SITEMAP_DATE=2018-10-26T13:42:07+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 'WRITE_IT_POST'.-->
<!--Writeit provides and injects WRITEIT_POST_NAME and WRITEIT_POST_HTML_NAME by default.-->
<!--WRITEIT_POST_SHORT_DESCRIPTION=The @inline attribute is one of those obscure things in Swift - it's nowhere to be found in Apple's docs, doesn't help you write cleaner code and has no purpose but to help the compiler make optimization decisions, but it's related to a pretty important aspect of your app's performance.-->
<title>The Forbidden @inline Attribute in Swift</title>
<div class="blog-post">
<div class="post-title-index">
<h1>The Forbidden @inline Attribute in Swift</h1>
</div>
<div class="post-info">
<div class="post-info-text">
Published on 26 May 2018
</div>
</div>
<p>The <code>@inline</code> attribute is one of those obscure things in Swift - it's nowhere to be found in Apple's docs, doesn't help you write cleaner code and has no purpose but to help the compiler make optimization decisions, but it's related to a pretty important aspect of your app's performance.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>In programming, <b>function inlining</b> is a compiler optimization technique that removes the overhead of calling certain methods by directly replacing calls to them with the method's contents, basically pretending that the method never existed in first place. This provides a great performance boost.</p>
<p>For example, consider the following code:</p>
<pre class="language-swift"><code class="language-swift">func calculateAndPrintSomething() {
var num = 1
num *= 10
num /= 5
print("My number: \(num)")
}
print("I'm going to do print some number")
calculateAndPrintSomething()
print("Done!")</code></pre>
<p>Assuming that <code>calculateAndPrintSomething()</code> isn't used anywhere else, it's clear that the method doesn't need to exist in the compiled binary - it's purpose is purely to make the code easier to read.</p>
<p>With function inlining, the Swift compiler can remove the overhead of calling an useless method by replacing it with it's contents:</p>
<pre class="language-swift"><code class="language-swift">//The compiled binary version of the above example
print("I'm going to do print some number")
var num = 1
num *= 10
num /= 5
print("My number: \(num)")
print("Done!")</code></pre>
<p>Based on your optimization level, this is done automatically by the Swift compiler - favoring inlining if optimizing for speed (<code>-O</code>), or favoring <b>not</b> inlining if optimizing for binary size (<code>-Osize</code>), since inlining a long method that is called in several places would result in duplicated code, and a larger binary.</p>
<p>Even though the compiler can make its own inlining decisions, the <code>@inline</code> attribute can be used in Swift to <b>force</b> its decision. It can be used in two ways:</p>
<p><code>@inline(__always)</code>: Signals the compiler to always inline the method, if possible.</p>
<p><code>@inline(never)</code>: Signals the compiler to never inline the method.</p>
<p>Now, you might be asking: <i>When the hell is doing this a good idea?</i></p>
<p>According to the Apple engineers, the answer is basically <a href="https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20170227/004886.html">never.</a> Even though the attribute is available for public use and widely used in the Swift source code, it is not officially supported for public use. It was simply never meant to be publicly available, and to quote Jordan Rose: <i>the underscores are there for a reason.</i> Many known and unknown issues can arise if you use it.</p>
<p>But since the attribute can be used publicly, I've decided that for the sake of learning something new I would experiment with it - and I've actually found cases where the attribute can be useful in iOS projects.</p>
<p>The compiler will make its inlining decisions based on your project's optimization settings, but there are cases where you could want a method to go <b>against</b> the optimization setting. In these cases, <code>@inline</code> can be used to achieve the user's goals.</p>
<p>For example, when optimizing for speed, it seems like the compiler will have a tendence to inline even methods that are not short, resulting in increased binary sizes. In this case, <code>@inline(never)</code> can be used to prevent the inlining of a specific widely-used-long method while still focusing on fast binaries.</p>
<p>Another more practical example is that you might want a method to be hidden from possible hackers for containing some sort of sensitive info, regardless if it will make your code slower or bigger. You can try to make the code harder to understand or use some obfuscation tool like <a href="https://github.com/rockbruno/swiftshield">SwiftShield</a>, but <code>@inline(__always)</code> can achieve this without hurting your code. I've detailed this example below.</p>
<h2>Using <code>@inline(__always)</code> to obfuscate Premium content</h2>
<p>Let's pretend we have a music player in our app and some actions are premium-only. The <code>isUserSubscribed(_:)</code> method validates the user subscription and returns a boolean stating if the user is subscribed or not:</p>
<pre class="language-swift"><code class="language-swift">func isUserSubscribed() -> Bool {
//Some very complex validation
return true
}
func play(song: Song) {
if isUserSubscribed() {
//Play the song
} else {
//Ask user to subscribe
}
}</code></pre>
<p>This works great for our code, but look what happens if I disassemble this app and search for the <code>play(_:)</code> method's assembly:</p>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/3kqUFaF.png" alt="">
</div>
<p>If I was a hacker trying to crack this app's subscription, glancing at the <code>play(_:)</code> method was all I had to do to realize that a boolean called <code>isUserSubscribed(_:)</code> is controlling the app's subscription.</p>
<p>I can now unlock the app's entire premium content by merely finding <code>isUserSubscribed(_:)</code> and forcing it to return <code>true</code>:</p>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/JMjdAMS.png" alt="">
</div>
<p>In this case, likely because the method is widely used around the app, the compiler decided to not inline it. This naive decision created a security flaw that allowed the app to be quickly reverse-engineered.</p>
<p>Now look what happens when <code>@inline(__always)</code> is applied to <code>isUserSubscribed(_:)</code>:</p>
<pre class="language-swift"><code class="language-swift">@inline(__always) func isUserSubscribed() -> Bool {
//Some very complex validation
return true
}
func play(song: Song) {
if isUserSubscribed() {
//Play the song
} else {
//Ask user to subscribe
}
}</code></pre>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/JwkToz8.png" alt="">
</div>
<p>The same <code>play(_:)</code> method's assembly now contains no obvious reference to a subscription! The method call got completely replaced by the "complex validation" that happened inside of it, making the assembly look more cryptic and the subscription significantly harder to be cracked.</p>
<p>As a bonus, since every call to <code>isUserSubscribed(_:)</code> got replaced by the validation, there is no single way to unlock the app's entire subscription - a hacker would now have to crack every single method that does the validation. Of course, this also means that our binary got larger as we now have duplicated code everywhere.</p>
<p>Be aware that using <code>@inline(__always)</code> doesn't guarantee that the compiler will actually inline your method. The rules for it are unknown, and there are some cases where inlining is impossible, such as when dynamic dispatching can't be avoided.</p>
<h2>What else?</h2>
<p>Since <code>@inline</code> is not officially supported, you should really never use it in a real project and use this article only for the sake of learning something new.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>However, I found it to be very useful and hope Apple decides to officially support it some day. If you are interested in more obscure Swift things, check out <a href="https://github.com/apple/swift">Swift's Source Code.</a></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>
<h2>References and Good reads</h2>
<a href="https://en.wikipedia.org/wiki/Inline_function">Inline Functions</a>
<br>
<a href="https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20170227/004883.html">[swift-users] @inline Thread</a>
</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://hachyderm.io/@rockbruno"><svg class="svg-logo-link" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M433 179.1c0-97.2-63.7-125.7-63.7-125.7-62.5-28.7-228.6-28.4-290.5 0 0 0-63.7 28.5-63.7 125.7 0 115.7-6.6 259.4 105.6 289.1 40.5 10.7 75.3 13 103.3 11.4 50.8-2.8 79.3-18.1 79.3-18.1l-1.7-36.9s-36.3 11.4-77.1 10.1c-40.4-1.4-83-4.4-89.6-54a102.5 102.5 0 0 1 -.9-13.9c85.6 20.9 158.7 9.1 178.8 6.7 56.1-6.7 105-41.3 111.2-72.9 9.8-49.8 9-121.5 9-121.5zm-75.1 125.2h-46.6v-114.2c0-49.7-64-51.6-64 6.9v62.5h-46.3V197c0-58.5-64-56.6-64-6.9v114.2H90.2c0-122.1-5.2-147.9 18.4-175 25.9-28.9 79.8-30.8 103.8 6.1l11.6 19.5 11.6-19.5c24.1-37.1 78.1-34.8 103.8-6.1 23.7 27.3 18.4 53 18.4 175z"/></svg></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/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script type="text/javascript" src="js/prism5.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>