๊ธ€ ์ž‘์„ฑ์ž: ๊ฐœ๋ฐœํ•˜๋Š” ํ›ˆ์ด

์ผ๋‹จ ์ •๋ฆฌ.. Static Dispatch, Dynamic Dispatch

์ง€๋‚œ ํฌ์ŠคํŠธ์—์„œ Static Dispatch์™€ Dynamic Dispatch๋ฅผ ์ •๋ฆฌํ•ด๋ณด์•˜๋Š”๋ฐ์š”, Static Dispatch๋Š” ์ธ์Šคํ„ด์Šค์—์„œ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ์‹ค์ œ๋กœ ์–ด๋–ค ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋ ์ง€ ์ปดํŒŒ์ผ ํƒ€์ž„์— ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Dynamic Dispatch๋Š” ๋Ÿฐํƒ€์ž„์— vtable์ด๋ผ๋Š” ํ…Œ์ด๋ธ”์— ์‹ค์ œ ์‹คํ–‰ํ•  ๋ฉ”์„œ๋“œ์˜ ์ฃผ์†Œ๋ฅผ ์ฐพ์•„ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒฝ์šฐ์˜€์Šต๋‹ˆ๋‹ค.

 

๋Ÿฐํƒ€์ž„์— ํ…Œ์ด๋ธ”์„ ๋ฃฉ์—…ํ•ด์•ผํ•˜๋Š” ์˜ค๋ฒ„ํ—ค๋“œ ๋•Œ๋ฌธ์— Dynamic Dispatch๋Š” ์„ฑ๋Šฅ์—๋„ ๋ถ€์ •์ ์ธ ์˜ํ–ฅ์„ ์ค€๋‹ค๋Š” ๊ฒƒ๋„ ์งš์–ด๋ดค์—ˆ์ฃ . ํ•˜์ง€๋งŒ ์ƒ์†๊ณผ ๋‹คํ˜•์„ฑ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Dynamic Dispatch์˜ ์‚ฌ์šฉ์ด ํ•„์ˆ˜์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค. 

 

๊ฒฐ๊ตญ ์ง€๋‚œ ํฌ์ŠคํŠธ์—์„œ๋Š” ๊ตฌ์กฐ์ฒด, ์—ด๊ฑฐํ˜• ๋“ฑ, ๊ฐ’ ํƒ€์ž… ์ธ์Šคํ„ด์Šค๋“ค์€ Static Dispatch, ์ฐธ์กฐ ํƒ€์ž… ์ธ์Šคํ„ด์Šค์ธ ํด๋ž˜์Šค๋Š” Dynamic Dispatch๋กœ ๊ฒฐ๋ก ์ด ๋‚ฌ์—ˆ๋Š”๋ฐ์š”, ์ข€ ๋” ๋ณต์žกํ•œ ์ƒํ™ฉ์„ ๊ณ ๋ฏผํ•ด๋ด…์‹œ๋‹ค.

 

์˜๋ฌธ์  1: ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜๋Š” ๊ตฌ์กฐ์ฒด๋Š” ์–ด์ฉ”๊ฑด๋ฐ?

์Šค์œ„ํ”„ํŠธ์— ์ง์ ‘์ ์ธ ์ƒ์†์€ ํด๋ž˜์Šค๋งŒ์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๊ตฌ์กฐ์ฒด๋กœ๋„ ์ƒ์†์˜ ๊ฐœ๋…์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋ฐ”๋กœ ํ”„๋กœํ† ์ฝœ์˜ ์‚ฌ์šฉ์ด์ฃ .

class Test {
    func printTest() {
        print("test")
    }
}

class TestA: Test {
    override func printTest() {
        print("testA")
    }
}

class TestB: Test {
    override func printTest() {
        print("testB")
    }
}

let tests = [Test(), TestA(), TestB()]

for test in tests {
    print(type(of: test))
    test.printTest()
}

์ง€๋‚œ ํฌ์ŠคํŠธ์—์„œ ์‚ฌ์šฉํ–ˆ๋˜ ์ด ํด๋ž˜์Šค๋“ค์„ ๊ตฌ์กฐ์ฒด์™€ ํ”„๋กœํ† ์ฝœ๋กœ ๋ฐ”๊ฟ”๋ณผ๊ฒŒ์š”!

 

protocol TestPrintable {
    func printTest()
}

struct Test: TestPrintable {
    func printTest() {
        print("test")
    }
}

struct TestA: TestPrintable {
    func printTest() {
        print("testA")
    }
}

struct TestB: TestPrintable {
    func printTest() {
        print("testB")
    }
}

let tests: [TestPrintable] = [Test(), TestA(), TestB()]

for test in tests {
    test.printTest()
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋™์ผํ•œ ์ถœ๋ ฅ์ด ๋‚˜์˜ค๊ฒ ์ฃ ? ์ด์ œ tests ๋ฐฐ์—ด์˜ ์š”์†Œ ํƒ€์ž…์€ ํ”„๋กœํ† ์ฝœ์˜ ํƒ€์ž…์ธ TestPrintable์ด ๋˜๊ฒ ๋„ค์š”.

 

์ž, ๊ทธ๋Ÿผ ๋‹ค์‹œ ์˜๋ฌธ์ ์ด ์ƒ๊น๋‹ˆ๋‹ค. ํ”„๋กœํ† ์ฝœ๋งŒ ๋ณด๊ณ  ์‹ค์ œ ๊ตฌํ˜„์ฒด๊ฐ€ ๋ญ”์ง€ ์–ด๋–ป๊ฒŒ ์•Œ์•„..?

 

์ปดํŒŒ์ผ ํƒ€์ž„์—๋Š” TestPrintable์˜ ์„ ์–ธ๋งŒ ์•Œ๊ณ ์žˆ๊ณ , ์ •์˜๋ฅผ ์ฐพ์•„๊ฐ€๋ ค๋ฉด ๊ฐ ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ฐ€์ง„ ๋ฉ”์„œ๋“œ ์ •์˜๋ฅผ ์ฐพ์•„๊ฐ€์•ผ๊ฒ ์ฃ ? ๊ฒฐ๊ตญ ์ด๋ฒˆ์—๋„ Dynamic Dispatch๊ฐ€ ๋˜๊ฒ ๋„ค์š”.

 

ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•œ ๊ตฌ์กฐ์ฒด์˜ Dynamic Dispatch

๊ตฌ์กฐ์ฒด๋„ vtable์„ ์“ธ ๊ฒƒ ๊ฐ™์ง€๋งŒ, ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ตฌ์กฐ์ฒด๋Š” ๊ณตํ†ต์กฐ์ƒ์ธ ์ธ์Šคํ„ด์Šค๊ฐ€ ์—†์œผ๋‹ˆ๊นŒ์š”.

 

๊ทธ๋ž˜์„œ ์ด๋ฒˆ์—๋Š” ์ƒˆ๋กœ์šด ํ…Œ์ด๋ธ”์ธ Protocol Witness Table(PWT)์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

 

Protocol Witness Table์€ ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜๋Š” ๊ฐ ๊ตฌ์กฐ์ฒด ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•˜๋‚˜์”ฉ ๊ฐ€์ง€๊ณ , ํŠน์ •ํ•œ ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ ์‹ค์ œ ๊ตฌํ˜„์„ ์ด ํ…Œ์ด๋ธ”์— ์—ฐ๊ฒฐ์‹œ์ผœ๋‘ก๋‹ˆ๋‹ค. 

WWDC์—์„œ ๋ฐœํ‘œํ•œ ์ž๋ฃŒ์—์„œ๋Š” ์ด๋ ‡๊ฒŒ ํ‘œํ˜„ํ•˜๊ณ  ์žˆ์–ด์š”. ๋ฐฐ์—ด์— ๋‹ด๊ธด ์ธ์Šคํ„ด์Šค๋“ค์ด ๋‚ด๋ถ€์— PTW๋ฅผ ๊ฐ€์ง€๊ณ  ์—ฌ๊ธฐ์— ์‹ค์ œ ๊ตฌํ˜„์— ๋Œ€ํ•œ ๋งํฌ๊ฐ€ ๋งŒ๋“ค์–ด์ ธ ์žˆ๋Š” ๊ฒƒ์ด์ฃ .

 

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋Ÿฐํƒ€์ž„์— ๋ฐฐ์—ด ์š”์†Œ๊ฐ€ ๊ฐ€์ง„ ํ…Œ์ด๋ธ”์„ ํ•œ ๋ฒˆ ์ฝ์–ด์„œ ์ฝ”๋“œ๋ฅผ ์ฐพ์•„๊ฐ€์•ผํ•˜๋‹ˆ Dynamic Dispatch๊ฐ€ ๋˜๊ฒ ์ฃ !

 

์˜๋ฌธ์  2: ํ”„๋กœํ† ์ฝœ์„ Extension ํ•ด์„œ ๋ฉ”์„œ๋“œ๊ฐ€ ์ •์˜๋˜์–ด ์žˆ๋‹ค๋ฉด?

์ด๋Ÿฐ ๊ฒฝ์šฐ๋Š” ์–ด๋–จ๊นŒ์š”?

protocol TestPrintable {
}

extension TestPrintable {
    func printTest() {
        print("hello")
    }
}

struct Test: TestPrintable {
    func printTest() {
        print("test")
    }
}

struct TestA: TestPrintable {
    func printTest() {
        print("testA")
    }
}

struct TestB: TestPrintable {
    func printTest() {
        print("testB")
    }
}

let tests: [TestPrintable] = [Test(), TestA(), TestB()]

for test in tests {
    test.printTest()
}

ํ”„๋กœํ† ์ฝœ์„ extensionํ•ด์„œ ๊ธฐ๋ณธ ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•ด๋‘๊ณ  ์‹คํ–‰์„ ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

 

๊ฒฐ๊ณผ๋Š”,

hello
hello
hello

์ด๋ ‡๊ฒŒ ๊ธฐ๋ณธ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๋ฐฐ์—ด์˜ ์š”์†Œ ํƒ€์ž…์ด TestPrintable์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด ํƒ€์ž…์— ๋Œ€ํ•œ ๋ฉ”์„œ๋“œ๊ฐ€ ๊ณง๋ฐ”๋กœ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์ด์ฃ . ๊ฐ ์ธ์Šคํ„ด์Šค์— ์ •์˜๋œ printTest๋Š” ํ”„๋กœํ† ์ฝœ๋กœ๋ถ€ํ„ฐ ์ฑ„ํƒ๋ฐ›์€ ๋ฉ”์„œ๋“œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

 

์‹ค์ œ๋กœ XCode์—์„œ ์ž๋™์™„์„ฑ๋„ ์ง€์›ํ•ด์ฃผ์ง€ ์•Š๋„ค์š”ใ…Žใ…Ž

 

์›๋ž˜๋Š” ์ด๋ ‡๊ฒŒ ํ•ด์ค˜์•ผํ•˜๋Š”๋ฐ ๋ง์ด์ฃ ..

 

์ฆ‰, ์ด๋Ÿฐ ๊ฒฝ์šฐ๋Š” ์–ธ์ œ๋‚˜ ํ”„๋กœํ† ์ฝœ์˜ ๊ธฐ๋ณธ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” Static Dispatch๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

 

์˜๋ฌธ์  3: ํ”„๋กœํ† ์ฝœ์— ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์žˆ๊ณ , ๊ธฐ๋ณธ ๋ฉ”์„œ๋“œ๊ฐ€ Extension์œผ๋กœ ์ •์˜๋œ ๊ฒฝ์šฐ๋Š”?

์•ž์„  ์˜ˆ์‹œ์—์„œ ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์ถ”๊ฐ€ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

protocol TestPrintable {
    func printTest()
}

extension TestPrintable {
    func printTest() {
        print("hello")
    }
}

struct Test: TestPrintable {
    func printTest() {
        print("test")
    }
}

struct TestA: TestPrintable {
    func printTest() {
        print("testA")
    }
}

struct TestB: TestPrintable {
    func printTest() {
        print("testB")
    }
}

let tests: [TestPrintable] = [Test(), TestA(), TestB()]

for test in tests {
    test.printTest()
}

์ด๋ฒˆ์—๋Š” ํ”„๋กœํ† ์ฝœ์— ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์กด์žฌํ•˜๊ณ , ์ด๋ฅผ ์ฑ„ํƒํ•˜๋Š” ๊ตฌ์กฐ์ฒด์—์„œ ๊ฐ๊ฐ ์ž์‹ ์˜ ๊ตฌํ˜„์ฒด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ์— ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๋ฉด ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ๋‚˜์˜ค๋Š”๋ฐ์š”,

test
testA
testB

์ด๋ฒˆ์—๋Š” ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•œ ์ธ์Šคํ„ด์Šค๋“ค์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๊ฐ ๋ฉ”์„œ๋“œ๋“ค์ด ํ”„๋กœํ† ์ฝœ์„ ๋ฐ›์€ ๊ตฌ์กฐ์ฒด์—์„œ ์ •์˜๋˜๊ณ  ์žˆ๋Š” ์ƒํ™ฉ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฐ ๊ฒฝ์šฐ๋Š” ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•œ ๊ตฌ์กฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ์šฐ์„ ์ ์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

 

๋”ฐ๋ผ์„œ ์ด๋Ÿฐ ๊ฒฝ์šฐ๋Š” Dynamic Dispatch ์ž…๋‹ˆ๋‹ค.

 

์˜๋ฌธ์  4: ํด๋ž˜์Šค์˜ Extension์€?

์ด๋ฒˆ์—” ํด๋ž˜์Šค๋ฅผ Extensionํ•˜๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค.

class Test {
    func printTest() {
        print("test")
    }
}

extension Test {
    func printExtension() {
        print("extension")
    }
}

class TestA: Test {
    override func printTest() {
        print("testA")
    }
    override func printExtension() {
        print("extensionA")
    }
}

class TestB: Test {
    override func printTest() {
        print("testB")
    }
}

let tests = [Test(), TestA(), TestB()]

for test in tests {
    test.printExtension()
}

์ตœ์ƒ์œ„ ํด๋ž˜์Šค์˜ Extension์— ์ƒˆ๋กœ์šด ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€ํ•˜๊ณ  TestA์—์„œ ์˜ค๋ฒ„๋ผ์ด๋”ฉ์„ ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ์—๋Ÿฌ๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค.. Extension์œผ๋กœ ๋งŒ๋“  ๋ฉ”์„œ๋“œ๋Š” ์˜ค๋ฒ„๋ผ์ด๋”ฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํ•˜๋„ค์š”.. Objective-C ๋Ÿฐํƒ€์ž„์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. 

 

๋งŒ์•ฝ Objective-C ๋Ÿฐํƒ€์ž„์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ํ•ญ์ƒ ์ตœ์ƒ์œ„ ํด๋ž˜์Šค์˜ ๋ฉ”์„œ๋“œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ Static Dispatch ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

 

์ •๋ฆฌ

https://www.rightpoint.com/rplabs/switch-method-dispatch-table

์ด ๊ธ€์— ํ‘œ๋กœ ์ž˜ ์ •๋ฆฌ๋˜์–ด ์žˆ์–ด์„œ ๊ทธ๋Œ€๋กœ ๊ฐ€์ ธ์™€๋ด…๋‹ˆ๋‹ค!

  • 1) ๊ฐ’ ํƒ€์ž…: ์ˆœ์ˆ˜ํ•œ ๊ฐ’ ํƒ€์ž…, ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜์ง€ ์•Š๋Š” ๊ตฌ์กฐ์ฒด๋‚˜ enum์€ ํ•ญ์ƒ Static Dispatch์ž…๋‹ˆ๋‹ค. ์–ด๋–ค ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜์ง€๋Š” ๋„ˆ๋ฌด๋‚˜ ๋ช…ํ™•ํ•˜๋‹ˆ๊นŒ์š”!
  • 2) ํ”„๋กœํ† ์ฝœ: ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜๋Š” ํƒ€์ž…๋“ค์€ ๊ธฐ๋ณธ์ ์œผ๋กœ Dynamic Dispatch์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์‚ฌ์šฉํ•˜๋Š” ํ…Œ์ด๋ธ”์€ Protocol Witness Table ์ด๊ตฌ์š”. ํ•˜์ง€๋งŒ Protocol์˜ ์ธํ„ฐํŽ˜์ด์Šค์— ํฌํ•จ๋˜์ง€ ์•Š๋Š” ๊ธฐ๋ณธ ๋ฉ”์„œ๋“œ๋“ค์€ ํ•ญ์ƒ ๊ธฐ๋ณธ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๊ธฐ ๋•Œ๋ฌธ์— Static Dispatch ์ž…๋‹ˆ๋‹ค.
  • 3) ํด๋ž˜์Šค: ํด๋ž˜์Šค๋Š” ๋‹คํ˜•์„ฑ๊ณผ ์ƒ์† ๋•Œ๋ฌธ์— ๊ธฐ๋ณธ์ ์œผ๋กœ Dynamic Dispatch์ด๊ณ , vtable์„ ์ด์šฉํ•ด์„œ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ํด๋ž˜์Šค์˜ extension์œผ๋กœ ์ •์˜๋œ ๋ฉ”์„œ๋“œ๋Š” Objective-C ๋Ÿฐํƒ€์ž„์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— Static Dispatch ์ž…๋‹ˆ๋‹ค.

Reference