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.8k
面向协议编程与 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
5.1k
GMTC 2019 - 在分歧中发展,2019 我们能用 Swift 做什么
onevcat
0
1k
从 Swift 到机器学习
onevcat
2
1k
iOS Dev - The Dark Side
onevcat
0
140
Swift 3 - From Expert to Beginner
onevcat
2
220
如何打造一个让人愉快的框架
onevcat
4
22k
JSPatch Introduction
onevcat
0
200
Objective-C Runtime Swizzle
onevcat
0
200
Unity Memory
onevcat
0
150
Other Decks in Technology
See All in Technology
結局QUICで通信は速くなるの?
kota_yata
8
7.3k
リリース2ヶ月で収益化した話
kent_code3
1
310
生成AIによるソフトウェア開発の収束地点 - Hack Fes 2025
vaaaaanquish
33
14k
意志の力が9割。アニメから学ぶAI時代のこれから。
endohizumi
1
100
Findy Freelance 利用シーン別AI活用例
ness
0
550
Telemetry APIから学ぶGoogle Cloud ObservabilityとOpenTelemetryの現在 / getting-started-telemetry-api-with-google-cloud
k6s4i53rx
0
150
Google Cloud で学ぶデータエンジニアリング入門 2025年版 #GoogleCloudNext / 20250805
kazaneya
PRO
22
5.5k
【新卒研修資料】数理最適化 / Mathematical Optimization
brainpadpr
27
13k
Claude CodeでKiroの仕様駆動開発を実現させるには...
gotalab555
3
1.1k
Intro to Software Startups: Spring 2025
arnabdotorg
0
260
Kiro と Q Dev で 同じゲームを作らせてみた
r3_yamauchi
PRO
1
100
形式手法特論:位相空間としての並行プログラミング #kernelvm / Kernel VM Study Tokyo 18th
ytaka23
3
1.4k
Featured
See All Featured
Product Roadmaps are Hard
iamctodd
PRO
54
11k
Building an army of robots
kneath
306
45k
How to Ace a Technical Interview
jacobian
278
23k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
229
22k
The Pragmatic Product Professional
lauravandoore
36
6.8k
Fantastic passwords and where to find them - at NoRuKo
philnash
51
3.4k
Building Better People: How to give real-time feedback that sticks.
wjessup
367
19k
It's Worth the Effort
3n
185
28k
Making Projects Easy
brettharned
117
6.3k
Build your cross-platform service in a week with App Engine
jlugia
231
18k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
251
21k
Navigating Team Friction
lara
188
15k
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