-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathnscopying-nszone-uses-in-swift.html
348 lines (300 loc) · 22.8 KB
/
nscopying-nszone-uses-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
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
<!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="Even in apps written in full Swift, interfacing with Objective-C is still a very big part of iOS development. Many types and semantics from Objective-C are hidden underneath the code we write today, and understanding where they come from can help you make better code decisions and just better understand the platform in general. This time, we'll take a look at what NSCopying and its companion class NSZone are and what they can do for Swift apps.">
<meta name="title" content="NSCopying uses in Swift">
<meta name="url" content="https://swiftrocks.com/nscopying-nszone-uses-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="NSCopying uses in Swift"/>
<meta property="og:image" content="https://swiftrocks.com/images/thumbs/thumb.jpg?4"/>
<meta property="og:description" content="Even in apps written in full Swift, interfacing with Objective-C is still a very big part of iOS development. Many types and semantics from Objective-C are hidden underneath the code we write today, and understanding where they come from can help you make better code decisions and just better understand the platform in general. This time, we'll take a look at what NSCopying and its companion class NSZone are and what they can do for Swift apps."/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="https://swiftrocks.com/nscopying-nszone-uses-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="NSCopying uses in Swift"/>
<meta name="twitter:description" content="Even in apps written in full Swift, interfacing with Objective-C is still a very big part of iOS development. Many types and semantics from Objective-C are hidden underneath the code we write today, and understanding where they come from can help you make better code decisions and just better understand the platform in general. This time, we'll take a look at what NSCopying and its companion class NSZone are and what they can do for Swift apps."/>
<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">
<!-- 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/nscopying-nszone-uses-in-swift"
},
"image": [
"https://swiftrocks.com/images/thumbs/thumb.jpg"
],
"datePublished": "2019-11-05T06: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": "NSCopying uses in Swift",
"abstract": "Even in apps written in full Swift, interfacing with Objective-C is still a very big part of iOS development. Many types and semantics from Objective-C are hidden underneath the code we write today, and understanding where they come from can help you make better code decisions and just better understand the platform in general. This time, we'll take a look at what NSCopying and its companion class NSZone are and what they can do for Swift apps."
}
</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=NSCopying uses in Swift-->
<!--WRITEIT_POST_HTML_NAME=nscopying-nszone-uses-in-swift-->
<!--WRITEIT_POST_SITEMAP_DATE_LAST_MOD=2020-04-12T14:00:00+02:00-->
<!--WRITEIT_POST_SITEMAP_DATE=2019-11-05T06: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=Even in apps written in full Swift, interfacing with Objective-C is still a very big part of iOS development. Many types and semantics from Objective-C are hidden underneath the code we write today, and understanding where they come from can help you make better code decisions and just better understand the platform in general. This time, we'll take a look at what NSCopying and its companion class NSZone are and what they can do for Swift apps.-->
<title>NSCopying uses in Swift</title>
<div class="blog-post">
<div class="post-title-index">
<h1>NSCopying uses in Swift</h1>
</div>
<div class="post-info">
<div class="post-info-text">
Published on 05 Nov 2019
</div>
</div>
<p>Even in apps written in full Swift, interfacing with Objective-C is still a very big part of iOS development. Many types and semantics from Objective-C are hidden underneath the code we write today, and understanding where they come from can help you make better code decisions and just better understand the platform in general. This time, we'll take a look at what <code>NSCopying</code> and its companion class <code>NSZone</code> are and what they can do for Swift apps.</p>
<h2>Context: How copying objects worked in Objective-C</h2>
<div class="sponsor-article-ad-auto hidden"></div>
<p>Objective-C is notable for its weird syntax and for being completely dynamic, but the superset of C was very clever in the way its problems were solved. I mentioned in previous articles that one of my favorite things in Swift is that many compiler features are tied to actual protocols inside the language, and this is the case for Objective-C as well.</p>
<p>In Objective-C, everything was an <b>object.</b> To support the language's dynamic features, objects provided type information to the run time by subclassing <code>NSObject</code>. Subclassing it also provided multiple helper methods like equality methods like <code>isEqual</code>, message routing methods like <code>respondsToSelector</code>, memory management methods like <code>retain/release</code> and type helper methods like <code>isKindOfClass</code>. <code>NSObject</code> is still used to this day in Swift -- many old iOS delegate protocols require the receiver to be an object as they need to check which methods the object can respond to.</p>
<p>An important aspect of the language was that in Objective-C, <b>every object is a reference type</b>. Because the language is insanely dynamic, there's no way to know if the object you're creating in compile time is actually going to look like that in runtime. Everything can be changed in runtime, and because of that, allocating objects on the stack was simply not possible. Everything has to work through shared pointers and dynamic heap allocations, similar to how <code>classes</code> behave in Swift.</p>
<pre><code>NSString blogName;
// Compiler error: Interface type cannot be statically allocated</code></pre>
<pre><code>NSMutableString *blogNameBuilder = [[NSMutableString alloc] initWithString:@"Swift"];
NSString* blogName = blogNameBuilder;
[blogNameBuilder appendString:@"Rocks"];
NSLog(@"%@", blogNameBuilder); // SwiftRocks
NSLog(@"%@", blogName); // SwiftRocks</code></pre>
<p>Because working with references is not always a good idea, languages have means to differentiate between reference semantics and value semantics, and the common way of achieving <b>value semantics</b> in Swift is to use a <code>struct</code>. Because Objective-C doesn't have those for objects, its is very different -- instead of having a clear distinction of reference types versus value types like in Swift, Objective-C allowed reference types to <b>opt-in</b> to value semantics through the <code>copy</code> property accessor:</p>
<pre><code>@property (copy, nonatomic) NSString* blogName;
---
_blogName = @""; // Initialize the ivar
NSMutableString *blogNameBuilder = [[NSMutableString alloc] initWithString:@"Swift"];
self.blogName = blogNameBuilder;
[blogNameBuilder appendString:@"Rocks"];
NSLog(@"%@", blogNameBuilder); // SwiftRocks
NSLog(@"%@", self.blogName); // Swift</code></pre>
<p>With <code>copy</code> as one of the property's accessors, attributing a value to the property's generated setter will instead attribute a copy-on-write copy of the original value instead of the regular pointer. <code>copy</code> in Objective-C works by changing the synthesized setter of the property -- instead of simply assigning the new value to the <i>ivar</i>, the synthesized setter calls a <code>copy()</code> method on the value:</p>
<pre><code>// Created by the runtime:
- (void)setBlogName:(NSString *)blogName {
_blogName = [blogName copy];
}</code></pre>
<p>Calling <code>copy()</code> returns a new instance of the type, completely separated from the original one. But where does this method comes from?</p>
<h2><code>NSCopying</code></h2>
<p>The <code>copy()</code> method comes from <code>NSCopying</code> -- a protocol built to do exactly what it implies: giving objects the ability to generate copies of themselves. Instead of tying the accessor to an internal compiler feature, The <code>copy</code> accessor works through <code>NSCopying</code> so value semantics on properties can also be applied to your own custom types as well. As expected, the <code>copy</code> accessor in Objective-C only works with types that inherit from <code>NSCopying</code>, and its usage in setters is simply a syntax sugar to calling the protocol manually.</p>
<p><code>copy()</code> is the only method defined by <code>NSCopying</code>, and implementing it is simply a matter of returning a new instance of your type. Let's use a <code>Box</code> pattern as an example: In Swift, a common way to bring reference semantics to a value type is to "box" a value inside a <code>class</code>:</p>
<pre><code>class Box<T> {
let element: T
init(element: T) {
self.element = element
}
}</code></pre>
<p>This allows us to create references to an arbitrary <code>T</code> type, but what if for some reason we wanted to make a copy of this entire box, including the underneath value?</p>
<p>One way to achieve this would be to simply create a new instance:</p>
<pre><code>func copyBox() -> Box<T> {
let newElement = element
let newBox = Box(element: newElement)
return newBox
}</code></pre>
<p>This approach, however, was a small issue: Although we are indeed creating a new instance of the box, we don't really know if the underneath <code>element</code> is going to be copied appropriately. If the <code>T</code> type requires doing additional actions to actually copy its contents, the <code>newElement</code> property would still be partially referencing the original <code>element</code> one.</p>
<p>One way to fix this would be to make <code>T</code> inherit from some "copying" protocol, giving us the ability to be sure that we're handling a new copy of the element. Luckily we don't need to, because that's exactly what <code>NSCopying</code> is for!</p>
<pre><code>class Box<T: NSCopying>: NSCopying {
let element: T
init(element: T) {
self.element = element
}
func copy(with zone: NSZone? = nil) -> Any {
guard let newElement = element.copy() as? T else {
fatalError("Couldn't copy element")
}
let newBox = Box(element: newElement)
return newBox
}
}</code></pre>
<p>By calling <code>copy()</code> on our box which internally calls <code>copy()</code> on the element as well, we're now sure that we're handling a reference to a unique copy.</p>
<pre><code>var nsString = NSMutableString(string: "Swift")
let stringBox: Box<NSString> = Box(element: nsString)
let stringBoxCopy = stringBox.copy() as! Box<NSString>
// Getting memory addresses
print(Unmanaged.passUnretained(stringBox).toOpaque()) // 0x0000600003620da0
print(Unmanaged.passUnretained(stringBoxCopy).toOpaque()) // 0x0000600003620e20
nsString.append("Rocks")
// Checking if the inner string was copied as well
print(stringBox.element) // SwiftRocks
print(stringBoxCopy.element) // Swift</code></pre>
<p>(I used <code>NSString</code> in this case because it inherits from <code>NSCopying</code> and is easy to use for tests like this.)</p>
<p><code>NSCopying</code> can be used in Swift as a generic way to create copies of classes (reference types), and as a bonus, making a Swift type inherit from <code>NSCopying</code> will also allow it to make use of the <code>copy</code> property accessor in Objective-C bridges.</p>
<pre><code>@property (copy, nonatomic) MyBridgedNSCopyingSwiftClass* foo;</code></pre>
<h2>What the hell is the <code>NSZone</code> argument?</h2>
<p>You might have noticed that <code>copy()</code> takes a <code>NSZone</code> argument that is ignored -- what is that and why is it ignored?</p>
<p>If you're looking for a short answer, you'll be happy to know that <code>NSZone</code> <b>is deprecated and you can completely ignore it.</b> For a bit of iOS history, keep on reading this section.</p>
<p>A common problem with heap allocation algorithms is <b>memory fragmentation.</b> We need to scan the heap to find a place to allocate an object, but if we're not careful about <b>where</b> we place it, deallocating objects can end up dividing our available memory into very small pieces of memory that are unusable by larger objects.</p>
<p>To visualize this, let's assume that we have a 16 bits block of free memory:</p>
<pre><code>| |</code></pre>
<p>Now, let's allocate a 4 bit object called 1, an 8 bit object called 2, and another 4 bit object called 3 to fill our memory:</p>
<pre><code>|1111222222223333|</code></pre>
<p>Now, let's deallocate objects 1 and 3, but <b>not</b> 2:</p>
<pre><code>| 22222222 |</code></pre>
<p>We now have 8 bits of free memory, but if you actually try to allocate an object that has 8 bits of size at this point, you'll not be able to! Even though we do have this space available, this space is <b>fragmented</b> into two blocks of 4 bits that can't be used by larger objects unless we deallocate or move object 2.</p>
<p>The solution to mitigate this at the time was to create <b>zones</b> of memory. Instead of putting everything into one big memory space, an algorithm that allocated/deallocated a lot of memory could create a separate allocation zone to prevent the rest of the physical memory from being fragmented. In this case, the algorithm that creates the large 8 bit object could create a separate 8 bit zone:</p>
<pre><code>Main: |11113333| Zone: |22222222|</code></pre>
<p>After deallocating objects 1 and 3, we'll now have the 8 bits of memory unfragmented and ready to be used by an object of equal size.</p>
<pre><code>Main: | | Zone: |22222222|</code></pre>
<p>This is exactly what <code>NSZone</code> does. Foundation provided a default object pointing to the "main" zone, but custom ones would be created with custom sizes and granularities.</p>
<pre><code>NSZone* defaultZone = NSDefaultMallocZone();
NSZone* customZone = NSCreateZone(8, 0, YES);</code></pre>
<p>However, as mentioned before, the usage of <code>NSZone</code> was deprecated long ago. The Objective-C runtimes ignores them completely, and Swift doesn't even let you reference them:</p>
<pre><code>let zone = NSDefaultMallocZone()
// 'NSDefaultMallocZone' is unavailable in Swift: Zone-based memory management is unavailable</code></pre>
<p>The reason is that the creation of ARC rendered it unnecessary as its memory management algorithms were smart enough to prevent fragmentation. Also, fragmentation wasn't much of an issue in iOS anyway -- allocating in virtual memory is much more flexible than in physical memory, and iPhones have enough physical memory to make this a rare occurrence.</p>
<h2>NSMutableCopying</h2>
<p>For completion purposes, it's important to also mention that Foundation also has a <code>NSMutableCopying</code> protocol that defines a similar <code>mutableCopy()</code> method. The functionality is the exact same as <code>NSCopying</code> -- the difference comes from the fact that it was common in Objective-C to differentiate between immutable and mutable objects. For example, as strings in Objective-C could be defined both as the immutable <code>NSString</code> and the mutable <code>NSMutableString</code>, the <code>NSMutableCopying</code> allowed developers to explicitly create mutable copies of objects. Since <code>let</code> and <code>var</code> accessors made this pattern unnecessary in Swift, <code>NSMutableCopying</code> doesn't have good uses in Swift unless you explicitly create separate mutable versions of your types.</p>
<h2>Conclusion</h2>
<div class="sponsor-article-ad-auto hidden"></div>
<p>Many features of iOS rely on legacy Objective-C components. As always, knowing the history of why things work the way they do can allow you to make better choices on the platform. In this case, <code>NSCopying</code> is an important aspect of Objective-C and can still be used not only for Swift-specific uses but also to unlock copy semantics in properties bridged to Objective-C.</p>
<p>If you want to see more Swift and iOS information like this, follow me on my Twitter (<a href="https://twitter.com/rockbruno_">@rockbruno_</a>), and let me know of any feedback, 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/[email protected]/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>