@ WWDC 15
class Temperature {
var celsius: Double = 0
var fahrenheit: Double {
get { return celsius * 9 / 5 + 32 }
set { celsius = (newValue - 32) * 5 / 9 }
}
}
let home = House()
let temp = Temperature()
temp.fahrenheit = 75
home.thermostat.temperature = temp
temp.fahrenheit = 425
home.oven.temperature = temp
home.oven.bake()
home์ temperature์ oven์ temperature๊ฐ ๊ฐ์ ์ธ์คํด์ค๋ฅผ ์ฐธ์กฐํ๊ณ ์๋ค.
sharing์ ๋ง๊ธฐ ์ํด์๋ copy๊ฐ ํ์ํ๋ค.
let home = House()
let temp = Temperature()
temp.fahrenheit = 75
home.thermostat.temperature = temp.copy()
temp.fahrenheit = 425
home.oven.temperature = temp.copy()
home.oven.bake()
class Oven {
var _temperature: Temperature = Temperature(celsius: 0)
var temperature: Temperature {
get { return _temperature }
set { _temperature = newValue.copy() }
}
}
- Cocoa[Touch] requires copying throughout
NSCopying
codifies copying an objectNSString
,NSArray
,NSDictionary
,NSURLRequest
, etc. all require copying
- Defensive copying pervades Cocoa[Touch] and Objective-C
NSDictionary
calls-copy
on its key- Property
copy
attribute provides defensive copying on assignment - ํ์ํ๊ธฐ ๋๋ฌธ์
copy
ํ์ง๋ง ์ฑ๋ฅ์ด ์กฐ๊ธ ๋จ์ด์ง๊ฒ ๋๋ค.
reference semantics์ ๋ฌธ์ ๊ฐ ์๋๋ผ mutation์ ๋ฌธ์ ๊ฐ ์๋๊น? ํ๋ ๊ด์
-
Functional programming languages have reference semantics with immutability
-
Eliminates many problems caused by reference semantics with mutation
- No worries about unintended side effects
-
Several notable disadvantages > Immutability์ ๋จ์ ๋ ์๋ค
- Can lead to awkward interfaces
- Does not map efficiently to the machine model
class Temperature {
let celsius: Double = 0
var fahrenheit: Double { return celsius * 9 / 5 + 32 }
init(celsius: Double) { self.celsius = celsius }
init(fahrenheit: Double) { self.celsius = (fahrenheit - 32) * 5 / 9 }
}
// With mutability
home.oven.temperature.fahrenheit += 10.0
// Without mutability
let temp = home.oven.temperature
home.oven.temperature = Temperature(fahrenheit: temp.fahrenheit + 10.0)
Immutability ๋๋ฌธ์ ์ง๊ด์ ์ด์ง ์์, ์ด์ํ ๋ฐฉ์์ผ๋ก ์ฝ๋๊ฐ ์์ฑ๋ ์ ์๋ค. ์ฌ์ค '='๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋ ๋ค๋ฅธ mutability๋ผ๊ณ ๋ณผ ์๋ ์๋ค.
func primes(n: Int) -> [Int] {
var numbers = [Int](2..<n) // Create an array
for i in 0..<n-2 {
guard let prime = numbers[i] where prime > 0 else { continue }
for multiple in stride(from: 2 * prime-2, to: n-2, by: prime) {
numbers[multiple] = 0
}
}
return numbers.filter { $0 > 0 } // Only prime numbers
}
very simple algorithm!
ํ์ค์ผ ๋ฒ์ ! Immutable!
primes = sieve [2..]
sieve [] = []
sieve (p : xs) = p : sieve [x | x <- xs, x 'mod' p > 0]
Swift ๋ฒ์ !
func sieve(numbers: [Int]) -> [Int] {
if numbers.isEmpty { return [] }
let p = numbers[0]
return [p] + sieve(numbers[1..<numbers.count].filter { $0 % p > 0 })
}
The Genuine Sieve of Eratosthenes
์ด non-mutating version์ mutating version์ ๋นํด ํจ์จ์ ์ด์ง ๋ชปํ๋ค.
-
Cocoa[Touch] has a number of immutable classes
NSDate
,NSURL
,UIImage
,NSNumber
, etc.- Improved safety (no need to use copy)
-
Downsides to immutability
NSURL *url = [[NSURL alloc] initWithString: NSHomeDirectory()]; NSString *component; while (component == getNextSubdir()) { url = [url URLByAppendingPathComponent: component]; }
๊ณ์ object๋ฅผ ์์ฑํ๋ ์ด ๋ฐฉ์์ ๋นํจ์จ์ ์ด๋ค.
NSArray<NSString *> *array = [NSArray arrayWithObject: NSHomeDirectory()]; NSString *component; while (component == getNextSubdir()) { array = [array arrayByAddingObject: component]; } url = [NSURL fileURLWithPathComponents: array];
-
Thoughtful mutability
NSMutableArray<NSString *> *array = [NSMutableArray array]; NSString *component; while (component == getNextSubdir()) { array = [array addObject: component]; } url = [NSURL fileURLWithPathComponents: array];
mutability์ ์ฅ์ ๋ ์๋ค. ๊ฑฑ์ ๋๋ ๊ฒ์ sharing! ๊ทธ๋์ value semantics๋ฅผ ์ฌ์ฉํ๋ค.
-
Mutating one variable of some value type will never affect a different variable
var a: Int = 17 var b = a assert(a == b) b += 25 print("a = \(a), b = \(b)") // a = 17, b = 42
- ์ค์ํํธ์ ๋ชจ๋ ๊ธฐ๋ณธ(fundamental) ํ์
์ value ํ์
์ด๋ค.
- Int, Double, String, ...
- ์ค์ํํธ์ ๋ชจ๋ collection์ value ํ์
์ด๋ค.
- Array, Set, Dictionary, ...
- ์ค์ํํธ์ tuple, struct, enum ๊ฐ์ value ํ์ ์ ํฌํจํ๋ ๊ฒ๋ค๋ value ํ์ ์ด๋ค.
- Value ํ์
์ ๊ฐ์ ์ํด ๊ตฌ๋ถ๋๋ค.
- Identity ๊ตฌ๋ถํ ์ ์๋ค.
- ์ด๋ป๊ฒ ๊ฐ์ ๊ฐ์ง๋ ์ง๋ ์ค์ํ ํฌ์ธํธ๊ฐ ์๋๋ค.
Value ํ์ ์ Equatable์ ๊ตฌํํด์ผ ํ๋ค.
protocol Equatable {
/// Reflexive - `x == x` is `true`
/// Symmetric - `x == y` then `y == x`
/// Transitive - `x == y` and `y == z` then `x == z`
func ==(lhs: Self, rhs: Self) -> Bool
}
var home = House()
var temp = Temperature()
temp.fahrenheit = 75
home.thermostat.temperature = temp
temp.fahrenheit = 425
home.oven.temperature = temp
home.oven.bake()
But not when you don't
let
์ ๊ฐ์ด ์ ๋ ๋ฐ๋์ง ์์์ ์๋ฏธ
let numbers = [1, 2, 3, 4, 5]
var
์ ๋ค๋ฅธ ๊ฐ๋ค์๋ ์ํฅ์ ์ฃผ์ง ์์ผ๋ฉด์ ๊ฐ์ ์
๋ฐ์ดํธํ ์ ์์์ ์๋ฏธ
var strings = [String]()
for x in numbers {
strings.append(String(x))
}
var numbers = [1, 2, 3, 4, 5]
schedule.processNumbersAsynchronously(numbers)
for i in 0..<numbers.count { numbers[i] = numbers[i] * i }
schedule.processNumbersAsynchronously(numbers)
Reference semantics๋ฅผ ๊ฐ์ก์๋๋ผ๋ฉด numbers๋ Race condition์ ๊ฒช๊ฒ ๋์์ ๊ฒ์ด๋ค.
Constant time
- Copying a low-level, fundamental type is constant time
- Int, Double, ...
- Copying a struct, enum, or tuple of value types is constant time
- CGPoint, ...
- Extensible data structures use copy-on-write
- Copying involves a fixed number of reference-counting operations
- String, Array, Set, Dictionary, ...
-
Value types generally used for "primitive" data of objects
class Button: Control { var label: String // primitive data var enabled: Bool // primitive data }
-
Value ํ์ ์ ๋ณต์ฌํ๋ฉด reference๋ฅผ ๊ณต์ ํ๋ค.
struct ButtonWrapper { var button: Button // ButtonWrapper๋ฅผ ๋ณต์ฌํ ๊ฐ์ฒด๋ ์ด button์ ๊ณต์ ํ๋ค. }
-
Value semantics๋ฅผ ์ ์งํ๋ ค๋ฉด ํน๋ณํ ๊ณ ๋ ค๊ฐ ์์ด์ผ ํ๋ค.
- Referened object์ mutation์ ์ด๋ป๊ฒ ๊ด๋ฆฌํ ์ง
- Reference์ identity๊ฐ equality์ ์ด๋ค ์ํฅ์ ๋ฏธ์น ์ง
- Referenced object์ ๋ํ ์ ํ๋์ง ์์ mutation์ value semantics๋ฅผ ๋ง๊ฐ๋จ๋ฆฐ๋ค
- Mutating ์ํค๋ ์์
์ non-mutating ์์
๋ค๋ก ๋ถ๋ฆฌํ๋ค
- Non-mutating ์์ ์ ํญ์ ์์ ํ๋ค.
- Mutating operations must first copy
struct BezierPath: Drawable {
private var _path = UIBezierPath()
var pathForReading: UIBezierPath {
return _path
}
var pathForWriting: UIBezierPath {
mutating get {
_path = _path.copy() as! UIBezierPath
return _path
}
}
}
extension BezierPath {
var isEmpty: Bool {
return pathForReading.empty
}
mutating func addLineToPoint(point: CGPoint) {
pathForWriting.addLineToPoint(point)
}
}
struct MyWrapper {
var _object: SomeSwiftObject
var objectForWriting: SomeSwiftObject {
mutating get {
if !isUniquelyReferencedNonObjC(&_object) {
_object = _object.copy()
}
return _object
}
}
}
- Reference semantics and unexpected mutation
- Value semantics solve these problems
- Expressiveness of mutability, safety of immutability