私たちが開発するアプリは、多かれ少なかれインターネットに接続されています。または、サーバー側のデータをプルして UI を更新したり、独自のメッセージをネットワーク経由でプッシュしたり、携帯電話で自分の写真を削除したり、音楽プレーヤー アプリを開いてお気に入りの曲をダウンロードしたりできます。データを要求、受信、処理、および送信する方法については、このセクションで説明します。
コンピュータ理論
1. HTTP における GET と POST の違いについて話す
キーワード: #方向#タイプ#引数位置
方向的には、GET はサーバーから情報を取得することであり、POST はサーバーに情報を送信することです。
タイプに関しては、GET は静的コンテンツと動的コンテンツを処理し、POST は動的コンテンツのみを処理します。
パラメーターの位置の観点から、GET のパラメーターはその URI にあり、POST のパラメーターはそのパッケージ本体にあります。この観点から、POST は GET よりも安全で機密性が高いです。
GET はブラウザの履歴にキャッシュして保存できること、その内容は理論的には長さが制限されていること、POST はこの 3 つの点で正反対です。
2. セッション、トークン、および Cookie の概念について話す
キーワード: #UserAuthentication#Client#Server
セッションは、サーバーがユーザーの認証と追跡に使用するデータ構造です。クライアントから送信された情報を判断してユーザーを決定します。ユーザーの一意の識別は、クライアントから送信されたセッション ID です。
トークンは、サーバーによって生成される文字列の文字列で、クライアントが要求するトークンであり、サーバーがユーザーを特定するための一意の識別子です。セッション ID はトークンとしてよく使用されます。トークンの出現により、サーバーがユーザー名とパスワードを頻繁にクエリすることを防ぎ、データベースに対するクエリの負荷を軽減します。
クッキーとは、クライアントがユーザー情報を保存する仕組みです。最初のセッションの HTTP プロトコルは、Cookie にセッション ID を記録し、そのセッション ID を毎回サーバーに送信します。
セッションは、一般的にユーザー認証に使用されます。デフォルトではサーバー上のファイルに保存されますが、もちろんメモリやデータベースに保存することもできます。
クライアントが Cookie を無効にすると、クライアントは URL 書き換え技術を使用します。つまり、セッション中に URL の末尾にセッション ID を追加してサーバーに送信します。
3. HTTPS で接続された Web サイトで、アカウントのパスワードを入力して [ログイン] をクリックした後、サーバーがリクエストを返す前に何が起こったのですか?
キーワード: #ロック#クライアント#サーバー
1) クライアントはリクエストをパックします。URL、ポート、アカウントのパスワードなどを含みます。アカウント パスワード ログインでは Post メソッドを使用する必要があるため、関連するユーザー情報が本文に読み込まれます。この要求には、ネットワーク アドレス、プロトコル、およびリソース パスの 3 つの側面が含まれている必要があります。これはHTTPS、つまりHTTP + SSL / TLSであり、暗号化された情報を処理するためのモジュールがHTTPに追加されていることに注意してください(ロックに相当)。このプロセスは、キーを要求するクライアントと同等です。
2) サーバーはリクエストを受け入れます。通常、クライアントの要求は最初に DNS サーバーに送信されます。DNS サーバーは、ネットワーク アドレスをネットワーク上のマシンに対応する IP アドレスに解決する役割を果たします。その中で、Hosts Hijack と ISP 障害の問題が発生する可能性があります。DNS レベルを通過した後、情報はサーバーに到達します. この時点で、クライアントはサーバーのポートとのソケット接続を確立します. ソケットは通常、要求をファイル記述子の形式で解析します. このプロセスは、キー テンプレートをクライアントに送信するかどうかをサーバー側で分析することに相当します。
3) サーバーはデジタル証明書を返します。サーバー側には一連のデジタル証明書 (キー テンプレートに相当) があり、この証明書が最初にクライアントに送信されます。このプロセスは、キー テンプレートをサーバーからクライアントに送信することと同じです。
4) クライアントは暗号化された情報を生成します。クライアントは受信したデジタル証明書 (キー テンプレート) に従ってキーを生成し、コンテンツをロックします。この時点で情報は暗号化されています。このプロセスは、クライアントがキーを生成してリクエストをロックするのと同じです。
5) クライアントは暗号化された情報を送信します。サーバーは、自身が送信したデジタル証明書のロック情報を受け取ります。このとき生成された鍵もサーバーに送信されます。このプロセスは、リクエストを送信するクライアントと同等です。
6) サーバー側で暗号化された情報のロックを解除します。暗号化された情報を受信した後、サーバーは取得したキーに従ってそれを復号化し、返されるデータを対称的に暗号化します。この処理は、サーバー側のロック解除要求、生成、およびロック応答情報に相当します。
7) サーバーはクライアントに情報を返します。クライアントは、対応する暗号化された情報を受け取ります。このプロセスは、サーバーがクライアントに応答を送信することと同じです。
8) クライアントはロックを解除し、情報を返します。クライアントは、新しく生成されたキーを使用してコンテンツを復号化し、ブラウザーに表示します。
プロセス全体のフローチャートは次のとおりです。
iOS ネットワーク リクエスト
4. 次のクラスを記述して比較してください: URLSessionTask、URLSessionDataTask、URLSessionUploadTask、URLSessionDownloadTask
キーワード: #URLSession
URLSessionTask は抽象クラスです。これを実装することで、リクエスト、アップロード、ダウンロードのタスクなど、あらゆるネットワーク送信タスクをインスタンス化できます。その中断 (キャンセル)、再開 (再開)、および終了 (中断) メソッドには、デフォルトの実装があります。
URLSessionDataTask は、HTTP GET 要求を担当します。URLSessionTask の具体的な実装です。通常、サーバーからデータを取得してメモリに格納するために使用されます。
URLSessionUploadTask は、HTTP Post/Put リクエストを担当します。URLSessionDataTask を継承しています。通常、データのアップロードに使用されます。
URLSessionDownloadTask は、データのダウンロードを担当します。URLSessionTask の具体的な実装です。通常、ダウンロードしたデータは一時ファイルに保存されますが、キャンセル後にデータを保存して、後でダウンロードを続行できます。
それらの関係は次のとおりです。
5. 完了ハンドラーとは?
キーワード: #閉鎖
Completion Handler は通常、API リクエスト後に返されたデータを処理するために使用されます。
URLSessionTask が終了すると、成功するかエラーを報告するかに関係なく、完了ハンドラーは通常、Data、URLResponse、Error の 3 つのパラメーターを受け入れます。これらの 3 つのパラメーターはすべてオプションであることに注意してください。
Swift では、完了ハンドラーは @escaping でマークする必要があります。これは常に API リクエストの後に実行されるため、つまり、完了ハンドラーはメソッドが返されるまで関与しません。これは、クロージャーをエスケープする典型的なケースです。
6. コード戦闘: API の URL を指定して、ユーザー データを返すメソッドを設計する
キーワード: #URLSessionDataTask
この質問では、URLSessionDataTask の基本的な使用法を調べます。以下は、最も単純で大雑把な書き方です。
func queryUser(url: String, completion: @escaping (_ user: User?, _ error : Error?) -> Void)) {
guard let url = URL(string: url) else {
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
DispatchQueue.main.async {
completion(nil, error)
}
return
} else if let data = data, let response = response as? HTTPURLResponse {
DispatchQueue.main.async {
completion(convertDataToUser(data), nil)
}
} else {
DispatchQueue.main.async {
completion(nil, NSError(“invalid response”))
}
}
}.resume()
}
上記の書き方には多くの問題がありますが、その中で最も重要なものは次のとおりです。
URL エラーの不適切な処理。将来のデバッグのためにエラー メッセージを返す必要があります。
URLSession のシングルトンを使用するのは良くありません。リクエストごとに dataTask を作成するのはもったいないですし、短期間に複数のリクエストを行うと、サーバーに不要な負荷がかかります。これに対処する正しい方法は、各リクエストの前のリクエストをキャンセルすることです(完了したかどうかに関係なく)。
コードの重複は冗長です。メイン スレッドに切り替えてクロージャーを呼び出すプロセスは、コード内で何度も使用されます。実際にメソッド全体をクラスに拡張し、戻り値をメンバー変数と組み合わせて使用できます。
上記の 3 つのポイントに加えて、コードをさらに変更して、可読性を高め、ロジックを改善することができます。変更されたコードは次のとおりです。
enum QueryError: String {
case InvaldURL = “Invalid URL”,
case InvalidResponse = “Invalid response”
}
class QueryService {
typealias QueryResult = (User?, String?) -> Void
var user: User?
var errorMessage: String?
let defaultSession = URLSession(configuration: .default)
var dataTask: URLSessionDataTask?
func queryUsers(url: String, completion: @escaping QueryResult) {
dataTask?.cancel()
guard let url = URL(string: url) else {
DispatchQueue.main.async {
completion(user, QueryError.InvalidURL)
}
return
}
dataTask = defaultSession.dataTask(with: url) { [weak self] data, response, error in
defer {
self?.dataTask = nil
}
if let error = error {
self?.errorMessage = error.localizedDescription
} else if let data = data,
let response = response as? HTTPURLResponse,
response.statusCode == 200 {
self?.user = convertDataToUser(data)
} else {
self?.errorMessage = QueryError.InvalidResponse
}
DispatchQueue.main.async {
completion(self?.user,self?.errorMessage)
}
}.resume()
}
}
上記の修正方法は、主にいくつかの欠陥に対するものです。API が Swift のプロトコル指向プログラミングで実装されている場合、コード全体がより柔軟になります。
情報プッシュ
7. iOS 開発におけるローカル メッセージ通知のプロセスはどのようなものですか?
キーワード: #UserNotifications
UserNotifications フレームワークは、リモートおよびローカル メッセージ通知用の Apple のフレームワークです。このプロセスは、主に次の 4 つのステップに分かれています。
1) 登録します。requestAuthorization メソッドを呼び出すことにより、通知センターは通知許可要求をユーザーに送信します。ポップアップ アラートで [同意する] をクリックして、登録を完了します。
2) 作成します。最初に情報コンテンツ UNMutableNotificationContent とトリガー メカニズム UNNotificationTrigger を設定し、次にこれら 2 つの値を使用して UNNotificationRequest を作成し、最後に現在の通知センター UNUserNotificationCenter.current() にリクエストを追加します。
3) 押す。このステップは、システムまたはリモート サーバーのプッシュ通知です。鮮明なサウンド (またはカスタマイズされたサウンド) を伴って、対応する UI を携帯電話のインターフェイスに表示するプロセスが通知されます。
4) 応答。ユーザーが通知を見てクリックすると、対応する応答オプションが表示されます。設定応答オプションは、UNNotificationAction および UNNotificationCategory です。
ボーナス回答:
リモート プッシュのプロセスはローカル プッシュのプロセスと似ていますが、違いは、作成の 2 番目のステップで、パラメーターの内容とメッセージの作成がローカルではなくサーバー側で完了することです。
8. iOS 開発におけるリモート メッセージ プッシュの原則は何ですか?
キーワード: #APNs サーバー
この質問に答える鍵は、iOS システム、アプリ、APNs サーバー、およびアプリに対応するクライアントの関係を明らかにすることです。具体的には:
アプリは、リモート メッセージのプッシュ許可のために iOS システムに適用されます。これは、ローカル プッシュ通知の登録と同じです。
iOS システムは、APNs (Apple Push Notification Service) サーバーから携帯電話のデバイス トークンを要求し、アプリにプッシュ通知の受け入れを許可するように指示します。
アプリは、携帯電話のデバイス トークンをアプリに対応するサーバーに転送します。
リモート メッセージは、アプリに対応するサーバーによって生成され、最初に APNs を通過します。
APNs は、応答している電話にリモート通知をプッシュします。
具体的なフローチャートは次のとおりです。
情報処理
9. iOS 開発でエンコードとデコードを実装するには?
キーワード: #エンコード可能 #デコード可能
编码和解码在 Swift 4 中引入了 Encodable 和 Decodable 这两个协议,而 Codable 是 Encodable 和 Decodable 的合集。在 Swift 中,Enum,Struct,Class 都支持 Codable。一个最简单的使用如下:
enum Gender: String, Codable {
case Male = “Male”
case Female = “Female”
}
class User: Codable {
let name: String
let age: Int
let gender: Gender
init(name: String, age: Int, gender: Gender) {
(self.name, self.age, self.gender) = (name, age, gender)
}
}
这样定义完成之后,我们就可以轻易的在 User 及其对应 JSON 数据进行编码和解码,示范代码如下:
let userJsonString = """
{
"name": "Cook",
"age": 58,
"gender": "Male"
}
"""
// 从JSON解码到实例
if let userJSONData = userJsonString.data(using: .utf8) {
let userDecode = try? JSONDecoder().decode(User.self, from: userJSONData)
}
//从实例编码到JSON
let userEncode = User(name: "Cook", age: 58, gender: Gender.Male)
let userEncodedData = try? JSONEncoder().encode(userEncode)
追问:假如 JSON 的键值和对象的属性名不匹配该怎么办?
可以在对象中定义一个枚举(enum CodingKeys: String, CodingKey),然后将属性和 JSON 中的键值进行关联。
追问:假如 class 中某些属性不支持 Codable 该怎么办?
将支持 Codable 的属性抽离出来定义在父类中,然后在子类中配合枚举(enum CodingKeys),将不支持的 Codable 的属性单独处理。
10.谈谈 iOS 开发中数据持久化的方案
关键词: #plist #Preference #NSKeyedArchiver #CoreData
数据持久化就是将数据保存在硬盘中,这样无论是断网还是重启,我们都可以访问到之前保存的数据。iOS 开发中有以下几种方案:
plist。它是一个 XML 文件,会将某些固定类型的数据存放于其中,读写分别通过 contentsOfFile 和 writeToFile 来完成。一般用于保存 App 的基本参数。
Preference。它通过 UserDefaults 来完成 key-value 配对保存。如果需要立刻保存,需要调用 synchronize 方法。它会将相关数据保存在同一个 plist 文件下,同样是用于保存 App 的基本参数信息。
NSKeyedArchiver。遵循 NSCoding 协议的对象就就可以实现序列化。NSCoding 有两个必须要实现的方法,即父类的归档 initWithCoder 和解档 encodeWithCoder 方法。存储数据通过 NSKeyedArchiver 的工厂方法 archiveRootObject:toFile: 来实现;读取数据通过 NSKeyedUnarchiver 的工厂方法 unarchiveObjectwithFile:来实现。相比于前两者, NSKeyedArchiver 可以任意指定存储的位置和文件名。
CoreData。前面几种方法,都是覆盖存储。修改数据要读取整个文件,修改后再覆盖写入,十分不适合大量数据存储。CoreData 就是苹果官方推出的大规模数据持久化的方案。它的基本逻辑类似于 SQL 数据库,每个表为 Entity,然后我们可以添加、读取、修改、删除对象实例。它可以像 SQL 一样提供模糊搜索、过滤搜索、表关联等各种复杂操作。尽管功能强大,它的缺点是学习曲线高,操作复杂。
以上几种方法是 iOS 开发中最为常见的数据持久化方案。除了这些以外,针对大规模数据持久化,我们还可以用 SQLite3、FMDB、Realm 等方法。相比于 CoreData 和其他方案,Realm 以其简便的操作和丰富的功能广受很多开发者青睐。同时大公司诸如 Google 的 Firebase 也有离线数据库功能。其实没有最佳的方案,只有最合适的方案,应该根据实际开发的 App 来挑选合适的持久化方案。