Skip to content

Commit a99c574

Browse files
committed
wrote about conditional downcasts in swift
1 parent b2deefe commit a99c574

File tree

4 files changed

+367
-183
lines changed

4 files changed

+367
-183
lines changed

issue08/README.md

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,10 +360,204 @@ this is what the code is doing:
360360

361361
the rest of this code is not magic. so really the important thing that you would want to take away with you is that typecasting in swift is done by an actual function call to an internal and private function called `imp___stubs__swift_dynamicCast`.
362362

363+
Unconditional Downcasting
364+
===
365+
Let's have a look at unconditional downcasting in swift. as i mentioned before, you can learn about downcasting on [apple's website](http://goo.gl/C15J0l) so i won't teach it here.
366+
imagine you have the following two classes:
367+
368+
```swift
369+
class Vehicle{
370+
func id() -> Int{
371+
return 0xabcdefa
372+
}
373+
}
374+
375+
class Car : Vehicle{
376+
override func id() -> Int {
377+
return 0xabcdefc
378+
}
379+
}
380+
```
381+
382+
and then we do this:
383+
384+
```swift
385+
func example2(){
386+
let v: Vehicle = Car()
387+
let c = [v][0] as Car
388+
println(c)
389+
}
390+
```
391+
so the variable `c` is unconditionally downcasted from the first item in the array that only has `v` as its member. this array is `[Vehicle]` so if we want to get a `Car` object out of it, we have to downcast it, which is what we are doing. So let's see how Swift will compile this code:
392+
393+
```asm
394+
0000000100001740 push rbp ; XREF=__TFC12swift_weekly14ViewController11viewDidLoadfS0_FT_T_+111, __TToFC12swift_weekly14ViewController11viewDidLoadfS0_FT_T_+119
395+
0000000100001741 mov rbp, rsp
396+
0000000100001744 push r15
397+
0000000100001746 push r14
398+
0000000100001748 push r13
399+
000000010000174a push r12
400+
000000010000174c push rbx
401+
000000010000174d sub rsp, 0x28
402+
0000000100001751 mov qword [ss:rbp+var_48], rdi
403+
0000000100001755 mov r12, qword [ds:__TMLCC12swift_weekly14ViewController3Car] ; __TMLCC12swift_weekly14ViewController3Car
404+
000000010000175c test r12, r12
405+
000000010000175f jne 0x100001777
406+
407+
0000000100001761 lea rdi, qword [ds:objc_class__TtCC12swift_weekly14ViewController3Car] ; objc_class__TtCC12swift_weekly14ViewController3Car
408+
0000000100001768 call imp___stubs__swift_getInitializedObjCClass
409+
000000010000176d mov r12, rax
410+
0000000100001770 mov qword [ds:__TMLCC12swift_weekly14ViewController3Car], r12 ; __TMLCC12swift_weekly14ViewController3Car
411+
412+
0000000100001777 mov esi, 0x10 ; XREF=__TFC12swift_weekly14ViewController8example2fS0_FT_T_+31
413+
000000010000177c mov edx, 0x7
414+
0000000100001781 mov rdi, r12
415+
0000000100001784 call imp___stubs__swift_allocObject
416+
0000000100001789 mov r14, rax
417+
000000010000178c lea rdi, qword [ds:0x10001e210] ; 0x10001e210 (_metadata + 0x10)
418+
0000000100001793 mov esi, 0x20
419+
0000000100001798 mov edx, 0x7
420+
000000010000179d call imp___stubs__swift_allocObject
421+
00000001000017a2 mov r13, rax
422+
00000001000017a5 mov qword [ds:r13+0x10], 0x1
423+
00000001000017ad mov qword [ds:r13+0x18], r14
424+
00000001000017b1 mov rbx, qword [ds:__TMLGCSs23_ContiguousArrayStorageCC12swift_weekly14ViewController7Vehicle_] ; __TMLGCSs23_ContiguousArrayStorageCC12swift_weekly14ViewController7Vehicle_
425+
00000001000017b8 test rbx, rbx
426+
00000001000017bb jne 0x1000017f5
427+
428+
00000001000017bd mov rsi, qword [ds:__TMLCC12swift_weekly14ViewController7Vehicle] ; __TMLCC12swift_weekly14ViewController7Vehicle
429+
00000001000017c4 test rsi, rsi
430+
00000001000017c7 jne 0x1000017df
431+
432+
00000001000017c9 lea rdi, qword [ds:objc_class__TtCC12swift_weekly14ViewController7Vehicle] ; objc_class__TtCC12swift_weekly14ViewController7Vehicle
433+
00000001000017d0 call imp___stubs__swift_getInitializedObjCClass
434+
00000001000017d5 mov rsi, rax
435+
00000001000017d8 mov qword [ds:__TMLCC12swift_weekly14ViewController7Vehicle], rsi ; __TMLCC12swift_weekly14ViewController7Vehicle
436+
437+
00000001000017df mov rdi, qword [ds:imp___got___TMPdCSs23_ContiguousArrayStorage] ; imp___got___TMPdCSs23_ContiguousArrayStorage, XREF=__TFC12swift_weekly14ViewController8example2fS0_FT_T_+135
438+
00000001000017e6 call imp___stubs__swift_getGenericMetadata1
439+
00000001000017eb mov rbx, rax
440+
00000001000017ee mov qword [ds:__TMLGCSs23_ContiguousArrayStorageCC12swift_weekly14ViewController7Vehicle_], rbx ; __TMLGCSs23_ContiguousArrayStorageCC12swift_weekly14ViewController7Vehicle_
441+
442+
00000001000017fd mov qword [ss:rbp+var_50], rax
443+
0000000100001801 mov esi, 0x28
444+
0000000100001806 mov edx, 0x7
445+
000000010000180b mov rdi, rbx
446+
000000010000180e call imp___stubs__swift_bufferAllocate
447+
0000000100001813 mov r14, rax
448+
0000000100001816 mov qword [ds:r14+0x18], 0x0
449+
000000010000181e mov qword [ds:r14+0x10], 0x0
450+
0000000100001826 mov rdi, r14 ; argument "ptr" for method imp___stubs__malloc_size
451+
0000000100001829 call imp___stubs__malloc_size
452+
000000010000182e sub rax, 0x20
453+
0000000100001832 jo 0x1000019d5
454+
455+
0000000100001838 cmp rax, 0xfffffffffffffff9
456+
000000010000183c jl 0x1000019d5
457+
458+
0000000100001842 mov rcx, rax
459+
0000000100001845 sar rcx, 0x3f
460+
0000000100001849 shr rcx, 0x3d
461+
000000010000184d add rcx, rax
462+
0000000100001850 sar rcx, 0x3
463+
0000000100001854 lea rax, qword [ds:rcx+rcx+0x1]
464+
0000000100001859 mov qword [ds:r14+0x10], 0x1
465+
0000000100001861 mov qword [ds:r14+0x18], rax
466+
0000000100001865 mov r15, qword [ds:r13+0x18]
467+
0000000100001869 mov qword [ds:r14+0x20], r15
468+
000000010000186d call imp___stubs___TMaCSs20_IndirectArrayBuffer
469+
0000000100001872 mov esi, 0x1b
470+
0000000100001877 mov edx, 0x7
471+
000000010000187c mov rdi, rax
472+
000000010000187f call imp___stubs__swift_allocObject
473+
0000000100001884 mov rbx, rax
474+
000000010000188f xor eax, eax
475+
0000000100001891 test r14, r14
476+
0000000100001894 je 0x100001899
477+
478+
0000000100001896 mov rax, r14
479+
480+
0000000100001899 mov qword [ds:rbx+0x10], rax ; XREF=__TFC12swift_weekly14ViewController8example2fS0_FT_T_+340
481+
000000010000189d mov byte [ds:rbx+0x18], 0x1
482+
00000001000018a1 mov byte [ds:rbx+0x19], 0x0
483+
00000001000018a5 mov byte [ds:rbx+0x1a], 0x0
484+
00000001000018a9 mov qword [ss:rbp+var_30], r13
485+
00000001000018ad lea rdi, qword [ss:rbp+var_30]
486+
00000001000018b1 call imp___stubs__swift_fixLifetime
487+
00000001000018bf mov al, byte [ds:rbx+0x19]
488+
00000001000018c2 test al, al
489+
00000001000018c4 je 0x100001906
490+
491+
00000001000018de mov r14, rax
492+
00000001000018e1 mov rdi, rbx
493+
00000001000018e4 call __TTSCC12swift_weekly14ViewController7Vehicle___TFVSs12_ArrayBufferg5countSi
494+
00000001000018e9 mov r15, rax
495+
00000001000018fc test r15, r15
496+
00000001000018ff jg 0x100001931
497+
498+
0000000100001901 jmp 0x1000019d5
499+
500+
0000000100001906 mov r14, qword [ds:rbx+0x10] ; XREF=__TFC12swift_weekly14ViewController8example2fS0_FT_T_+388
501+
000000010000190a test r14, r14
502+
000000010000190d je 0x1000019d5
503+
504+
0000000100001913 mov r15, qword [ds:r14+0x10]
505+
0000000100001927 cmp r15, 0x0
506+
000000010000192b jle 0x1000019d5
507+
508+
0000000100001931 lea rdi, qword [ss:rbp+var_38] ; XREF=__TFC12swift_weekly14ViewController8example2fS0_FT_T_+447
509+
0000000100001935 xor esi, esi
510+
0000000100001937 mov rdx, rbx
511+
000000010000193a call __TTSCC12swift_weekly14ViewController7Vehicle___TFVSs12_ArrayBufferg9subscriptFSiQ_
512+
000000010000193f mov rdi, qword [ss:rbp+var_38]
513+
0000000100001943 mov rsi, r12
514+
0000000100001946 call imp___stubs__swift_dynamicCastClassUnconditional
515+
000000010000194b mov qword [ss:rbp+var_40], rax
516+
0000000100001957 mov rbx, rax
517+
000000010000195a lea rdi, qword [ss:rbp+var_40]
518+
000000010000195e call __TTSCC12swift_weekly14ViewController3Car_VSs7_StdoutS2_Ss16OutputStreamType___TFSs5printU_Ss16OutputStreamType__FTQ_RQ0__T_
519+
000000010000199b mov edi, 0xa ; argument "c" for method imp___stubs__putchar
520+
00000001000019a0 call imp___stubs__putchar
521+
00000001000019c6 add rsp, 0x28
522+
00000001000019ca pop rbx
523+
00000001000019cb pop r12
524+
00000001000019cd pop r13
525+
00000001000019cf pop r14
526+
00000001000019d1 pop r15
527+
00000001000019d3 pop rbp
528+
00000001000019d4 ret
529+
```
530+
531+
i've cut some of the asm code out of this output since they were doing the retain/release cycles for us, i thought they were quite useless.
532+
533+
now let's focus on this part of our swift code:
534+
535+
```swift
536+
let c = [v][0] as Car
537+
```
538+
539+
this is translated to asm like so:
540+
541+
```asm
542+
0000000100001937 mov rdx, rbx
543+
000000010000193a call __TTSCC12swift_weekly14ViewController7Vehicle___TFVSs12_ArrayBufferg9subscriptFSiQ_
544+
000000010000193f mov rdi, qword [ss:rbp+var_38]
545+
0000000100001943 mov rsi, r12
546+
0000000100001946 call imp___stubs__swift_dynamicCastClassUnconditional
547+
```
548+
549+
as you can see, swift called a private hidden method called `imp___stubs__swift_dynamicCastClassUnconditional` to __downcast__ the `[Vehicle]` array's first item into a `Car` instance
550+
551+
Conditional Downcasting
552+
===
553+
554+
555+
363556
Conclusion
364557
===
365558
1. Typecasting of values in Swift is done through an internal function called `imp___stubs__swift_dynamicCast`. Swift tends to typecast dynamically at runtime, rather than compile-time. This obviously has performance implications so keep that in mind.
366559
2. An internal function called `__TTSSi_VSs7_StdoutS_Ss16OutputStreamType___TFSs5printU_Ss16OutputStreamType__FTQ_RQ0__T_` does the work for `println()` of `Int` values to the console.
560+
3. Unconditional downcasts in Swift are done with a call to an internal function called `imp___stubs__swift_dynamicCastClassUnconditional`, at runtime.
367561

368562
References
369563
===

issue08/exampleCode/swift-weekly/ViewController.swift

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,47 @@ class ViewController: UIViewController {
3131

3232
}
3333

34+
class Vehicle{
35+
func id() -> Int{
36+
return 0xabcdefa
37+
}
38+
}
39+
40+
class Car : Vehicle{
41+
override func id() -> Int {
42+
return 0xabcdefc
43+
}
44+
}
45+
46+
func example2(){
47+
let v: Vehicle = Car()
48+
let c = [v][0] as Car
49+
println(c)
50+
}
51+
52+
class Bicycle : Vehicle{
53+
override func id() -> Int {
54+
return 0xabcdefb
55+
}
56+
}
57+
58+
func example3(){
59+
60+
let items = [Bicycle(), Car(), Bicycle()]
61+
for i in items{
62+
if let b = i as? Bicycle{
63+
println(0xabcdefd)
64+
}
65+
else if let c = i as? Car{
66+
println(0xabcdefe)
67+
}
68+
}
69+
}
70+
3471
override func viewDidLoad() {
3572
super.viewDidLoad()
3673

37-
example1()
74+
example2()
3875
}
3976

4077
}

0 commit comments

Comments
 (0)