質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

3回答

2991閲覧

UITableViewControllerとUITableViewDataSourceの関係について

secondpenguin

総合スコア14

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2016/12/17 16:13

Swift初心者です。
現在UITableViewControllerを使ってテーブルビューを実装中です(正確には、UITableViewControllerを継承したクラス内にコードを書いています)

UITableViewControllerのソースコードを見ると、UITableViewDataSourceというプロトコルを批准しています。UITableViewDataSourceは、それを批准したクラスに対して

swift

1tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 2tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

という2つのメソッドを実装することを要求していると思うのですが、UITableViewControllerにそれら二つのメソッドが書かれているように見えません。

どういった仕組みでUITableViewControllerとUITableViewDataSourceの関係が成立しているのでしょうか?また、自分の理解しているデリゲートとプロトコルの関係が間違っていればご指摘いただけると幸いです。

###補足情報
Xcode version 8.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

結論から言いますとUITableViewControllerはUITableViewDataSourceに準拠しています。

ポイントは2つあります。

  1. Xcodeなどで確認しているUIKitの内容はHeaderファイルであること
  2. UITableViewControllerに関してpublicなものがHeaderに含まれていないこと

UITableViewControllerはUITableViewDataSourceに準拠している

次のようにUITableViewControllerを継承する場合にoverrideしていることからもUITableViewDataSourceのpublic funcが実装されていることが分かります。(他の回答者様も同様の内容について言及されています)

swift

1open class CustomTableViewController: UITableViewController { 2 override public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 3 return 0 4 } 5 6 override public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 7 return UITableViewCell() 8 } 9}

例えば、自らUITableViewDataSourceに準拠させようとすると次のようになります

swift

1open class SampleTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { 2 public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 3 return 0 4 } 5 6 public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 7 return UITableViewCell() 8 } 9}

ヘッダーと実装ファイルの例

public以上のアクセスレベルのものがヘッダーに表示されます。
ここではヘッダーに含まれているものが実装のすべてではないということが分かっていただければ良いです。

実装ファイル

swift

1open class SampleTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { 2 open var openString: String? 3 public var publicString: String? 4 internal var internalString: String? 5 fileprivate var fileprivateString: String? 6 private var privateString: String? 7 8 override open func viewDidLoad() { 9 super.viewDidLoad() 10 // Do any additional setup after loading the view, typically from a nib. 11 12 } 13 14 override open func didReceiveMemoryWarning() { 15 super.didReceiveMemoryWarning() 16 // Dispose of any resources that can be recreated. 17 } 18 19 public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 20 return 0 21 } 22 23 public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 24 return UITableViewCell() 25 } 26 27 func cannotAccessFromOutsideOfFramework() { 28 } 29}

ヘッダーファイル

swift

1open class SampleTableViewController : UIViewController, UITableViewDelegate, UITableViewDataSource { 2 3 open var openString: String? 4 5 public var publicString: String? 6 7 override open func viewDidLoad() 8 9 override open func didReceiveMemoryWarning() 10 11 public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 12 13 public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 14}

UITableViewControllerヘッダーに含まれるはずのpublic funcは何処へ

上記示したように、UITableViewDataSourceに準拠し以下2つのメソッドを実装していればヘッダーファイルにも表示されるはずです。しかし、UIKitにはそれが含まれていません。
調べて見ましたが、Swiftのみでこの挙動の再現方法は分かりませんでした。

swift

1public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 2public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

生成されるヘッダーファイルの比較

Swiftで実現する方法は分からなかったので、Frameworkとして生成されるヘッダーを比較しました。
以下の3つの場合について示します。尚Sampleプロジェクト新規プロジェクト作成しCocoa Touch Frameworkを追加することでSwift, Objective-Cそれぞれについて作成しました。

  1. UIKit
  2. Sampleプロジェクト(Swift)
  3. Sampleプロジェクト(Objective-C)

Apple公式UIKit

UITableViewController.h

Xcodeに含まれているUITableViewController.hは以下の場所にあります。

bash

1/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UITableViewController.h

中身を見てみると以下の様になっています。

UITableViewController.h

1// 2// UITableViewController.h 3// UIKit 4// 5// Copyright (c) 2008-2016 Apple Inc. All rights reserved. 6// 7#import <Foundation/Foundation.h> 8#import <UIKit/UIViewController.h> 9#import <UIKit/UITableView.h> 10#import <UIKit/UIKitDefines.h> 11 12// Creates a table view with the correct dimensions and autoresizing, setting the datasource and delegate to self. 13// In -viewWillAppear:, it reloads the table's data if it's empty. Otherwise, it deselects all rows (with or without animation) if clearsSelectionOnViewWillAppear is YES. 14// In -viewDidAppear:, it flashes the table's scroll indicators. 15// Implements -setEditing:animated: to toggle the editing state of the table. 16 17NS_ASSUME_NONNULL_BEGIN 18 19NS_CLASS_AVAILABLE_IOS(2_0) @interface UITableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> 20 21- (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER; 22- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER; 23- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; 24 25@property (nonatomic, strong, null_resettable) UITableView *tableView; 26@property (nonatomic) BOOL clearsSelectionOnViewWillAppear NS_AVAILABLE_IOS(3_2); // defaults to YES. If YES, any selection is cleared in viewWillAppear: 27 28@property (nonatomic, strong, nullable) UIRefreshControl *refreshControl NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED; 29 30@end 31 32NS_ASSUME_NONNULL_END

Sampleプロジェクトにて検証した結果

Swiftで書かれたFrameworkではSWIFT_CLASSが使われておりObjective-Cへ変換されています。
この際にSwiftのアクセスレベルに応じてヘッダーが生成されています。
一方でObjective-Cで書かれたFrameworkではヘッダーと実装ファイルが分かれているため、
ヘッダーで定義したものが書き出されています。
上記UITableViewController.hとこれらを鑑みるとUIKitに関しては依然Objective-Cで作成されていると考えられます。
そのため、UITableViewController.hのヘッダーにはたとえpublic funcであってもUITableViewDataSourceに定義されているものは記載されていないと考えられます。

Swift Frameworkで書き出されるヘッダー

Header

1// Generated by Apple Swift version 3.0.1 (swiftlang-800.0.58.6 clang-800.0.42.1) 2#pragma clang diagnostic push 3 4#if defined(__has_include) && __has_include(<swift/objc-prologue.h>) 5# include <swift/objc-prologue.h> 6#endif 7 8--- 9長いので省略 10--- 11 12#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" 13#pragma clang diagnostic ignored "-Wduplicate-method-arg" 14@class UITableView; 15@class UITableViewCell; 16@class NSBundle; 17@class NSCoder; 18 19SWIFT_CLASS("_TtC25FrameworkForTeratail5919325SampleTableViewController") 20@interface SampleTableViewController : UIViewController <UIScrollViewDelegate, UITableViewDataSource, UITableViewDelegate> 21@property (nonatomic, copy) NSString * _Nullable openString; 22@property (nonatomic, copy) NSString * _Nullable publicString; 23- (void)viewDidLoad; 24- (void)didReceiveMemoryWarning; 25- (NSInteger)tableView:(UITableView * _Nonnull)tableView numberOfRowsInSection:(NSInteger)section; 26- (UITableViewCell * _Nonnull)tableView:(UITableView * _Nonnull)tableView cellForRowAtIndexPath:(NSIndexPath * _Nonnull)indexPath; 27- (nonnull instancetype)initWithNibName:(NSString * _Nullable)nibNameOrNil bundle:(NSBundle * _Nullable)nibBundleOrNil OBJC_DESIGNATED_INITIALIZER; 28- (nullable instancetype)initWithCoder:(NSCoder * _Nonnull)aDecoder OBJC_DESIGNATED_INITIALIZER; 29@end 30 31#pragma clang diagnostic pop

Objective-C Frameworkで書き出されるヘッダー

Header

1#import <UIKit/UIKit.h> 2 3@interface OCTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> 4- (id)initWithStyle:(UITableViewStyle)style; 5 6@property (nonatomic, strong) UITableView *tableView; 7@property (nonatomic) BOOL clearsSelectionOnViewWillAppear; 8@end

まとめ

UITableViewControllerのヘッダーにはUITableViewDataSourceのメソッドは含まれていないものの、
UITableViewDataSourceに準拠したメソッドの実装は行われている。
その裏には依然Objective-Cが生きていることを垣間見ることができる。

長文失礼しました、参考になれば幸いです。

投稿2016/12/19 07:05

nafu

総合スコア30

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

secondpenguin

2016/12/19 11:50

nafu様 非常に本格的な検証ありがとうございます。 初学者の自分にとっては高度すぎるほどの内容であるにも関わらず、わかりやすい説明でとても助かりました。 なるほど、UITableViewController.hはObjective-Cで実装されており、結果、public funcとして実装されてあってもヘッダーに表示されていないということが起こっている、のですね。 自分は、ヘッダーとして公開されているものがAppleの膨大なソースコードのほんの一端であるということさえちゃんと意識できていなかったので、nafu様の回答で改めてSwiftの奥深さに触れることができた気がします。 ありがとうございました!
guest

0

UITableViewControllerのソースコードを見ると

UIKitのソースコードって公開されてないんじゃ‥?

ちゃんと実装されていると思いますが。(コードはSwift2です)

swift

1let t = UITableViewController() 2print(t.tableView(t.tableView, numberOfRowsInSection: 0)) 3//=> 0

投稿2016/12/19 01:08

fuzzball

総合スコア16731

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

secondpenguin

2016/12/19 11:55

fuzzball様 ご回答ありがとうございます。 なるほど、私はUITableViewController.hで確認できるコードで全て完結できると勘違いしていました。確かにfuzzballs様が実行されている通り、疑問を持った箇所はコードが動くかどうかをシンプルに試して、実装されているということを確認して進めていくのが良さそうですね。 ありがとうございました!
guest

0

swift

1tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 2tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

上記のメソッドはUITableViewController クラスのメソッドではありません、UITableViewDataSource プロトコルのメソッドです。

ですからUITableViewDataSourceに準拠した時には上記の必須のメソッドを実装する必要があります。
※ 以下がUITableViewDataSourceのメソッドでoptionalの付いていない最初の2つが実装必須という事です。

swift

1public protocol UITableViewDataSource : NSObjectProtocol { 2 3 @available(iOS 2.0, *) 4 public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 5 6 @available(iOS 2.0, *) 7 public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 8 9 @available(iOS 2.0, *) 10 optional public func numberOfSections(in tableView: UITableView) -> Int 11 12 @available(iOS 2.0, *) 13 optional public func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 14 15 @available(iOS 2.0, *) 16 optional public func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? 17 18 @available(iOS 2.0, *) 19 optional public func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool 20 21 @available(iOS 2.0, *) 22 optional public func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool 23 24 @available(iOS 2.0, *) 25 optional public func sectionIndexTitles(for tableView: UITableView) -> [String]? 26 27 @available(iOS 2.0, *) 28 optional public func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int 29 30 @available(iOS 2.0, *) 31 optional public func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) 32 33 @available(iOS 2.0, *) 34 optional public func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) 35} 36

自身の定義したクラスがUITableViewControllerクラスを継承しているということはUITableViewControllerクラスに適用されているUITableViewDataSourceプロトコルのメソッドを実装しなければならないという事です。

swift

1// UITableViewControllerの上にマウスを持って行き⌘ボタンを押したままクリックするとUITableViewControllerの定義に飛べます。 2class TableViewController: UITableViewController { 3 // 自分で定義したクラス 4} 5 67 8// 更にUITableViewDataSourceの上にマウスを持って行き⌘ボタンを押したままクリックするとUITableViewDataSourceの定義に飛べます。 9@available(iOS 2.0, *) 10open class UITableViewController : UIViewController, UITableViewDelegate, UITableViewDataSource { 11 12 public init(style: UITableViewStyle) 13 14 public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) 15 public init?(coder aDecoder: NSCoder) 16 17 open var tableView: UITableView! 18 19 @available(iOS 3.2, *) 20 open var clearsSelectionOnViewWillAppear: Bool // defaults to YES. If YES, any selection is cleared in viewWillAppear: 21 22 @available(iOS 6.0, *) 23 open var refreshControl: UIRefreshControl? 24} 25

投稿2016/12/17 16:32

編集2016/12/17 23:07
_Kentarou

総合スコア8490

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

secondpenguin

2016/12/18 05:13

_Kentarou さま とても丁寧なご回答ありがとうございます。 自分もUITableViewDataSourceに書かれているメソッドを実装しなければいけないということは理解しており、コードの中に tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell の二つを実装しています。override funcを使って実装しています。 ここからが自分の理解が追いついていないところなのですが、overrideで実装できた以上、当然のようにUITableViewController自身に上記二つのメソッドが記述されていると思っていたのですが、UITableViewControllerの中を見てもそのメソッドはなく、混乱しているところです。 _Kentarou様からご回答いただいたソースの通り、open var tableView: UITableView!というプロパティはありますが、これはあくまでプロパティであってメソッドではないため、自分にはUITableViewControllerがUITableViewDataSourceを批准しきれていないように見えてしまうのです。 UITableViewDataSourceが要求するプロトコルをUITableViewControllerがメソッドを備えて批准し、具体的な内容については継承した子クラスで定義する、という流れなら自然に理解できるのですが…… 質問の仕方が不慣れで大変申し訳ないです。よろしくお願いいたします。
_Kentarou

2016/12/18 06:01

secondpenguinさんの言わんとしていることは理解しました。 確かに同じ様なものを自分で定義した場合は、継承元でもプロトコルのメソッドを書かないとエラーになりますね、、、 他にも同じ様なパターンはあるかもしれませんが、UIKitの中のことなのでちょっと調べた感じでは分からなかったです、すみません。 何かヒントになるような記述等ありましたら追記致します。
secondpenguin

2016/12/18 11:17

_Kentarou様 ご返信ありがとうございます。ちょっとマニアックな疑問だったかもしれません。でもそれが分かっただけでも自分にとっては収穫です。 自分も引き続き調べてみます。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問