3. 목적
Swift vs. Kotlin Swift & Kotlin
문법의 유사성 소개
모던 언어에서의 이슈들을 두 언어가 어떻게 해결하고 있는지 비교
차이점이 생길 수 밖에 없는 기반 시스템 비교
4. Swift & Kotlin
Swift
•Apple에서 개발
•Objective-C의 다음 세대 언어
•Open source (https://github.com/apple/swift)
•주로 사용되는 곳: iOS, macOS, watchOS, tvOS
•기타: Linux
Kotlin
•JetBrains에서 개발
•Java의 다음 세대 언어 (중의 하나)
•Open source (https://github.com/JetBrains/kotlin)
•주로 사용되는 곳: Android, server, 자바가 쓰이는 곳 어디든
•기타: JVM이 아닌 JavaScript 타겟도 지원함
7. 변수 선언
let number: Int = 1
var any: Any = number
val number: Int = 1
var any: Any = number
타입 체크
if any is Int {
// ...
}
if (any is Int) {
// ...
}
Downcast let num2: Int = any as! Int val num2: Int = any as Int
타입 변환 let long: Int64 = Int64(number) val long: Long = number.toLong()
타입 추론 let number = 1 val number = 1
정적 타입
8. 변수 선언
let number: Int = 1
var any: Any = number
val number: Int = 1
var any: Any = number
타입 체크
if any is Int {
// ...
}
if (any is Int) {
// ...
}
Downcast let num2: Int = any as! Int val num2: Int = any as Int
타입 변환 let long: Int64 = Int64(number) val long: Long = number.toLong()
타입 추론 let number = 1 val number = 1
정적 타입
9. 변수 선언
let number: Int = 1
var any: Any = number
val number: Int = 1
var any: Any = number
타입 체크
if any is Int {
// ...
}
if (any is Int) {
// ...
}
Downcast let num2: Int = any as! Int val num2: Int = any as Int
타입 변환 let long: Int64 = Int64(number) val long: Long = number.toLong()
타입 추론 let number = 1 val number = 1
정적 타입
10. 변수 선언
let number: Int = 1
var any: Any = number
val number: Int = 1
var any: Any = number
타입 체크
if any is Int {
// ...
}
if (any is Int) {
// ...
}
Downcast let num2: Int = any as! Int val num2: Int = any as Int
타입 변환 let long: Int64 = Int64(number) val long: Long = number.toLong()
타입 추론 let number = 1 val number = 1
정적 타입
11. var conference = "LetSwift"
conference = "VarSwift"
var conference = "ValKotlin"
conference = "VarKotlin"
let conference: String
if isSwift {
conference = "LetSwift"
} else {
conference = "ValKotlin"
}
val conference: String
if (isSwift) {
conference = "LetSwift"
} else {
conference = "ValKotlin"
}
let conference = "LetSwift"
conference = "VarSwift"
val conference = "ValKotlin"
conference = “VarKotlin"
읽기 전용 변수
읽기 전용 변수 -> 재할당 불가
12. var conference = "LetSwift"
conference = "VarSwift"
var conference = "ValKotlin"
conference = "VarKotlin"
let conference: String
if isSwift {
conference = "LetSwift"
} else {
conference = "ValKotlin"
}
val conference: String
if (isSwift) {
conference = "LetSwift"
} else {
conference = "ValKotlin"
}
let conference = "LetSwift"
conference = "VarSwift"
val conference = "ValKotlin"
conference = “VarKotlin"
읽기 전용 변수
읽기 전용 변수 -> 재할당 불가
13. var conference = "LetSwift"
conference = "VarSwift"
var conference = "ValKotlin"
conference = "VarKotlin"
let conference: String
if isSwift {
conference = "LetSwift"
} else {
conference = "ValKotlin"
}
val conference: String
if (isSwift) {
conference = "LetSwift"
} else {
conference = "ValKotlin"
}
let conference = "LetSwift"
conference = "VarSwift"
val conference = "ValKotlin"
conference = “VarKotlin"
읽기 전용 변수
컴파일 시점에 값이 정해지는 것은 아님
처음 사용되기 전에 값이 한번 할당된다는 것을 컴파일 시점에 체크
14. 타입 추론
SomeClassWithVeryLongName* bar = [[SomeClassWithVeryLongName alloc] init];
SomeClassWithVeryLongName* other = bar
SomeClassWithVeryLongName bar = new SomeClassWithVeryLongName();
SomeClassWithVeryLongName other = bar;
ObjC
Java
15. 타입 추론
SomeClassWithVeryLongName* bar = [[SomeClassWithVeryLongName alloc] init];
SomeClassWithVeryLongName* other = bar
SomeClassWithVeryLongName bar = new SomeClassWithVeryLongName();
SomeClassWithVeryLongName other = bar;
ObjC
Java
중복된 정보
16. 타입 추론
SomeClassWithVeryLongName* bar = [[SomeClassWithVeryLongName alloc] init];
SomeClassWithVeryLongName* other = bar
SomeClassWithVeryLongName bar = new SomeClassWithVeryLongName();
SomeClassWithVeryLongName other = bar;
ObjC
Java
let bar = SomeClassWithVeryLongName()
var other = bar
val bar = SomeClassWithVeryLongName()
var other = bar
17. 타입 추론
SomeClassWithVeryLongName* bar = [[SomeClassWithVeryLongName alloc] init];
SomeClassWithVeryLongName* other = bar
SomeClassWithVeryLongName bar = new SomeClassWithVeryLongName();
SomeClassWithVeryLongName other = bar;
ObjC
Java
let bar = SomeClassWithVeryLongName()
var other = bar
val bar = SomeClassWithVeryLongName()
var other = bar
other = SomeOtherClass()
other = SomeOtherClass() 그래도 정적 타입이니까
18. Optional & Nullable
nil/null 값을 가진 변수를 주소값으로 사용했을때
•런타임 에러 발생
•프로그램의 안정성을 해침 (가장 빈번한 에러)
•Objc의 경우 sendmsg방식으로 크래시는 안나도 의도치 않은 동작 가능
•100% 안전하려면 항상 변수가 nil/null 인지 확인해야함
•nil/null인 경우가 없는 경우도 체크하게 됨 -> 비효율
•강제가 아니기 때문에 빠뜨릴 수 있음 -> 구멍
19. Optional & Nullable
타입에 nil/null을 가질 수 있는지의 의미도 부여한다면
•nil/null 가질 수 있는 Optional/Nullable 타입: 무조건 체크해야함
•런타임 에러 -> 컴파일타임 에러: 안정성 향상
•not-null 타입: 체크 안하고 안정적으로 사용
•의외로 not-null인 변수를 다루는 경우가 더 많음: 코드가 간결해짐
20. Optional & Nullable
타입
let str = "1234"
let num: Int? = Int(str)
val str = "1234"
val num: Int? = str.toIntOrNull()
기본값 let num2: Int = num ?? -1 val num2: Int = num ?: -1
체이닝 server?.connect().name.capitalized server?.connect()?.name?.capitalize()
Downcast
let any: Any
let number = any as? Int
val any: Any
val number = any as? Int
강제
Downcast
let number = any as! Int val number = any as Int
강제
Unwrap
server!.connect().name.capitalized server!!.connect().name.capitalize()
21. Optional & Nullable
타입
let str = "1234"
let num: Int? = Int(str)
val str = "1234"
val num: Int? = str.toIntOrNull()
기본값 let num2: Int = num ?? -1 val num2: Int = num ?: -1
체이닝 server?.connect().name.capitalized server?.connect()?.name?.capitalize()
Downcast
let any: Any
let number = any as? Int
val any: Any
val number = any as? Int
강제
Downcast
let number = any as! Int val number = any as Int
강제
Unwrap
server!.connect().name.capitalized server!!.connect().name.capitalize()
22. Optional & Nullable
타입
let str = "1234"
let num: Int? = Int(str)
val str = "1234"
val num: Int? = str.toIntOrNull()
기본값 let num2: Int = num ?? -1 val num2: Int = num ?: -1
체이닝 server?.connect().name.capitalized server?.connect()?.name?.capitalize()
Downcast
let any: Any
let number = any as? Int
val any: Any
val number = any as? Int
강제
Downcast
let number = any as! Int val number = any as Int
강제
Unwrap
server!.connect().name.capitalized server!!.connect().name.capitalize()
23. Optional & Nullable
타입
let str = "1234"
let num: Int? = Int(str)
val str = "1234"
val num: Int? = str.toIntOrNull()
기본값 let num2: Int = num ?? -1 val num2: Int = num ?: -1
체이닝 server?.connect().name.capitalized server?.connect()?.name?.capitalize()
Downcast
let any: Any
let number = any as? Int
val any: Any
val number = any as? Int
강제
Downcast
let number = any as! Int val number = any as Int
강제
Unwrap
server!.connect().name.capitalized server!!.connect().name.capitalize()
((server?.connect())?.name)?.capitalized
24. Optional & Nullable
타입
let str = "1234"
let num: Int? = Int(str)
val str = "1234"
val num: Int? = str.toIntOrNull()
기본값 let num2: Int = num ?? -1 val num2: Int = num ?: -1
체이닝 server?.connect().name.capitalized server?.connect()?.name?.capitalize()
Downcast
let any: Any
let number = any as? Int
val any: Any
val number = any as? Int
강제
Downcast
let number = any as! Int val number = any as Int
강제
Unwrap
server!.connect().name.capitalized server!!.connect().name.capitalize()
25. Optional & Nullable
타입
let str = "1234"
let num: Int? = Int(str)
val str = "1234"
val num: Int? = str.toIntOrNull()
기본값 let num2: Int = num ?? -1 val num2: Int = num ?: -1
체이닝 server?.connect().name.capitalized server?.connect()?.name?.capitalize()
Downcast
let any: Any
let number = any as? Int
val any: Any
val number = any as? Int
강제
Downcast
let number = any as! Int val number = any as Int
강제
Unwrap
server!.connect().name.capitalized server!!.connect().name.capitalize()
웬만하면 쓰지말라고 느낌표 2개
26. Optional & Nullable
타입
let str = "1234"
let num: Int? = Int(str)
val str = "1234"
val num: Int? = str.toIntOrNull()
기본값 let num2: Int = num ?? -1 val num2: Int = num ?: -1
체이닝 server?.connect().name.capitalized server?.connect()?.name?.capitalize()
Downcast
let any: Any
let number = any as? Int
val any: Any
val number = any as? Int
강제
Downcast
let number = any as! Int val number = any as Int
강제
Unwrap
server!.connect().name.capitalized server!!.connect().name.capitalize()
27. Optional & Nullable
func foo(value: String?) {
if let value = value {
print(value.capitalized)
}
}
fun foo(value: String?) {
if (value != null) {
println(value.capitalize())
}
}
func foo(value: String?) {
guard let value = value else { return }
print(value.capitalized)
}
fun foo(value: String?) {
if (value == null) return
println(value.capitalize())
}
func foo(value: Any) {
if let value = value as? String {
print(value.capitalized)
}
}
fun foo(value: Any) {
if (value is String) {
println(value.capitalize())
}
}
Smart CastOptional Unwrap
28. Optional & Nullable
func foo(value: String?) {
if let value = value {
print(value.capitalized)
}
}
fun foo(value: String?) {
if (value != null) {
println(value.capitalize())
}
}
func foo(value: String?) {
guard let value = value else { return }
print(value.capitalized)
}
fun foo(value: String?) {
if (value == null) return
println(value.capitalize())
}
func foo(value: Any) {
if let value = value as? String {
print(value.capitalized)
}
}
fun foo(value: Any) {
if (value is String) {
println(value.capitalize())
}
}
Smart CastOptional Unwrap
29. Optional & Nullable
func foo(value: String?) {
if let value = value {
print(value.capitalized)
}
}
fun foo(value: String?) {
if (value != null) {
println(value.capitalize())
}
}
func foo(value: String?) {
guard let value = value else { return }
print(value.capitalized)
}
fun foo(value: String?) {
if (value == null) return
println(value.capitalize())
}
func foo(value: Any) {
if let value = value as? String {
print(value.capitalized)
}
}
fun foo(value: Any) {
if (value is String) {
println(value.capitalize())
}
}
Smart CastOptional Unwrap
36. view2
class View
location
(10.0, 0.0)
view1
class View
location
(0.0, 0.0)
view2.location = Point(view.location.x + 10.0, view.location)
기존 객체의 값을 이용해 새로운 객체를 할당
class Point
readonly x: 0.0
readonly y: 0.0
class Point
readonly x: 10.0
readonly y: 0.0
ObjC
*) 실제 View, UIView의 location의 예시는 아님
공유 상태 문제 해결
37. Immutable type
공유되는 변수의 내부 값이 변경 가능할때 생기는 문제 원천 차단
복잡도 감소
•변수의 상태 변화에 대한 트래킹 이슈 없음
성능 향상
•multi-thread에서 lock없이 공유 가능
38. view1
class View
location:
(0.0, 0.0)
Value 타입을 이용하면
애초에 공유될 수가 없음
struct Point
x: 0.0
y: 0.0
view2
class View
location:
(0.0, 0.0)
struct Point
x: 0.0
y: 0.0
Value 타입을 이용하면 초간단
공유 상태 문제 해결
39. view1
class View
location:
(0.0, 0.0)
struct Point
x: 0.0
y: 0.0
view2
class View
location:
(10.0, 0.0)
struct Point
x: 10.0
y: 0.0
Value 타입을 이용하면 초간단
view2.location.x += 10.0
공유 상태 문제 해결
40. 숫자, 문자, Boolean
Value
(Int, Int64, Double, Bool, …)
Value (*) / Reference
(Int, Long, Double, Boolean, …)
String Value Reference
Collections
Value
(Array, Set, Dictionary)
Reference
(List, Set, Map)
Enum Value Reference
Struct Value 없음
Tuple Value 없음
Class Reference Reference
Function,
Closure / Lambda
Reference Reference
Reference / Value 타입
(*) Kotlin에는 Value 타입의 개념이 없고 JVM에서 원시타입이 Value 타입
41. String Literals
생성 let str = "lettSwiftn" val str = "valtKotlinn"
여러줄 생성
val json = """
{
"name": “Swift",
"version": (version)
}
"""
val json = """
{
"name": "Kotlin",
"version": $version
}
"""
연결
let lang = "Swift"
let title = "Hello, " + lang
val lang = "Kotlin"
val title = "Hello, " + lang
템플릿
let year = 2017
let current = "LetSwift(year)"
let previous = "LetSwift(year - 1)”
val year = 2017
val current = "ValKotlin$year"
val previous = "ValKotlin${year - 1}"
(Swift4.0)
50. 1. mutable 동작의 API가 없음
2. open class가 아니어서 상속을 할 수 없음
-> String 타입으로 다룰 수 있는 Mutable String을 만드는 것이 불가능
public class String … { … }
String: Mutability
51. 1. mutable 동작의 API가 없음
2. open class가 아니어서 상속을 할 수 없음
-> String 타입으로 다룰 수 있는 Mutable String을 만드는 것이 불가능
String은 Map의 key등 상수형으로 많이 쓰임
이런 String의 상태가 변경되는 것은 큰 혼란을 일으킬 수 있음
public class String … { … }
String: Mutability
52. val str1 = "Let"
var str2 = str1 + "Swift"
str2 = str2.replace("Swift", “Kotlin")
1. mutable 동작의 API가 없음
2. open class가 아니어서 상속을 할 수 없음
-> String 타입으로 다룰 수 있는 Mutable String을 만드는 것이 불가능
변경하려면: 변경된 문자열을 가진 새로운 객체를 생성해야 함
public class String … { … }
String: Mutability
53. Collection
중복 가능
순서 있음
struct Array<E>
interface List<out E>
interface MutableList<E> : List<E>
(class: ArrayList, …)
(*)
중복 불가 struct Set<E>
interface Set<out E>
interface MutableSet<E> : Set<E>
(class: LinkedHashSet, SortedSet, …)
Key의 중복 불가
Key, Value의 쌍
struct Dictionary<K, V>
interface Map<out K, out V>
interface MutableMap<K, V> : Map<K, V>
(class: LinkedHashMap, HashMap, …)
(*) Array도 존재하지만 JVM array에 대응하는 자료 구조로
Collection과는 성격이 약간 다름
55. Collection
중복 가능
순서 있음
struct Array<E>
interface List<out E>
interface MutableList<E> : List<E>
(class: ArrayList, …)
(*)
중복 불가 struct Set<E>
interface Set<out E>
interface MutableSet<E> : Set<E>
(class: LinkedHashSet, SortedSet, …)
Key의 중복 불가
Key, Value의 쌍
struct Dictionary<K, V>
interface Map<out K, out V>
interface MutableMap<K, V> : Map<K, V>
(class: LinkedHashMap, HashMap, …)
Immutable/Mutable 인터페이스 제공
성격에 따라 다양한 실제 구현 클래스 사용가능 (Java)
56. Immutable Collections
Array /
List
let array: [String] = ["Swift", "Kotlin"]
print(array[0])
val list: List<String> =
listOf("Swift", "Kotlin")
println(list[1])
Set
let set: Set<String> = ["Swift", "Kotlin"]
if set.contains("Objc") { }
val set: Set<String> =
setOf("Swift", "Kotlin")
if ("Java" in set) { }
Dictionary /
Map
let dic: [String : Double] =
[“Swift" : 3.1, "Kotlin" : 1.1]
print(dic["Swift"])
val map: Map<String, Double> =
mapOf("Swift" to 3.1, “Kotlin" to 1.1)
println(map["Kotlin"])
57. Immutable Collections
Array /
List
let array: [String] = ["Swift", "Kotlin"]
print(array[0])
val list: List<String> =
listOf("Swift", "Kotlin")
println(list[1])
Set
let set: Set<String> = ["Swift", "Kotlin"]
if set.contains("Objc") { }
val set: Set<String> =
setOf("Swift", "Kotlin")
if ("Java" in set) { }
Dictionary /
Map
let dic: [String : Double] =
[“Swift" : 3.1, "Kotlin" : 1.1]
print(dic["Swift"])
val map: Map<String, Double> =
mapOf("Swift" to 3.1, “Kotlin" to 1.1)
println(map["Kotlin"])
58. Immutable Collections
Array /
List
let array: [String] = ["Swift", "Kotlin"]
print(array[0])
val list: List<String> =
listOf("Swift", "Kotlin")
println(list[1])
Set
let set: Set<String> = ["Swift", "Kotlin"]
if set.contains("Objc") { }
val set: Set<String> =
setOf("Swift", "Kotlin")
if ("Java" in set) { }
Dictionary /
Map
let dic: [String : Double] =
[“Swift" : 3.1, "Kotlin" : 1.1]
print(dic["Swift"])
val map: Map<String, Double> =
mapOf("Swift" to 3.1, “Kotlin" to 1.1)
println(map["Kotlin"])
59. Immutable Collections
Array /
List
let array: [String] = ["Swift", "Kotlin"]
print(array[0])
val list: List<String> =
listOf("Swift", "Kotlin")
println(list[1])
Set
let set: Set<String> = ["Swift", "Kotlin"]
if set.contains("Objc") { }
val set: Set<String> =
setOf("Swift", "Kotlin")
if ("Java" in set) { }
Dictionary /
Map
let dic: [String : Double] =
[“Swift" : 3.1, "Kotlin" : 1.1]
print(dic["Swift"])
val map: Map<String, Double> =
mapOf("Swift" to 3.1, “Kotlin" to 1.1)
println(map["Kotlin"])
60. Immutable Collections
Array /
List
let array: [String] = ["Swift", "Kotlin"]
print(array[0])
val list: List<String> =
listOf("Swift", "Kotlin")
println(list[1])
Set
let set: Set<String> = ["Swift", "Kotlin"]
if set.contains("Objc") { }
val set: Set<String> =
setOf("Swift", "Kotlin")
if ("Java" in set) { }
Dictionary /
Map
let dic: [String : Double] =
[“Swift" : 3.1, "Kotlin" : 1.1]
print(dic["Swift"])
val map: Map<String, Double> =
mapOf("Swift" to 3.1, “Kotlin" to 1.1)
println(map["Kotlin"])
61. Mutable Collections
Array /
List
var array: [String] = ["Swift", "Kotlin"]
print(array[0])
array[0] = "Objc"
val list: MutableList<String> =
mutableListOf("Swift", "Kotlin")
println(list[1])
list[1] = "Java"
Set
var set: Set<String> = ["Swift", "Kotlin"]
if set.contains("Objc") { }
set.insert("Objc")
val set: MutableSet<String> =
mutableSetOf("Swift", "Kotlin")
if ("Java" in set) { }
set.add("Java")
Dictionary /
Map
var dic: [String : Double] =
[“Swift" : 3.1, "Kotlin" : 1.1]
print(dic["Swift"])
dic["Swift"] = 4.0
val map: MutableMap<String, Double> =
mutableMapOf("Swift" to 3.1, “Kotlin" to 1.1)
println(map["Kotlin"])
map["Kotlin"] = 1.2
62. Mutable Collections
Array /
List
var array: [String] = ["Swift", "Kotlin"]
print(array[0])
array[0] = "Objc"
val list: MutableList<String> =
mutableListOf("Swift", "Kotlin")
println(list[1])
list[1] = "Java"
Set
var set: Set<String> = ["Swift", "Kotlin"]
if set.contains("Objc") { }
set.insert("Objc")
val set: MutableSet<String> =
mutableSetOf("Swift", "Kotlin")
if ("Java" in set) { }
set.add("Java")
Dictionary /
Map
var dic: [String : Double] =
[“Swift" : 3.1, "Kotlin" : 1.1]
print(dic["Swift"])
dic["Swift"] = 4.0
val map: MutableMap<String, Double> =
mutableMapOf("Swift" to 3.1, “Kotlin" to 1.1)
println(map["Kotlin"])
map["Kotlin"] = 1.2
63. Mutable Collections
Array /
List
var array: [String] = ["Swift", "Kotlin"]
print(array[0])
array[0] = "Objc"
val list: MutableList<String> =
mutableListOf("Swift", "Kotlin")
println(list[1])
list[1] = "Java"
Set
var set: Set<String> = ["Swift", "Kotlin"]
if set.contains("Objc") { }
set.insert("Objc")
val set: MutableSet<String> =
mutableSetOf("Swift", "Kotlin")
if ("Java" in set) { }
set.add("Java")
Dictionary /
Map
var dic: [String : Double] =
[“Swift" : 3.1, "Kotlin" : 1.1]
print(dic["Swift"])
dic["Swift"] = 4.0
val map: MutableMap<String, Double> =
mutableMapOf("Swift" to 3.1, “Kotlin" to 1.1)
println(map["Kotlin"])
map["Kotlin"] = 1.2
64. Mutable Collections
Array /
List
var array: [String] = ["Swift", "Kotlin"]
print(array[0])
array[0] = "Objc"
val list: MutableList<String> =
mutableListOf("Swift", "Kotlin")
println(list[1])
list[1] = "Java"
Set
var set: Set<String> = ["Swift", "Kotlin"]
if set.contains("Objc") { }
set.insert("Objc")
val set: MutableSet<String> =
mutableSetOf("Swift", "Kotlin")
if ("Java" in set) { }
set.add("Java")
Dictionary /
Map
var dic: [String : Double] =
[“Swift" : 3.1, "Kotlin" : 1.1]
print(dic["Swift"])
dic["Swift"] = 4.0
val map: MutableMap<String, Double> =
mutableMapOf("Swift" to 3.1, “Kotlin" to 1.1)
println(map["Kotlin"])
map["Kotlin"] = 1.2
65. Mutable Collections
Array /
List
var array: [String] = ["Swift", "Kotlin"]
print(array[0])
array[0] = "Objc"
val list: MutableList<String> =
mutableListOf("Swift", "Kotlin")
println(list[1])
list[1] = "Java"
Set
var set: Set<String> = ["Swift", "Kotlin"]
if set.contains("Objc") { }
set.insert("Objc")
val set: MutableSet<String> =
mutableSetOf("Swift", "Kotlin")
if ("Java" in set) { }
set.add("Java")
Dictionary /
Map
var dic: [String : Double] =
[“Swift" : 3.1, "Kotlin" : 1.1]
print(dic["Swift"])
dic["Swift"] = 4.0
val map: MutableMap<String, Double> =
mutableMapOf("Swift" to 3.1, “Kotlin" to 1.1)
println(map["Kotlin"])
map["Kotlin"] = 1.2
66. Mutable Collections
Array /
List
var array: [String] = ["Swift", "Kotlin"]
print(array[0])
array[0] = "Objc"
val list: MutableList<String> =
mutableListOf("Swift", "Kotlin")
println(list[1])
list[1] = "Java"
Set
var set: Set<String> = ["Swift", "Kotlin"]
if set.contains("Objc") { }
set.insert("Objc")
val set: MutableSet<String> =
mutableSetOf("Swift", "Kotlin")
if ("Java" in set) { }
set.add("Java")
Dictionary /
Map
var dic: [String : Double] =
[“Swift" : 3.1, "Kotlin" : 1.1]
print(dic["Swift"])
dic["Swift"] = 4.0
val map: MutableMap<String, Double> =
mutableMapOf("Swift" to 3.1, “Kotlin" to 1.1)
println(map["Kotlin"])
map["Kotlin"] = 1.2
Value 타입이므로 let -> var 로
Copy-On-Write로 동작
67. Mutable Collections
Array /
List
var array: [String] = ["Swift", "Kotlin"]
print(array[0])
array[0] = "Objc"
val list: MutableList<String> =
mutableListOf("Swift", "Kotlin")
println(list[1])
list[1] = "Java"
Set
var set: Set<String> = ["Swift", "Kotlin"]
if set.contains("Objc") { }
set.insert("Objc")
val set: MutableSet<String> =
mutableSetOf("Swift", "Kotlin")
if ("Java" in set) { }
set.add("Java")
Dictionary /
Map
var dic: [String : Double] =
[“Swift" : 3.1, "Kotlin" : 1.1]
print(dic["Swift"])
dic["Swift"] = 4.0
val map: MutableMap<String, Double> =
mutableMapOf("Swift" to 3.1, “Kotlin" to 1.1)
println(map["Kotlin"])
map["Kotlin"] = 1.2
Mutable 전용 인터페이스 이용
var는 단순히 변수를 재할당 가능하다는 의미
68. Swift의 Mutability
Value 타입으로 구현
변수의 타입만으로 Immutable / mutable 동작 구분 (let, var)
데이터 저장공간은 Reference type의 멤버 변수로 가짐
Mutable 타입의 경우 내용이 처음 변경될 때 새로운 객체 생성
(이때 저장공간 복사 -> Copy-On-Write)
69. Swift의 Mutability
Value 타입으로 구현
변수의 타입만으로 Immutable / mutable 동작 구분 (let, var)
데이터 저장공간은 Reference type의 멤버 변수로 가짐
Mutable 타입의 경우 내용이 처음 변경될 때 새로운 객체 생성
(이때 저장공간 복사 -> Copy-On-Write)
var original: [Int] = [1, 2]
var mutable = array
mutable.append(3)
original
struct Array<Int>
_storage
1, 2
…
refCount: 2
[1, 2]
mutable
struct Array<Int>
_storage
1, 2
70. Swift의 Mutability
Value 타입으로 구현
변수의 타입만으로 Immutable / mutable 동작 구분 (let, var)
데이터 저장공간은 Reference type의 멤버 변수로 가짐
Mutable 타입의 경우 내용이 처음 변경될 때 새로운 객체 생성
(이때 저장공간 복사 -> Copy-On-Write)
var original: [Int] = [1, 2]
var mutable = array
mutable.append(3)
original
struct Array<Int>
_storage
1, 2
…
refCount: 1
[1, 2]
mutable
struct Array<Int>
_storage
1, 2, 3
…
refCount: 1
[1, 2, 3]
72. Swift의 Mutability
func append(var array: [Int]) {
array.append(3)
}
var mutable: [Int] = [1, 2]
append(array: mutable)
어짜피 Value 타입이기 때문에,
본래의 변수가 변하지 않는다
하지만, 함수로는 var 타입 변수를 넘길 수 없다. (Swift3)
73. Swift의 Mutability
func append(var array: [Int]) {
array.append(3)
}
var mutable: [Int] = [1, 2]
append(array: mutable)
하지만, 값을 변경해야하는 경우마다 데이터를 복사해야하는 것도 비효율적
하지만, 함수로는 var 타입 변수를 넘길 수 없다. (Swift3)
79. In-Out과 Reference
func increase(_ i: inout Int) {
i += 1
}
var number = 10
increase(&number)
print(number)
In-Out 파라미터
원래 변수의 주소가 넘어감
원래 변수의 value를 변경하게 됨
80. In-Out과 Reference
func increase(_ i: inout Int) {
i += 1
}
var number = 10
increase(&number)
print(number)
class Wrapper(var i: Int)
fun increase(wrapper: Wrapper) {
wrapper.i += 1
}
val number = Wrapper(10)
increase(number)
println(number.i)
원래 변수의 주소가 넘어감
원래 변수의 value를 변경하게 됨
Wrapper를 이용할 수 밖에 없음
In-Out 파라미터
81. In-Out과 Reference
func increase(_ i: inout Int) {
i += 1
}
var number = 10
increase(&number)
print(number)
class Wrapper(var i: Int)
fun increase(wrapper: Wrapper) {
wrapper.i += 1
}
val number = Wrapper(10)
increase(number)
println(number.i)
원래 변수의 주소가 넘어감
원래 변수의 value를 변경하게 됨
Wrapper를 이용할 수 밖에 없음
In-Out 파라미터
이것이 두 언어의 Mutability 구현의 차이
83. Extension
extension Int {
var length: Int {
return String(self).characters.count
}
func concat(_ to: Int) -> String {
return String(self) + String(to)
}
}
print(1234.length) // 4
print(123.concat(456)) // "123456"
val Int.length: Int get() = toString().length
fun Int.concat(to: Int): String =
toString() + to.toString()
println(123.length) // 3
println(123.concat(456)) // "123456"
extension 정의 block 사용
84. Extension
extension Int {
var length: Int {
return String(self).characters.count
}
func concat(_ to: Int) -> String {
return String(self) + String(to)
}
}
print(1234.length) // 4
print(123.concat(456)) // "123456"
val Int.length: Int get() = toString().length
fun Int.concat(to: Int): String =
toString() + to.toString()
println(123.length) // 3
println(123.concat(456)) // "123456"
extension 정의 block 사용 extension할 각 함수, 프로퍼티 별로 정의
85. Extension
extension Int {
var length: Int {
return String(self).characters.count
}
func concat(_ to: Int) -> String {
return String(self) + String(to)
}
}
print(1234.length) // 4
print(123.concat(456)) // "123456"
val Int.length: Int get() = toString().length
fun Int.concat(to: Int): String =
toString() + to.toString()
println(123.length) // 3
println(123.concat(456)) // "123456"
extension 정의 block 사용 extension할 각 함수, 프로퍼티 별로 정의
fun length(receiver: Int) = receiver.toString().length
fun concat(receiver: Int, to: Int): String = receiver.toString() + to.toString()
실제로는 아래와 같은 함수로 컴파일 됨
86. Trait
class MyClass { }
protocol MyProtocol { }
extension MyClass: MyProtocol { }
let bar: MyProtocol = MyClass()
extension MyProtocol {
func foo() { }
}
87. Trait
class MyClass { }
protocol MyProtocol { }
extension MyClass: MyProtocol { }
let bar: MyProtocol = MyClass()
extension MyProtocol {
func foo() { }
}
bar.foo()
let bar2: MyClass = MyClass()
bar2.foo()
88. Trait
class MyClass { }
protocol MyProtocol { }
extension MyClass: MyProtocol { }
let bar: MyProtocol = MyClass()
extension MyProtocol {
func foo() { }
}
bar.foo()
let bar2: MyClass = MyClass()
bar2.foo()
AKA: Protocol Oriented Programming
89. class MyClass { }
protocol MyProtocol { }
extension MyClass: MyProtocol { }
let bar: MyProtocol = MyClass()
extension MyProtocol {
func foo() { }
}
bar.foo()
let bar2: MyClass = MyClass()
bar2.foo()
interface MyProtocol {
fun foo() { }
}
class MyClass : MyProtocol
val bar: MyProtocol = MyClass()
bar.foo()
AKA: Protocol Oriented Programming
Trait
90. class MyClass { }
protocol MyProtocol { }
extension MyClass: MyProtocol { }
let bar: MyProtocol = MyClass()
extension MyProtocol {
func foo() { }
}
bar.foo()
let bar2: MyClass = MyClass()
bar2.foo()
interface MyProtocol {
fun foo() { }
}
class MyClass : MyProtocol
val bar: MyProtocol = MyClass()
bar.foo()
fun MyProtocol.foo2() { }
bar.foo2()
val bar2: MyClass = MyClass()
bar2.foo2()
AKA: Protocol Oriented Programming
Trait
91. class MyClass { }
protocol MyProtocol { }
extension MyClass: MyProtocol { }
let bar: MyProtocol = MyClass()
extension MyProtocol {
func foo() { }
}
bar.foo()
let bar2: MyClass = MyClass()
bar2.foo()
interface MyProtocol {
fun foo() { }
}
class MyClass : MyProtocol
val bar: MyProtocol = MyClass()
bar.foo()
fun MyProtocol.foo2() { }
bar.foo2()
val bar2: MyClass = MyClass()
bar2.foo2()
AKA: Protocol Oriented Programming 미리 인터페이스가 클래스에 적용이 되어야함
Trait
92. Trait
extension String: MyProtocol {
}
"Swift".foo()
정의되어 있는 class/struct에도 trait 가능
class MyClass { }
protocol MyProtocol { }
extension MyClass: MyProtocol { }
let bar: MyProtocol = MyClass()
extension MyProtocol {
func foo() { }
}
bar.foo()
let bar2: MyClass = MyClass()
bar2.foo()
interface MyProtocol {
fun foo() { }
}
class MyClass : MyProtocol
val bar: MyProtocol = MyClass()
bar.foo()
fun MyProtocol.foo2() { }
bar.foo2()
val bar2: MyClass = MyClass()
bar2.foo2()
AKA: Protocol Oriented Programming 미리 인터페이스가 클래스에 적용이 되어야함
93. Trait
extension String: MyProtocol {
}
"Swift".foo()
// ...
// ...
// ...
정의되어 있는 class/struct에도 trait 가능 안됨
class MyClass { }
protocol MyProtocol { }
extension MyClass: MyProtocol { }
let bar: MyProtocol = MyClass()
extension MyProtocol {
func foo() { }
}
bar.foo()
let bar2: MyClass = MyClass()
bar2.foo()
interface MyProtocol {
fun foo() { }
}
class MyClass : MyProtocol
val bar: MyProtocol = MyClass()
bar.foo()
fun MyProtocol.foo2() { }
bar.foo2()
val bar2: MyClass = MyClass()
bar2.foo2()
AKA: Protocol Oriented Programming 미리 인터페이스가 클래스에 적용이 되어야함
94. 유틸리티를 위한 extension
fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
val sum = 1.apply { println("get 1") } + 2 // 3
fun <T, R> T.let(block: (T) -> R): R = block(this)
val file = "filename".let { File(it) }
fun CharSequence?.isNullOrBlank(): Boolean = this == null || this.isBlank()
val str: String? = null
if (str.isNullOrBlank()) { }
Nullable에 대한 extension
모든 타입에 대한 넘나 편리한 유틸리티
선언형 표현에 크게 도움이 됨
101. Heap
fun foo() {
val subview = View(context)
this.viewGroup.addView(subview)
this.viewGroup.removeView(subview)
}
GC
class View
…
Stack
subview:
class ViewGroup
child:
… GC task
thread
102. Heap
fun foo() {
val subview = View(context)
this.viewGroup.addView(subview)
this.viewGroup.removeView(subview)
}
GC
class View
…
Stack
subview:
class ViewGroup
child:
… GC task
thread
103. Heap
fun foo() {
val subview = View(context)
this.viewGroup.addView(subview)
this.viewGroup.removeView(subview)
}
GC
class View
…
Stack
subview:
class ViewGroup
child:
… GC task
thread
104. Heap
fun foo() {
val subview = View(context)
this.viewGroup.addView(subview)
this.viewGroup.removeView(subview)
}
GC
class View
…
Stack
subview:
class ViewGroup
child:
… GC task
thread
105. Heap
fun foo() {
val subview = View(context)
this.viewGroup.addView(subview)
this.viewGroup.removeView(subview)
}
GC
class View
…
Stack
class ViewGroup
child:
… GC task
thread
바로 해제되지 않는다.
106. Heap
fun foo() {
val subview = View(context)
this.viewGroup.addView(subview)
this.viewGroup.removeView(subview)
}
GC
class View
…
Stack
class ViewGroup
child:
…
No ref?
GC task
thread
107. Heap
fun foo() {
val subview = View(context)
this.viewGroup.addView(subview)
this.viewGroup.removeView(subview)
}
GC
class View
…
Stack
class ViewGroup
child:
…
No ref?
GC task
thread
108. Heap
fun foo() {
val subview = View(context)
this.viewGroup.addView(subview)
this.viewGroup.removeView(subview)
}
GC
Stack
class ViewGroup
child:
… GC task
thread
109. ARC vs GC: ReactiveX
var disposeBag = DisposeBag()
func startCount() {
Observable<Int>.interval(1.0, scheduler: s)
.subscribe(onNext: { print("next: ($0)") })
.addDisposableTo(disposeBag)
}
func stopCount() {
disposeBag = DisposeBag()
}
private val disposables = CompositeDisposable()
fun startCount() {
Observable.interval(0, 1, TimeUnit.SECONDS)
.subscribe { Log.d(TAG, "next: $it") }
.let { disposables.add(it) }
}
fun stopCount() {
disposables.clear()
}
1. disposeBag 변수에 다른 값을 할당
Subscription을 명시적으로 끝내야할 때
110. ARC vs GC: ReactiveX
var disposeBag = DisposeBag()
func startCount() {
Observable<Int>.interval(1.0, scheduler: s)
.subscribe(onNext: { print("next: ($0)") })
.addDisposableTo(disposeBag)
}
func stopCount() {
disposeBag = DisposeBag()
}
private val disposables = CompositeDisposable()
fun startCount() {
Observable.interval(0, 1, TimeUnit.SECONDS)
.subscribe { Log.d(TAG, "next: $it") }
.let { disposables.add(it) }
}
fun stopCount() {
disposables.clear()
}
1. disposeBag 변수에 다른 값을 할당
2. 기존 DisposeBag 객체는 refcount == 0 이 됨
Subscription을 명시적으로 끝내야할 때
111. ARC vs GC: ReactiveX
var disposeBag = DisposeBag()
func startCount() {
Observable<Int>.interval(1.0, scheduler: s)
.subscribe(onNext: { print("next: ($0)") })
.addDisposableTo(disposeBag)
}
func stopCount() {
disposeBag = DisposeBag()
}
private val disposables = CompositeDisposable()
fun startCount() {
Observable.interval(0, 1, TimeUnit.SECONDS)
.subscribe { Log.d(TAG, "next: $it") }
.let { disposables.add(it) }
}
fun stopCount() {
disposables.clear()
}
1. disposeBag 변수에 다른 값을 할당
2. 기존 DisposeBag 객체는 refcount == 0 이 됨
3. 즉시 메모리 해제가 되며 deinit이 불림
Subscription을 명시적으로 끝내야할 때
112. ARC vs GC: ReactiveX
var disposeBag = DisposeBag()
func startCount() {
Observable<Int>.interval(1.0, scheduler: s)
.subscribe(onNext: { print("next: ($0)") })
.addDisposableTo(disposeBag)
}
func stopCount() {
disposeBag = DisposeBag()
}
private val disposables = CompositeDisposable()
fun startCount() {
Observable.interval(0, 1, TimeUnit.SECONDS)
.subscribe { Log.d(TAG, "next: $it") }
.let { disposables.add(it) }
}
fun stopCount() {
disposables.clear()
}
1. disposeBag 변수에 다른 값을 할당
2. 기존 DisposeBag 객체는 refcount == 0 이 됨
3. 즉시 메모리 해제가 되며 deinit이 불림
4. 가지고 있던 구독을 dispose 함
Subscription을 명시적으로 끝내야할 때
public final class DisposeBag: DisposeBase {
…
deinit {
dispose()
}
}
113. ARC vs GC: ReactiveX
var disposeBag = DisposeBag()
func startCount() {
Observable<Int>.interval(1.0, scheduler: s)
.subscribe(onNext: { print("next: ($0)") })
.addDisposableTo(disposeBag)
}
func stopCount() {
disposeBag = DisposeBag()
}
private val disposables = CompositeDisposable()
fun startCount() {
Observable.interval(0, 1, TimeUnit.SECONDS)
.subscribe { Log.d(TAG, "next: $it") }
.let { disposables.add(it) }
}
fun stopCount() {
disposables.clear()
}
1. disposeBag 변수에 다른 값을 할당
2. 기존 DisposeBag 객체는 refcount == 0 이 됨
3. 즉시 메모리 해제가 되며 deinit이 불림
4. 가지고 있던 구독을 dispose 함
GC에서는 언제 해제될지 예측할 수 없다.
Subscription을 명시적으로 끝내야할 때
114. ARC vs GC: ReactiveX
var disposeBag = DisposeBag()
func startCount() {
Observable<Int>.interval(1.0, scheduler: s)
.subscribe(onNext: { print("next: ($0)") })
.addDisposableTo(disposeBag)
}
func stopCount() {
disposeBag = DisposeBag()
}
private val disposables = CompositeDisposable()
fun startCount() {
Observable.interval(0, 1, TimeUnit.SECONDS)
.subscribe { Log.d(TAG, "next: $it") }
.let { disposables.add(it) }
}
fun stopCount() {
disposables.clear()
}
1. disposeBag 변수에 다른 값을 할당
2. 기존 DisposeBag 객체는 refcount == 0 이 됨
3. 즉시 메모리 해제가 되며 deinit이 불림
4. 가지고 있던 구독을 dispose 함
GC에서는 언제 해제될지 예측할 수 없다.
-> 명시적으로 CompositeDisposable 이용
Subscription을 명시적으로 끝내야할 때
115. ARC vs GC: ReactiveX
var disposeBag = DisposeBag()
func startCount() {
Observable<Int>.interval(1.0, scheduler: s)
.subscribe(onNext: { print("next: ($0)") })
.addDisposableTo(disposeBag)
}
func stopCount() {
disposeBag = DisposeBag()
}
private val disposables = CompositeDisposable()
fun startCount() {
Observable.interval(0, 1, TimeUnit.SECONDS)
.subscribe { Log.d(TAG, "next: $it") }
.let { disposables.add(it) }
}
fun stopCount() {
disposables.clear()
}
1. disposeBag 변수에 다른 값을 할당
2. 기존 DisposeBag 객체는 refcount == 0 이 됨
3. 즉시 메모리 해제가 되며 deinit이 불림
4. 가지고 있던 구독을 dispose 함
GC에서는 언제 해제될지 예측할 수 없다.
-> 명시적으로 CompositeDisposable 이용
Subscription을 명시적으로 끝내야할 때
*) RxSwift3에도 CompositeDisposable은 있으나 clear가 없이 dispose만 있다. 재활용이 불가능
116. ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = { self.foo() }
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass {
var lambda: ((Unit) -> Unit)?
init {
lambda = { this.foo() }
}
fun foo() { }
}
var c: MyClass? = MyClass()
c = null
117. ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = { self.foo() }
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass {
var lambda: ((Unit) -> Unit)?
init {
lambda = { this.foo() }
}
fun foo() { }
}
var c: MyClass? = MyClass()
c = null
118. ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = { self.foo() }
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass {
var lambda: ((Unit) -> Unit)?
init {
lambda = { this.foo() }
}
fun foo() { }
}
var c: MyClass? = MyClass()
c = null
순환 참조 발생 -> 런타임 메모리 누수 발생
119. ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = { self.foo() }
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass {
var lambda: ((Unit) -> Unit)?
init {
lambda = { this.foo() }
}
fun foo() { }
}
var c: MyClass? = MyClass()
c = null
순환 참조 발생 -> 런타임 메모리 누수 발생
왜일까?
120. ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = { self.foo() }
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
121. c:
ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = { self.foo() }
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass
refCount: 1
closure:
…
Closure
refCount: 1
“self”:
…
122. c:
ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = { self.foo() }
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass
refCount: 1
closure:
…
Closure
refCount: 1
“self”:
…
123. c:
ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = { self.foo() }
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass
refCount: 2
closure:
…
Closure
refCount: 1
“self”:
…
124. c:
ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = { self.foo() }
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass
refCount: 2
closure:
…
Closure
refCount: 1
“self”:
…
순환 참조 발생
125. c:
ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = { self.foo() }
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass
refCount: 2
closure:
…
Closure
refCount: 1
“self”:
…
126. c:
ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = { self.foo() }
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass
refCount: 1
closure:
…
Closure
refCount: 1
“self”:
…
127. c:
ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = { self.foo() }
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass
refCount: 1
closure:
…
Closure
refCount: 1
“self”:
…
이건 이제 어느 누구도 해제할 수 없다.
-> 메모리 누수!!
128. c:
ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = {[weak self] in self?.foo()}
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass
refCount: 1
closure:
…
Closure
refCount: 1
“self”:
…
129. c:
ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = {[weak self] in self?.foo()}
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass
refCount: 1
closure:
…
Closure
refCount: 1
“self”:
…
130. c:
ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = {[weak self] in self?.foo()}
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass
refCount: 1
closure:
…
Closure
refCount: 1
“self”:
…
weak 참조, refCount 증가시키지 않음
(unowned도 비슷)
131. c:
ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = {[weak self] in self?.foo()}
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass
refCount: 1
closure:
…
Closure
refCount: 1
“self”:
…
132. c:
ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = {[weak self] in self?.foo()}
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass
refCount: 0
closure:
…
Closure
refCount: 1
“self”:
…
133. c:
ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = {[weak self] in self?.foo()}
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
Closure
refCount: 0
“self”: nil
…
134. c:
ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = {[weak self] in self?.foo()}
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
135. ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = {[weak self] in self?.foo()}
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
136. ARC vs GC: 순환 참조
class MyClass {
var lambda: ((Unit) -> Unit)?
init {
lambda = { this.foo() }
}
fun foo() { }
}
var c: MyClass? = MyClass()
c = null
c: class MyClass
lambda
…
Lambda
“self”:
…
137. ARC vs GC: 순환 참조
class MyClass {
var lambda: ((Unit) -> Unit)?
init {
lambda = { this.foo() }
}
fun foo() { }
}
var c: MyClass? = MyClass()
c = null
c: class MyClass
lambda
…
Lambda
“self”:
…
138. ARC vs GC: 순환 참조
class MyClass {
var lambda: ((Unit) -> Unit)?
init {
lambda = { this.foo() }
}
fun foo() { }
}
var c: MyClass? = MyClass()
c = null
c: class MyClass
lambda
…
Lambda
“self”:
…
139. ARC vs GC: 순환 참조
class MyClass {
var lambda: ((Unit) -> Unit)?
init {
lambda = { this.foo() }
}
fun foo() { }
}
var c: MyClass? = MyClass()
c = null
c: class MyClass
lambda
…
Lambda
“self”:
…
No ref?
GC task
thread
140. ARC vs GC: 순환 참조
class MyClass {
var lambda: ((Unit) -> Unit)?
init {
lambda = { this.foo() }
}
fun foo() { }
}
var c: MyClass? = MyClass()
c = null
c: class MyClass
lambda
…
Lambda
“self”:
…
GC task
thread
141. ARC vs GC: 순환 참조
class MyClass {
var lambda: ((Unit) -> Unit)?
init {
lambda = { this.foo() }
}
fun foo() { }
}
var c: MyClass? = MyClass()
c = null
c:
GC task
thread
142. ARC vs GC: 순환 참조
class MyClass {
var lambda: ((Unit) -> Unit)?
init {
lambda = { this.foo() }
}
fun foo() { }
}
var c: MyClass? = MyClass()
c = null
143. ARC vs GC: 순환 참조
class MyClass {
var closure: (() -> Void)?
init() {
closure = {[weak self] in self?.foo()}
}
func foo() { }
}
var c: MyClass? = MyClass()
c = nil
class MyClass {
var lambda: ((Unit) -> Unit)?
init {
lambda = { this.foo() }
}
fun foo() { }
}
var c: MyClass? = MyClass()
c = null
146. switch / when
let index = 100
let validRange = 0...1000
let special: Set<Int> = [100, 101]
switch index {
case 0...10, 20, 30: print("0~10, 20, 30")
case 11, 22, 33: print("11, 22, 33")
case _ where special.contains(index): print("special")
case validRange: print("valid")
default: print("invalid")
}
val index = 11
val validRange = 0..1000
val special = setOf(100, 101)
when (index) {
!in validRange -> println("invalid")
in 0..10, 20, 30 -> println("0~10, 20, 30")
11, 22, 33 -> println("11, 22, 33")
in special -> println("special")
else -> println("valid")
}
공통점: break 필요없음.
주의점:
Swift의 switch에 익숙해지면
다른 언어 (Java, C, C++) switch에서 break 빼먹기 쉬움
-> 컴파일 에러 안남, 버그
147. switch / when
let index = 100
let validRange = 0...1000
let special: Set<Int> = [100, 101]
switch index {
case 0...10, 20, 30: print("0~10, 20, 30")
case 11, 22, 33: print("11, 22, 33")
case _ where special.contains(index): print("special")
case validRange: print("valid")
default: print("invalid")
}
val index = 11
val validRange = 0..1000
val special = setOf(100, 101)
when (index) {
!in validRange -> println("invalid")
in 0..10, 20, 30 -> println("0~10, 20, 30")
11, 22, 33 -> println("11, 22, 33")
in special -> println("special")
else -> println("valid")
}
- enum의 associated value 가능
- tuple 가능
148. switch / when
let index = 100
let validRange = 0...1000
let special: Set<Int> = [100, 101]
switch index {
case 0...10, 20, 30: print("0~10, 20, 30")
case 11, 22, 33: print("11, 22, 33")
case _ where special.contains(index): print("special")
case validRange: print("valid")
default: print("invalid")
}
val index = 11
val validRange = 0..1000
val special = setOf(100, 101)
when (index) {
!in validRange -> println("invalid")
in 0..10, 20, 30 -> println("0~10, 20, 30")
11, 22, 33 -> println("11, 22, 33")
in special -> println("special")
else -> println("valid")
}
- enum의 associated value 가능
- tuple 가능
when {
str.isEmpty() -> println("str empty")
str.length > 100 -> println("str too long")
File(str).exists() -> println("file exists")
else -> println("file not found")
}
if .. else if .. else if … else 의 가독성 개선
150. 제어 구문과 Expression
Expression: 값을 가진 구문
index + 3 "Kotlin $version” str.length + 10 str.isEmpty()
if (count == 0) { "Empty" } else { "Count: $count" } if…else 도 Expression!
151. 제어 구문과 Expression
Expression: 값을 가진 구문
index + 3 "Kotlin $version” str.length + 10 str.isEmpty()
if (count == 0) { "Empty" } else { "Count: $count" }
val str = if (count == 0) "Empty" else "Count: $count"
let str = count == 0 ? "Empty" : "Count: (count)"
if…else 도 Expression!
152. 제어 구문과 Expression
Expression: 값을 가진 구문
index + 3 "Kotlin $version” str.length + 10 str.isEmpty()
if (count == 0) { "Empty" } else { "Count: $count" }
val str = if (count == 0) "Empty" else "Count: $count"
let str = count == 0 ? "Empty" : "Count: (count)"
if…else 도 Expression!
153. 제어 구문과 Expression
val str =
if (count == 0) {
notifyEmpty()
"Empty"
} else {
total++
"Count: $count"
}
맨 마지막 expression이 Block의 값
154. 제어 구문과 Expression
val str =
if (count == 0) {
notifyEmpty()
"Empty"
} else {
total++
"Count: $count"
}
다른 작업을 할 수 있음
155. 제어 구문과 Expression
val str =
if (count == 0) {
notifyEmpty()
"Empty"
} else {
total++
"Count: $count"
}
val message = when {
str.isEmpty() -> "empty"
str.length > 100 -> "too long"
File(str).exists() -> "file exists"
else -> "file not found"
}
when 도 expression
중간의 임시 변수 없이 값을 바로 얻어
변수를 초기화하거나, 함수 인자로 넘길때 편리함
-> 선언형으로 코드를 작성하기 쉬움
156. for 루프
for (int i = 0; i < array.count; i++) {
NSString* str = array[i];
// ...
}
c-style의 전통적인 for 루프
157. for (int i = 0; i < array.count; i++) {
NSString* str = array[i];
// ...
}
for 루프
c-style의 전통적인 for 루프
158. for (int i = 0; i < array.count; i++) {
NSString* str = array[i];
// ...
}
직관적이지 않다더라…
심지어 는 ++도 없앰…
for 루프
c-style의 전통적인 for 루프
159. Iterator와 for 루프
Element
let array = ["Swift", "Kotlin"]
for str in array {
print(str)
}
val array = listOf("Swift", "Kotlin")
for (str in array) {
println(str)
}
index와
Element
for (index, str) in array.enumerated() {
print("(index): (str)")
}
for ((index, str) in array.withIndex()) {
println("$index: $str")
}
index
for i in 0..<100 {
}
for (i in 0 until 100) {
}
160. Iterator와 for 루프
Element
let array = ["Swift", "Kotlin"]
for str in array {
print(str)
}
val array = listOf("Swift", "Kotlin")
for (str in array) {
println(str)
}
index와
Element
for (index, str) in array.enumerated() {
print("(index): (str)")
}
for ((index, str) in array.withIndex()) {
println("$index: $str")
}
index
for i in 0..<100 {
}
for (i in 0 until 100) {
}
161. Iterator와 for 루프
Element
let array = ["Swift", "Kotlin"]
for str in array {
print(str)
}
val array = listOf("Swift", "Kotlin")
for (str in array) {
println(str)
}
index와
Element
for (index, str) in array.enumerated() {
print("(index): (str)")
}
for ((index, str) in array.withIndex()) {
println("$index: $str")
}
index
for i in 0..<100 {
}
for (i in 0 until 100) {
}
162. Iterator와 for 루프
Element
let array = ["Swift", "Kotlin"]
for str in array {
print(str)
}
val array = listOf("Swift", "Kotlin")
for (str in array) {
println(str)
}
index와
Element
for (index, str) in array.enumerated() {
print("(index): (str)")
}
for ((index, str) in array.withIndex()) {
println("$index: $str")
}
index
for i in 0..<100 {
}
for (i in 0 until 100) {
}
163. Iterator와 for 루프
Element
let array = ["Swift", "Kotlin"]
for str in array {
print(str)
}
val array = listOf("Swift", "Kotlin")
for (str in array) {
println(str)
}
index와
Element
for (index, str) in array.enumerated() {
print("(index): (str)")
}
for ((index, str) in array.withIndex()) {
println("$index: $str")
}
index
for i in 0..<100 {
}
for (i in 0...99) {
}
165. 함수
func message(status: Int) -> String {
return “TBD"
}
fun message(status: Int) : String {
return "TBD"
}
fun message(status: Int) : String = "TBD"
func message(status: Int) -> String {
switch (status) {
case 200: return "OK"
case 201...299: return "Success"
case 300...399: return "Redirect"
case 400..<500: return "Client error"
case 500..<600: return "Server error"
default: return “Undefined"
}
}
fun message(status: Int) : String =
when (status) {
200 -> "OK"
in 201..299 -> "Success"
in 300..399 -> "Redirect"
in 400 until 500 -> "Client error"
in 500 until 600 -> "Server error"
else -> "Undefined"
}
166. Named Argument
func insert(name: String, index: Int) {
// check index
storage[index] = name
}
insert(name: "Swift", index: 0)
fun insert(name: String, index: Int) {
// check index
storage[index] = name
}
insert(name = "Kotlin", index = 0)
insert("Kotlin", 0)
insert(index = 0, name = "Kotlin")
func insert(_ name: String, at index: Int) {
// check index
storage[index] = name
}
insert("Swift", at: 0)
함수의 각 파라미터 이름을 붙여 호출할 수 있음
167. 기본값
func insert(name: String, index: Int = 0) {
// check index
storage[index] = name
}
insert(name: "Swift")
fun insert(name: String, index: Int = 0) {
// check index
storage[index] = name
}
insert("Kotlin")
파라미터에 기본값을 줄 수 있음
168. 로컬 함수
로컬 함수
func foo(_ factor: Int) {
func calculate(_ i: Int) -> Int {
return i * factor
}
for i in 1...10 {
print(calculate(i))
}
}
fun foo(factor: Int) {
fun calculate(i: Int) = i * factor
for (i in 1..10) {
println(calculate(i))
}
}
로컬 함수는 로컬 변수에 접근할 수 있음
169. 일급 함수
func length(str: String) -> Int {
return str.characters.count
}
var foo: (String) -> Int
foo = length
print(foo("Swift"))
fun length(str: String): Int = str.length
var foo: (String) -> Int
foo = ::length
println(foo("Kotlin"))
- 함수도 타입이 있고 변수에 할당하고 파라미터로 넘길 수 있다.
170. 일급 함수
func length(str: String) -> Int {
return str.characters.count
}
var foo: (String) -> Int
foo = length
print(foo("Swift"))
foo = { str in Int(str) ?? 0 }
print(foo("Swift"))
fun length(str: String): Int = str.length
var foo: (String) -> Int
foo = ::length
println(foo("Kotlin"))
foo = { str -> str.toIntOrNull() ?: 0 }
println(foo("Kotlin"))
- 함수도 타입이 있고 변수에 할당하고 파라미터로 넘길 수 있다.
- 익명함수를 런타임에 만들 수 있다.
Closure Lambda
171. 함수형 표현
let array = [0, 1, 2, 3]
array.filter { $0 > 0 }
.map { "[index: ($0)]" }
.forEach { print($0) }
val list = listOf(0, 1, 2, 3)
list.filter { it > 0 }
.map { "[index: $it]" }
.forEach { println(it) }
자세한 설명은 세션의 범위를 넘어서므로 생략
보통 Collection 등의 extension에 공통적으로 정의됨