Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
面向协议编程与 Cocoa 的邂逅
Search
Wei Wang
September 24, 2016
Technology
14
4.7k
面向协议编程与 Cocoa 的邂逅
My talk in MDCC 16. About using Protocol-Oriented Programming language in daily Cocoa life.
Wei Wang
September 24, 2016
Tweet
Share
More Decks by Wei Wang
See All by Wei Wang
網路之難,難於上青天 - iPlayground 2019
onevcat
11
4.9k
GMTC 2019 - 在分歧中发展,2019 我们能用 Swift 做什么
onevcat
0
930
从 Swift 到机器学习
onevcat
2
960
iOS Dev - The Dark Side
onevcat
0
120
Swift 3 - From Expert to Beginner
onevcat
2
210
如何打造一个让人愉快的框架
onevcat
4
22k
JSPatch Introduction
onevcat
0
180
Objective-C Runtime Swizzle
onevcat
0
180
Unity Memory
onevcat
0
130
Other Decks in Technology
See All in Technology
AndroidデバイスにFTPサーバを建立する
e10dokup
0
250
Building Products in the LLM Era
ymatsuwitter
10
5.4k
リーダブルテストコード 〜メンテナンスしやすい テストコードを作成する方法を考える〜 #DevSumi #DevSumiB / Readable test code
nihonbuson
11
7.2k
CZII - CryoET Object Identification 参加振り返り・解法共有
tattaka
0
350
PHPカンファレンス名古屋-テックリードの経験から学んだ設計の教訓
hayatokudou
0
220
Helm , Kustomize に代わる !? 次世代 k8s パッケージマネージャー Glasskube 入門 / glasskube-entry
parupappa2929
0
250
2/18/25: Java meets AI: Build LLM-Powered Apps with LangChain4j
edeandrea
PRO
0
110
オブザーバビリティの観点でみるAWS / AWS from observability perspective
ymotongpoo
8
1.5k
エンジニアのためのドキュメント力基礎講座〜構造化思考から始めよう〜(2025/02/15jbug広島#15発表資料)
yasuoyasuo
16
6.6k
利用終了したドメイン名の最強終活〜観測環境を育てて、分析・供養している件〜 / The Ultimate End-of-Life Preparation for Discontinued Domain Names
nttcom
2
180
なぜ私は自分が使わないサービスを作るのか? / Why would I create a service that I would not use?
aiandrox
0
720
表現を育てる
kiyou77
1
210
Featured
See All Featured
Build The Right Thing And Hit Your Dates
maggiecrowley
34
2.5k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
33
2.8k
Being A Developer After 40
akosma
89
590k
Why You Should Never Use an ORM
jnunemaker
PRO
55
9.2k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
4
330
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.3k
How to Think Like a Performance Engineer
csswizardry
22
1.3k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
193
16k
Practical Orchestrator
shlominoach
186
10k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
12
960
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
114
50k
Transcript
ᶎݻܐᦓᖫᑕӨ Cocoa ጱᮁᭊ MDCC 16 ሴ - 2016 ଙ 9
์ 24 ෭
Ԇ᷌ • ᩸ɾॳᦩ • ಥɾ૬ • ɾᅾ • ݳɾജ
Ԇ᷌ • ᩸ɾॳᦩ - Ջԍฎ Swi' ܐᦓ • ಥɾ૬ •
ɾᅾ • ݳɾജ
Ԇ᷌ • ᩸ɾॳᦩ - Ջԍฎ Swi' ܐᦓ • ಥɾ૬ -
ܐᦓಘᶎݻܐᦓᖫᑕ • ɾᅾ • ݳɾജ
Ԇ᷌ • ᩸ɾॳᦩ - Ջԍฎ Swi' ܐᦓ • ಥɾ૬ -
ܐᦓಘᶎݻܐᦓᖫᑕ • ɾᅾ - ࣁ෭ଉݎӾֵአܐᦓ • ݳɾജ
Ԇ᷌ • ᩸ɾॳᦩ - Ջԍฎ Swi' ܐᦓ • ಥɾ૬ -
ܐᦓಘᶎݻܐᦓᖫᑕ • ɾᅾ - ࣁ෭ଉݎӾֵአܐᦓ • Model (Networking) • ViewController • ݳɾജ
᩸ɾॳᦩ Ջԍฎ Swi% ܐᦓ Protocol
protocol Greetable { var name: String { get } func
greet() }
ᶎݻ Object-oriented
ᶎݻ class Animal { var leg: Int { return 2
} func eat() { print("eat food.") } func run() { print("run with \(leg) legs") } } class Tiger: Animal { override var leg: Int { return 4 } override func eat() { print("eat meat.") } } let tiger = Tiger() tiger.eat() // "eat meat" tiger.run() // "run with 4 legs"
ViewController → UIViewController class ViewCotroller: UIViewController { // ᖀಥ //
view, isFirstResponder()... // ෛے func myMethod() { } }
AnotherViewController → UITableViewController → UIViewController class AnotherViewController: UITableViewController { //
ᖀಥ // tableView, isFirstResponder()... // ෛے func myMethod() { } }
ࢯहԏӞ Cross-Cu'ng Concerns ཞڔىဳᅩ
ᥴ٬ොໜ • Copy & Paste • ف BaseViewController • ׁᩢဳف
• ग़ᖀಥ
Objec&ve-C Message Sending
ViewController *v1 = ... [v1 myMethod]; AnotherViewController *v2 = ...
[v2 myMethod];
NSArray *array = @[v1, v2]; for (id obj in array)
{ [obj myMethod]; }
NSObject *v3 = [NSObject new] // v3 ဌํਫሿ `myMethod` NSArray
*array = @[v1, v2, v3]; for (id obj in array) { [obj myMethod]; } // Runtime error: // unrecognized selector sent to instance blabla
ࢯहԏԫ Dynamic Dispatch Safety ۖாၝݎਞق
OOP ࢯह • ۖாၝݎਞق • ཞڔىဳᅩ
ܐᦓ Protocol
Java, C# Interface
Swift protocol
Swi$ protocol protocol Greetable { var name: String { get
} func greet() }
Swi$ protocol protocol Greetable { var name: String { get
} func greet() } struct Person: Greetable { let name: String func greet() { print("֦অ \(name)") } } Person(name: "Wei Wang").greet()
ಥɾ૬ ܐᦓಘᶎݻܐᦓᖫᑕ
OOP ࢯह • ۖாၝݎਞق • ཞڔىဳᅩ
protocol Greetable { var name: String { get } func
greet() } struct Person: Greetable { let name: String func greet() { print("֦অ \(name)") } }
protocol Greetable { var name: String { get } func
greet() } struct Cat: Greetable { let name: String func greet() { print("meow~ \(name)") } }
let array: [Greetable] = [ Person(name: "Wei Wang"), Cat(name: "onevcat")]
for obj in array { obj.greet() } // ֦অ Wei Wang // meow~ onevcat
struct Bug: Greetable { let name: String } // Compiler
Error: // 'Bug' does not conform to protocol 'Greetable' // protocol requires function 'greet()'
OOP ࢯह • ✅ ۖாၝݎਞق • ཞڔىဳᅩ
ֵአܐᦓوՁդᎱ protocol P { func myMethod() }
// class ViewController: UIViewController extension ViewController: P { func myMethod()
{ doWork() } } // class AnotherViewController: UITableViewController extension AnotherViewController: P { func myMethod() { doWork() } }
Swi$ 2 - ܐᦓಘ
protocol P { func myMethod() } extension P { func
myMethod() { doWork() } } ԅ P ਧԎጱොဩ׀ἕᦊਫሿ
extension ViewController: P { } extension AnotherViewController: P { }
viewController.myMethod() anotherViewController.myMethod()
ਫሿ๚ࣁܐᦓӾ्กጱٖ protocol P { func myMethod() } extension P {
func myMethod() { doWork() } func anotherMethod() { myMethod() someOtherWork() } } viewController.anotherMethod()
• ܐᦓਧԎ • ׀ਫሿጱفݗ • ᭽ܐᦓጱᔄࣳᵱᥝٌᬰᤈਫሿ • ܐᦓಘ • ԅفݗ׀ἕᦊਫሿ
• ໑ഝفݗ׀᷐क़ਫሿ
OOP ࢯह • ✅ ۖாၝݎਞق • ✅ ཞڔىဳᅩ
ɾᅾ ࣁ෭ଉݎӾֵአܐᦓ
WWDC 15 #408 Protocol-Oriented Programming in Swi3
Model (Networking)
ໜֺ चԭ Protocol ጱᗑᕶ᧗
Demo • चԭܐᦓ • ᔄࣳਞق • ᥴᘠݳ • ݢܔᇿၥᦶ •
ಘ
• Networking: • AFNetworking • Alamofire • (ASIHTTPRequest) ! •
Model Parser • SwiAyJSON • Argo • Himotoki
սضᘍᡤֵአܐᦓ ṛଶܐᦓ۸ํۗԭᥴᘠ҅ၥᦶզ݊ಘ
APIKit1 + Himotoki2 2 h$ps:/ /github.com/ikesyo/Himotoki 1 h$ps:/ /github.com/ishkawa/APIKit
Controller
ໜֺ ړᶭے
ړᶭےጱᗑᕶ᧗ struct Pagination<T> { let items: [T] let hasNext: Bool
} struct ChannelsResquest: Request { typealias Response = Pagination<Channel> let lastId: Int? }
class ChannelsTableViewController: UITableViewController { private var lastId: Int? = nil
private var hasNext = true override func viewDidLoad() { super.viewDidLoad() load() } func load() { if hasNext { client.send(ChannelsResquest(lastId: lastId)) { result in } } } }
class ChannelsTableViewController: UITableViewController { private var lastId: Int? = nil
private var hasNext = true private var data: [Channel] = [] override func viewDidLoad() { super.viewDidLoad() load() } func load() { if hasNext { client.send(ChannelsResquest(lastId: lastId)) { result in self.lastId = result!.items.last?.id self.hasNext = result!.hasNext self.data = result.items self.tableView.reloadData() } } } }
extension ChannelsTableViewController: UITableViewDelegate { override func tableView(tableView: UITableView, willDisplayCell cell:
UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { if indexPath.row == data.count - 1 { load() } }
ඳԪᬮဌํᕮ...
ඳԪᬮဌํᕮ... private var isLoading = false func load() { if
isLoading { return } if hasNext { isLoading = true client.send(ChannelsResquest(lastId: lastId)) { result in //... self.isLoading = false } } }
ChannelsTableViewController Pagina&on<Channel>
FriendsTableViewController Pagina&on<Friend>
ොໜӞғ॔ګᔌᩂ
ොໜӞғ॔ګᔌᩂ
ොໜԫғᆿᔄ
BaseTableViewController
class BaseTableViewController: UITableViewController { var lastId: Int? = nil var
hasNext = true var isLoading = false func loadNext() { if isLoading { return } if hasNext { isLoading = true doLoad {result in self.lastId = //... self.hasNext = //... } } } func doLoad(handler: (Any?)->Void) { // ?? } }
class BaseTableViewController: UITableViewController { var lastId: Int? = nil var
hasNext = true var isLoading = false func loadNext() { if isLoading { return } if hasNext { isLoading = true doLoad {result in self.lastId = //... self.hasNext = //... } } } func doLoad(handler: (Any?)->Void) { fatalError("You should implement it in subclass!") } }
class FriendsTableViewController: BaseTableViewController { private var data: [Friend] = []
override func viewDidLoad() { super.viewDidLoad() loadNext() } override func doLoad(handler: (Any?)->Void) { client.send(FriendsRequest(lastId: lastId)) { result in handler(result) // ... self.data = result!.items self.tableView.reloadData() } } }
None
None
WTF?
• FriendsTableViewController → FriendsCollec5onViewController
• FriendsTableViewController → FriendsCollec5onViewController • BaseTableViewController → BaseCollec5onViewController
• FriendsTableViewController → FriendsCollec5onViewController • BaseTableViewController → BaseCollec5onViewController ॔ګᔌᩂ?
• FriendsTableViewController → FriendsCollec5onViewController • BaseTableViewController → BaseCollec5onViewController ॔ګᔌᩂ
ොໜӣғܐᦓ
struct NextPageState<T> { private(set) var hasNext: Bool private(set) var isLoading:
Bool private(set) var lastId: T? init() { hasNext = true isLoading = false lastId = nil } mutating func reset() { hasNext = true isLoading = false lastId = nil } mutating func update(hasNext: Bool, isLoading: Bool, lastId: T?) { self.hasNext = hasNext self.isLoading = isLoading self.lastId = lastId } }
protocol NextPageLoadable: class { associatedtype DataType associatedtype LastIdType var data:
[DataType] { get set } var nextPageState: NextPageState<LastIdType> { get set } func performLoad( successHandler: (_ rows: [DataType], _ hasNext: Bool, _ lastId: LastIdType?) -> (), failHandler: () -> () ) }
protocol NextPageLoadable: class { associatedtype DataType associatedtype LastIdType var data:
[DataType] { get set } var nextPageState: NextPageState<LastIdType> { get set } func performLoad( successHandler: (_ rows: [DataType], _ hasNext: Bool, _ lastId: LastIdType?) -> (), failHandler: () -> () ) } extension NextPageLoadable where Self: UITableViewController { func loadNext() { guard nextPageState.hasNext else { return } if nextPageState.isLoading { return }
extension NextPageLoadable where Self: UITableViewController { func loadNext() { guard
nextPageState.hasNext else { return } if nextPageState.isLoading { return } nextPageState.isLoading = true performLoad(successHandler: { rows, hasNext, lastId in self.data += rows self.nextPageState.update(hasNext: hasNext, isLoading: false, lastId: lastId)
extension NextPageLoadable where Self: UITableViewController { func loadNext() { guard
nextPageState.hasNext else { return } if nextPageState.isLoading { return } nextPageState.isLoading = true performLoad(successHandler: { rows, hasNext, lastId in self.data += rows self.nextPageState.update(hasNext: hasNext, isLoading: false, lastId: lastId) self.tableView.reloadData() }, failHandler: { //.. }) } }
class FriendTableViewController: UITableViewController { var nextPageState = NextPageState<Int>() var data:
[Friend] = [] } extension FriendTableViewController: NextPageLoadable { func performLoad( successHandler: ([String], Bool, Int?) -> (), failHandler: () -> ()) { client.send(FriendsRequest()) { result in if let result = result { successHandler(result.items, result.hasNext, result.items.last.id) } else { failHandler() } } } }
extension NextPageLoadable where Self: UITableViewController { func loadNext() { ...
} }
extension NextPageLoadable where Self: UITableViewController { func loadNext() { ...
} } extension FriendTableViewController: NextPageLoadable { ... } class FriendTableViewController: UITableViewController { //... override func viewDidLoad() { super.viewDidLoad() loadNext() } override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { if indexPath.row == data.count - 1 { loadNext() } } }
extension NextPageLoadable where Self: UITableViewController { func loadNext() { ...
} } UICollec(onView ெԍېҘ
extension NextPageLoadable where Self: UITableViewController { func loadNext() { ...
} } UICollec(onView ெԍېҘ ॔ګᔌᩂҘ extension NextPageLoadable where Self: UICollectionViewController { func loadNext() { ... } }
extension NextPageLoadable where Self: UITableViewController { func loadNext() { guard
nextPageState.hasNext else { return } if nextPageState.isLoading { return } nextPageState.isLoading = true performLoad(successHandler: { rows, hasNext, lastId in self.data += rows self.nextPageState.update(hasNext: hasNext, isLoading: false, lastId: lastId) self.tableView.reloadData() }, failHandler: { // Failed when first loading if self.nextPageState.lastId == nil { self.data = [] self.nextPageState.reset() } }) } }
tableView.reloadData() colletionView.reloadData()
tableView.reloadData() colletionView.reloadData() protocol ReloadableType { func reloadData() } extension UITableView:
ReloadableType {} extension UICollectionView: ReloadableType {}
extension NextPageLoadable where Self: UITableViewController { func loadNext() { //...
self.tableView.reloadData() //... } }
extension NextPageLoadable { func loadNext(view: ReloadableType) { //... view.reloadData() //...
} }
extension NextPageLoadable where Self: UITableViewController { func loadNext() { loadNext(reloadView:
tableView) } }
extension NextPageLoadable where Self: UITableViewController { func loadNext() { loadNext(reloadView:
tableView) } } extension NextPageLoadable where Self: UICollectionViewController { func loadNext() { loadNext(reloadView: collectionView) } }
• FriendsTableViewController → FriendsCollec5onViewController
class FriendTableViewController: UITableViewController { var nextPageState = NextPageState<Int>() var data:
[Friend] = [] } extension FriendTableViewController: NextPageLoadable { func performLoad( successHandler: ([String], Bool, Int?) -> (), failHandler: () -> ()) { client.send(FriendsRequest()) { result in if let result = result { successHandler(result.items, result.hasNext, result.items.last.id) } else { failHandler() } } } }
class FriendCollectionViewController: UITableViewController { var nextPageState = NextPageState<Int>() var data:
[Friend] = [] } extension FriendCollectionViewController: NextPageLoadable { func performLoad( successHandler: ([String], Bool, Int?) -> (), failHandler: () -> ()) { client.send(FriendsRequest()) { result in if let result = result { successHandler(result.items, result.hasNext, result.items.last.id) } else { failHandler() } } } }
ViewController ၥᦶ
ֵአܐᦓᕟᕢ ViewController • כ೮ᓌܔጱ ViewController ᖀಥ (ٺᖀಥ)
ֵአܐᦓᕟᕢ ViewController • כ೮ᓌܔጱ ViewController ᖀಥ (ٺᖀಥ) • ֵአܐᦓ̿೪ᤰ̀ViewController ಅᵱᥝጱۑᚆ
ֵአܐᦓᕟᕢ ViewController • כ೮ᓌܔጱ ViewController ᖀಥ (ٺᖀಥ) • ֵአܐᦓ̿೪ᤰ̀ViewController ಅᵱᥝጱۑᚆ
• ਖ਼դᎱᨱձړᐶڊ ViewController
ֵአܐᦓᕟᕢ ViewController • כ೮ᓌܔጱ ViewController ᖀಥ (ٺᖀಥ) • ֵአܐᦓ̿೪ᤰ̀ViewController ಅᵱᥝጱۑᚆ
• ਖ਼դᎱᨱձړᐶڊ ViewController • ֵአ᯿ጱොဩਖ਼ܐᦓ᭑Ⴙ᭗አ۸
ݳɾജ ֵአܐᦓଆۗද࠺դᎱᦡᦇ
വគᩒා • Protocol-Oriented Programming in Swi4 - WWDC 15 #408
• Protocols with Associated Types - @alexisgallagher • Protocol Oriented Programming in the Real World - @_maHhewpalmer • PracIcal Protocol-Oriented-Programming - @natashatherobot
ᨀᨀᘰލ FAQ Email:
[email protected]
, GitHub: onevcat