본문 바로가기

면접질문정리/Swift문법

[Swift] Protocol Oriented Programming

Protocol oriented Programming

  • 스위프트의 핵심 패러다임 중 하나

예전에 옵젝씨에서

  • 단지기능의 청사진 역할
  • 주로 Delegate, DataSource

현재

  • protocol extension이 가능해졌다.
    특정타입이 할일을 명시해주는 동시에 역할을 해주는 것까지 가능해짐
// MARK: 프로토콜 기본구현

protocol LayoutDrawable {
	func drawsomeLayout()
}

class MyView: UIVuew, LayoutDrawable {
	
}

extension LayoutDrawable {
	func drawsomeLayout() {
		//...
	}
}

 

이게무슨장점이있는지 ??

저 기본구현을 어떤 클래스나 구조체 열거형이라도 프로토콜을 명시해주면 프로토콜에 있던 기능을 모두 쓸수 있따는게 장점입니다.

왜프로토콜이 탄생했나 ?

  • 상속의 한계
    • 서로다른 클래스에서 상속받은 클래스는 동일한 기능을 구현하기 위해 중복코드 발생
  • 카테고리의 한계 및 부작용
    • 프로퍼티 추가 불가
    • 오직 클래스에만 적용가능
    • 기존 메서드를(자신도 모르게) 오버라이드 가능
  • 참조타입의 한계
    • 동적 할당과 참조 카운팅에 많은 자원소모(의도치않게 값이 변경되며 할당과 해제 그리고 reference Counting함에 있어서 메모리자원을 많이 소모할 수 있다라는 아이디어에서 출발을 한것)
----protocol-----
class Person: Runable, Talkable, Swimable {}
class Bird: Flyable {}
struct Frog: Runable, Swimable {}
struct Turtle: Swimable {}
struct Fish: Swimable {}
 

이런 기능을 사용할 수 있다라는걸 명시해주는거지 원래는 내부에 구현을 해줘야하는데 이걸 extension으로 구현해줄 수 있는것이다.

그래서 이걸 어떻게 써먹으라고 ?

POP & Value in Protject
Social Media

기획자: (뚜벅뚜벅 걸어오는발걸음 소리) “테이블 뷰 형식의 타임라인이 필요해요!”

쿼카: ㅇㅋ 뭐…고정정도야뭐… 뚞딲만들어드림 ㄱㄷ

만국박람회처럼 하나의 이미지와 label이 cell담겨 주루루룩 스크롤이가능한
화면이고 하나의 TimeLine을 누르면 DetailView로 넘어가는 구조이다.

class TimeLineTableViewCell: UITableViewCell {
	var mediaImageView: UIImageView
	var note: UILabel
	var content: NSDictionary
}
class DetailViewController: UIViewController {
	var mediaImageView: UIImageView
	var note: UIlabel
	var content: NSDictionary
}
class TimelineTableViewController: UITableViewController {
	var contents: [NSDictionary]
}
 

기획자: “쿼카! 정말 간단한 기능이 더필요해요! 사진첩처럼 볼 수 있는 모드로 추가할 수 있어여 ??”

쿼카: “(음…한번에 말씀합시다요…한번만 봐줌) 예”

무엇부터 건드리져 >?

  • Content Model
class Content {
	var URLString: String
	var note: String
} 
 

음… 근데 class는 메모리를 마이 잡아먹을 수 잇으니 구조체로 하는게 좋을것같음

struct Content {
	var URLString: String
	var note: String
}
 
  • Model Propetry
class TimelineTableViewController: UITableViewController, ContainContents {}
class TimelineCollectionViewController: UITableViewController, ContainContents {}

protocol ContainContents {
	var contents: [Content] { get }
}
class TimelineContentObject {
	static let shared = TimelineContentObject()
	var contents: [Content] = [Content]()
}

extension ContainContents {
	var contents: [Content] {
		return TimelineContentObject.shared.contents
	}
}
 

두개의 VC이 같은 데이터를 가질 수 있게 Sington object를 만들어 shared할 수 있게 해줌

출처 - https://www.youtube.com/watch?v=9gkzHUsQiUc

그래서 위 사진과같이 두 뷰컨이 하나의 모델의 데이터를 사용하는 구조임

  • View
    [After]
protocol MediaContainer: class {
	var content: Content? { get set }
	var media: UIImageView { get }
	var note: UILabel { get set }
	
	func contentChanged()
}

extension MediaContainer {
	func contentChanged() {
		//
	}
}
 
class TimelineTableViewCell: UITableViewCell, MediaContainer {
	var media: UIImageView
	var note: UILabel
	var content: Content? {
		didSet {
			contentChanged()
		}
	}
}
class TimelineCollectionViewCell: UICollectionViewCell, MediaContainer {
	var media: UIImageView
	var note: UILabel
	var content: Content? {
		didSet {
			contentChanged()
		}
	}
}

class DetailViewController: UIViewController, MediaContainter {
	var media: UIImageView
	var note: UILabel
	var conetnt: Content? {
		didSet {
			contentChnaged()
		}
	}
}
 

TableViewCell, CollectionViewCell, detailViewController, 같은 기능 같은 역할을 수행할 수 있도록 될 수 있다.

  • Contoller
    TableVC, CollectionVC는 다음 화면으로 넘어가는 기능을 가지고 있다.
    이를 protocol로 extension으로 기본구현을 만들어주면 어떨까 ?
protocol CanCHowDetailView {
	func showDetailView(withContent content: Content)
	var navigationController: UINavigationController? { get }
}

extension CanShowDetailView {
	func showDetailView(withContent content: Content) {
		//..
	}
}
 

push를 통해 다음화면으로 넘어가기위한 NavigationController property를 가지고 있어야하고 넘어갔을때 화면에 뷰를 띄어줘야하기때문에 showDetailView를 가지는 protocol을 정의한다.

class TimelineTableViewController: UITableViewController, ContainContents, CanShowDetailView {
	...
}
class TimelineControllerViewController: UITableViewController, ContainContents, CanShowDetailView {
	..
}
 

프로토콜을 만든것을 적용 시켜주는것이다!!
휴… 드디어끝낸것같다… 좀 쉬어볼까…
.
.
.
기획자: (뚜벅뚜벅뚜벅 걸어오는소리) “쿼카씨! 이거 진짜 간단한건데요! 사진말고 영상도 보여줄 수 없을까요 ??? __”

쿼카: (예? 명치한데 쳐달라구요?) 예?

  • 기존에 TableViewCell, ScrollView의 ImageView가 Image와 Video둘다 가능해야하게 만들어야한다…
  • protocol
protocol ContnetPresentable: class, Layout {
	var frame: CGRect { get set }
	var canPresentContent: Bool { set }
}

extension ContentPresentable {
	var canPresentContent: Bool {
return true
	}
}

extension UIImageView: ContentPresentable {}
extension AVPlayerLayer: ContentPresentable {}
 
  • Protocol/Model

[Before]

struct Content {
	var URLString: String
	var note: String
}

protocol MediaContainer: class {
	var content: Content? { get set }
	var media: UIImageView { get }
	var note: UILabel { get set }
	
	func contentChanged()
}

extension MediaContainer {
	func contentChanged() {
		//
	}
}
 

[After]

struct Content {
	*enum MediaType {
		case image, video
	}
	var type: Content.MediaType*
	var URLString: String
	var note: String
}

protocol MediaContainer: class {
	var content: Content? { get set }
	var media: *ContentPresentblae { get }*
	var note: UILabel { get set }
	
	*var videoLayer: AVPlayerLayer { get }
	var mediaImageView: UIImageView { get }*
	
	func contentChanged()
}

extension MediaContainer {
	func contentChanged() {
		//
	}
	*var media: ContentPresentable {
		switch content!.type {
			case .image: 
				return mediaImageView
			case .video:
				return videoLayer
		}
	}*
}
 

=> 영상을 누르면 영상이, 이미지를 누르면 이미지가 나오게된다.

모델 protocol과 struct를 바꿔주는것만으로도 기능을 추가할 수 있었따.

그렇다면 어떤 이점을 얻었나 ??

  • 범용적인 사용
    • 클래스, 구조체, 열거형 등등 모든 타입에 적용 가능
    • 제네릭과 결합하면 더욱 파급적인 효과(=원하는 타입만 들어올 수 있게 해준다고 정해주려면 제네릭을 사용하면된다.)
      (Type safe & Flexible code)
  • 상속의 한계 극복
    • 특정 상속 체계에 중속되지않음
    • 프레임워크에 종속적이지않게 재활용 가능
  • 적은 시스템 비용
    • Reference type cost > Value type cost
  • 용이한 테스트
    • GUI코드 없이도 수월한 테스트

그렇다면 한계점은 어떤게 있나 ??

  • Objective-C Protocol 코드에 Swift Extension 을 붙이게되면 기본 구현부를 작성할 수 가없다.
  • 고로 자주 사용되는 Delegate, DataSource 등 프레임워크 프로토콜에 기본 구현 불가
    (코코아 터치 코코아 프레임워크는 아직 옵젝씨 DataDelegate, DataSource, TableViewDataSource, TextFiledDelegate등등)
extension UITextFieldDelegate {
	func textFieldShouldREturn(textField: UItextField) -> Bool {
		textField.endEditing(true)
		return true
	}
}

extension UITableViewDelegate {
	func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
		tableView.deselectRowAtIndexPAth(indexPath, anumated: true)
	}
}
 

향후 이런 단점을 애플이 보안해준다면 정말 좋을 것…

정리

  • Value Type을 사용하여 성능상의 이득을 취하자
  • Protocol + Extension + Generic은 환상의 조합이다.
  • 이제 상속을 통한 수직 확장이 아닌 Protocol과 Extension을 통한 수평확장과 기능추가를 고민해볼때!..

Reference

  • https://youtu.be/9gkzHUsQiUc
  • POP in swift WWDC15 408
  • Building Better Apps with Value Types in Swift WWDC15 414
  • Protocol and Value Oriented Programming in UIkit Apps WWDC16, 419
  • Let.Swift Session
    • 스위프트 Internals 김정님
    • 스위프트 퍼포먼스 이해하기 유용하님