SENI #014
SENI #014
SENI adalah kegiatan yang diprakarsai oleh
由左耳朵耗子--陈皓: Kerjakan setidaknya satu pertanyaan algoritma Leetcode setiap minggu, baca dan komentari setidaknya satu artikel teknis berbahasa Inggris, pelajari setidaknya satu keterampilan teknis, dan bagikan artikel yang berisi opini dan pemikiran. (Artinya, Algoritma, Review, Tip, dan Share disebut sebagai SENI) dan bertahan setidaknya selama satu tahun.
SENI 014
Ini adalah pasal 14
Pertanyaan algoritma algoritma
290. Pola Kata
Kesulitan: Mudah
Diberikan pattern dan string str, temukan apakah str mengikuti pola yang sama.
Di sini ikuti berarti kecocokan penuh, sehingga terdapat bijeksi antara huruf di pattern dan kata tidak kosong di str.
Contoh 1:
**Input:** pattern = `"abba"`, str = `"dog cat cat dog"`
**Output:** true```
**Contoh 2:**
**Input:**pattern = "abba", str = "dog cat cat fish"
Output: false```
Contoh 3:
**Input:** pattern = `"aaaa"`, str = `"dog cat cat dog"`
**Output:** false```
**Contoh 4:**
Input: pattern = "abba", str = "dog dog dog dog"
Output: false```
Catatan:
Anda mungkin berasumsi pattern hanya berisi huruf kecil, dan str berisi huruf kecil yang dipisahkan oleh satu spasi.
Solusi
Bahasa: C
Ketika saya pertama kali melihat algoritma ini, saya merasa dapat diselesaikan dengan kamus, tetapi tidak ada kamus di antara tipe data dasar dalam bahasa C, dan saya tidak dapat memikirkan metode yang sederhana, jadi saya membaca beberapa jawaban, dan dua hari kemudian, berdasarkan pemahaman saya sendiri, saya menerapkannya lagi. Algoritma awalnya adalah sebagai berikut:
bool wordPattern0(char* pattern, char* str) {
int str_len = strlen(str);
int pattern_len = strlen(pattern);
char buffer[str_len + 1];
strcpy(buffer, str);
char *s = strtok(buffer, " ");
char *tables[26] ;
memset(tables, '\0', 26 * sizeof(char *));
int i =0;
for (; s; s = strtok(NULL, " ")) {
if (i>=pattern_len) {
return false;
}
char aa = pattern[i];
char *s1 = tables[aa - 'a'];
if (!s1) {
tables[aa - 'a'] = s;
}
else{
if (strcmp(s, s1)) {
return false;
}
}
i++;
}
if (i!=pattern_len) {
return false;
}
return true;
}
Setelah dijalankan, saya menemukan ada test case yang tidak bisa dilewati, seperti: Ketika char* pattern = “abba”; char*str = “anjing anjing anjing anjing”;
Awalnya saya mengira telah memahami algoritma orang lain, namun saya masih melakukan kesalahan saat menerapkannya. Saya masih belum bisa melakukannya dengan benar. Saya menganalisis alasannya:
Saya membaca persyaratan pertanyaan lagi, dan kemudian saya perhatikan bahwa pola dan str harus memiliki hubungan satu-ke-satu, dan implementasi saya adalah hubungan satu-ke-banyak, dan kamus adalah hubungan satu-ke-banyak, artinya implementasi saya hanya dapat menjamin Untuk huruf yang sama pada posisi pola yang berbeda, hanya dapat dijamin bahwa kata-kata pada posisi yang sama yang sesuai dengan str juga sama. Misalnya, pola a in pertama bersesuaian dengan anjing, dan pola a keempat juga harus bersesuaian dengan anjing. Namun, tidak ada jaminan bahwa untuk kata yang sama di posisi str yang berbeda, hanya ada satu karakter pola yang sesuai dengannya. Implementasi yang ditingkatkan adalah sebagai berikut:
bool wordPattern0(char* pattern, char* str) {
int str_len = strlen(str);
int pattern_len = strlen(pattern);
char buffer[str_len + 1];
strcpy(buffer, str);
char *s = strtok(buffer, " ");
char *tables[26] ;
memset(tables, '\0', 26 * sizeof(char *));
int i =0;
for (; s; s = strtok(NULL, " ")) {
if (i>=pattern_len) {
return false;
}
char aa = pattern[i];
char *s1 = tables[aa - 'a'];
if (!s1) {
tables[aa - 'a'] = s;
}
else{
if (strcmp(s, s1)) {
return false;
}
}
i++;
}
if (i!=pattern_len) {
return false;
}
for (int i =0; i<26; i++) {
if (!tables[i]) {
continue;
}
for (int j =i+1; j<26; j++) {
if (!tables[j]) {
continue;
}
if (!strcmp(tables[i], tables[j])) {
return false;
}
}
}
return true;
}
Faktanya, ini hanyalah menambahkan penilaian yang sesuai satu-ke-satu.
for (int i =0; i<26; i++) {
if (!tables[i]) {
continue;
}
for (int j =i+1; j<26; j++) {
if (!tables[j]) {
continue;
}
if (!strcmp(tables[i], tables[j])) {
return false;
}
}
}
Implementasi berikut patut dipelajari:
Implementasi 1, ini implementasi pertama yang saya lihat, jadi implementasi saya mirip sekali, namun ada perbedaannya. Algoritma ini melakukan penilaian terlebih dahulu sebelum kata dalam str ditambahkan pada tabel sebelumnya. Jika kata-kata dalam str sudah ada di tabel, ia langsung mengembalikan false, sementara implementasi saya terlebih dahulu menambahkan kata-kata dalam str ke tabel, dan akhirnya membandingkan.
bool wordPattern(char* pattern, char* str) {
int str_len = strlen(str);
char buffer[str_len + 1];
strcpy(buffer, str);
char *p = pattern;
char *s = strtok(buffer, " ");
char *table[26];
memset(table, '\0', 26 * sizeof(char *));
do {
if (*p == '\0') return false;
int input = *p++ - 'a';
if (table[input] == NULL) {
////加入table之前先判断table中是否已经存在,如果存在直接返回false
for (int i = 0; i < 26; i++) {
if (table[i] == NULL)
continue;
if (strcmp(table[i], s) == 0)
return false;
}
table[input] = (char *) malloc((strlen(s) + 1) * sizeof(char));
strcpy(table[input], s);
} else {
if (strcmp(table[input], s))
return false;
}
s = strtok(NULL, " ");
} while (s != NULL);
for (int i = 0; i < 26; i++)
free(table[i]);
if (*p != '\0')
return false;
return true;
}
Implementasi 2. Implementasi ini sebenarnya sama persis dengan algoritma sebelumnya. Bedanya, tabel tersebut tidak lagi diisi dengan kata-kata dalam str, melainkan nilai hash. Apalagi algoritma hash ini relatif sederhana, yaitu mengubah kata menjadi bilangan bulat.
bool wordPattern(char* pattern, char* str)
{
unsigned int table[26] = {0};
for (; *pattern && *str; pattern++) {
unsigned int hash = 2139062143;
for(; *str && *str != ' '; str++)
hash = 37 * hash + *str;
if (*str)
str++;
if (!table[*pattern - 'a']) {
////加入table之前先判断table中是否已经存在,如果存在直接返回false
for (int i = 0; i < 26; ++i)
if (table[i] == hash)
return false;
table[*pattern - 'a'] = hash;
} else
if (table[*pattern - 'a'] != hash)
return false;
}
return !*str && !*pattern;
}
Implementasi 3, implementasi ini sedikit lebih rumit, terutama karena tidak menggunakan fungsi strtok dan mengimplementasikannya sendiri.
bool wordPattern2(char* pattern, char* str) {
int patternLength = strlen(pattern);
int tokens = 0;
for (int i = 0; str[i] != 0; i++) {
if (str[i] == ' ') {
tokens++;
}
}
tokens++;
if (patternLength != tokens) {
return false;
}
char *dict[26];
for (int i = 0; i < 26; i++) {
dict[i] = NULL;
}
for (int i = 0; pattern[i] != 0; i++) {
int size = 0;
int capacity = 8;
char *ithTok = malloc(sizeof(char) * 8);
int spacesToSkip = i;
char *stringPointer = str;
while (spacesToSkip) {
if (*stringPointer == ' ') {
spacesToSkip--;
}
stringPointer++;
}
//这个for循环相当于strtok函数,取出str中的单词
for (int i = 0; stringPointer[i] != ' ' && stringPointer[i] != 0; i++) {
ithTok[i] = stringPointer[i];
size++;
if (size >= capacity) {
ithTok = realloc(ithTok, sizeof(char) * capacity * 2);
capacity *= 2;
}
}
//下面的判断和上面两个算法一样
ithTok[size] = 0;
if (dict[pattern[i] - 'a'] == NULL) {
////加入table之前先判断table中是否已经存在,如果存在直接返回false
for (int j = 0; j < 26; j++) {
if (dict[j] != NULL) {
if (strcmp(dict[j], ithTok) == 0) {
return false;
}
}
}
dict[pattern[i] - 'a'] = ithTok;
}
else {
if (strcmp(dict[pattern[i] - 'a'], ithTok) != 0) {
return false;
}
}
}
return true;
}
Implementasi 4: Implementasi ini tidak pernah jelas bagi saya. Meskipun dapat berjalan dengan sukses di LeetCode, menurut saya akan ada masalah tabrakan hash.
#define alphabetSIZE 26 //a~z 26 letters
#define hashtableSIZE 3533 //randomly choose a large prime number
struct Node{
int count;
char *str;
struct Node *next;
};
void initialize_hashtable(struct Node *hashtable)
{
int i;
for(i=0;i<hashtableSIZE;i++)
{
hashtable[i].count = 0;
hashtable[i].next = NULL;
}
}
long hash (char *str)
{
int c;
unsigned long hash = 0;
while(c = *str++)
hash = (hash<<5) + hash + c;
return hash%hashtableSIZE;
}
void hashtable_free(struct Node *hashtable)
{
int i;
for(i=0;i<hashtableSIZE;i++)
{
struct Node *tail = hashtable[i].next;
while(tail)
{
struct Node *temp = tail;
tail = tail->next;
free(temp);
}
}
free(hashtable);
}
bool wordPattern(char* pattern, char* str) {
int alphabet[alphabetSIZE]={0},i,patternSize = strlen(pattern);
struct Node *hashtable = (struct Node *)malloc(sizeof(struct Node)*hashtableSIZE);
initialize_hashtable(hashtable);
struct Node *node;
char buffer[patternSize + 1];
strcpy(buffer, str);
char *word = strtok(buffer," ");
for(i=0;word;i++)
{
if(i>=patternSize) return 0; // the number of word in str is more than the length of pattern
node = &hashtable[hash(word)];
if(alphabet[pattern[i]-'a'] != node->count)
return 0;
alphabet[pattern[i]-'a'] = node->count = i+1;
word = strtok(NULL," ");
}
hashtable_free(hashtable);
return i==patternSize?1:0;
}
Implementasi 5: Saya punya waktu untuk memahami algoritma berikut.
bool wordPattern4(char* pattern, char* str){
bool follow = false;
int sl = strlen(str);
int pl = strlen(pattern);
char c = ' ';
char** strArray;
int i = 0;
int j = 0;
int k = 0;
int temp = 0;
if ((pl==1)) return true;
bool endofstr = false;
strArray = malloc(pl*sizeof(char*));
for (j = 0; endofstr == false; j++){
strArray[j] = malloc(10*sizeof(char));
k = 0;
while( i < sl){
if (str[i]!=c)
strArray[j][k] = str[i];
else{
strArray[j][k] = '\0';
i++;
break;
}
k++;
i++;
}
if (i == sl) endofstr = true;
printf("%d sub-string, %s\n", j, strArray[j]);
}
// for (i = 0; i <= j; i++){
// printf("%d:%s\n",i,strArray[i]);
// }
if (pl != j) return false;
for (i = 0; i < pl-1; i++){
for (k = i+1; k < pl; k++){
temp = strcmp(strArray[i], strArray[k]);
if ((pattern[i] == pattern[k]) && !temp)
follow = true;
else if ((pattern[i] != pattern[k]) && temp)
follow = true;
else
return false;
}
}
return follow;
}
Ulasan
Artikel ini berasal dari https://medium.com/@JimmyMAndersson/ios-development-and-the-wrong-kind-of-mvc-4e3e2decb82e, Bagian pertama artikel ini membahas tentang cara menggunakan pola desain MVC dalam pengembangan iOS, tetapi kemudian membahas tentang pewarisan. Kualitas blognya rata-rata.
Pengembangan iOS dan Jenis MVC yang Salah
Saat memulai sebagai Pengembang iOS, Anda akan mendengar banyak tentang Pola MVC (Model-View-Controller) dan cara terbaik saat mengembangkan aplikasi seluler. Pola MVC adalah cara yang baik untuk memikirkan masalah tertentu ketika mengembangkan aplikasi dengan antarmuka pengguna grafis. Ini memberi Anda cara berpikir yang memungkinkan Anda membagi kode Anda menjadi paket-paket yang dapat dikelola, di mana setiap paket akan menangani domain masalahnya sendiri.
Saat Anda memulai sebagai pengembang iOS, Anda akan mendengar banyak tentang pola MVC (Model-View-Controller) dan cara terbaik saat mengembangkan aplikasi seluler. Pola MVC adalah cara yang baik untuk memikirkan isu-isu tertentu ketika mengembangkan aplikasi dengan antarmuka pengguna grafis. Ini memberi Anda cara berpikir yang memungkinkan Anda membagi kode Anda menjadi paket-paket yang dapat dikelola, di mana setiap paket akan menangani domain masalahnya sendiri.
Paket apa yang sedang Anda bicarakan?
Ada tiga paket konseptual berbeda di MVC yang seharusnya menangani masalah seperti: Ada tiga paket konsep berbeda di MVC yang seharusnya menangani masalah seperti ini:
Model: Paket Model menangani apa yang oleh pengembang disebut Logika Bisnis. Apa itu Logika Bisnis, bergantung pada aplikasi khusus Anda, namun Anda dapat menggeneralisasikannya dengan mengartikan “hal-hal dalam aplikasi Anda yang mengelola data Anda dan menjaganya tetap terkini dan akurat”. Artinya, tugas Model adalah melacak posisi karakter game Anda, mengetahui berapa kali Anda memutar lagu di iTunes, menyimpan foto yang telah diedit ke format yang dapat Anda bagikan di media sosial… Model inilah yang membuat aplikasi Anda cerdas.
Paket model menangani apa yang oleh pengembang disebut logika bisnis. Logika bisnis bergantung pada aplikasi spesifik Anda, namun Anda dapat meringkasnya sebagai “apa yang ada di aplikasi Anda yang mengelola data dan menjaganya tetap terkini dan akurat”. Ini berarti model-modelnya berfungsi untuk melacak lokasi karakter permainan Anda, mengetahui berapa kali Anda memutar lagu di iTunes, menyimpan foto-foto yang telah diedit dalam format yang dapat Anda bagikan di media sosial… model-model yang membuat aplikasi Anda lebih pintar. .
Lihat: View pada aplikasi MVC akan merepresentasikan data yang dikirimkan dari Model dan menampilkannya kepada pengguna. Suatu Tampilan seharusnya “bodoh”, artinya ia hanya mengetahui cara menggambar atau menampilkan data yang diterimanya namun tidak boleh melakukan penyesuaian apa pun atau bahkan mengetahui jenis datanya.
Tampilan pada aplikasi MVC akan mewakili data yang dikirim dari model dan menampilkannya kepada pengguna. Tampilannya harus “bodoh”, artinya hanya mengetahui cara menggambar atau menampilkan data yang diterimanya, namun tidak boleh melakukan penyesuaian apa pun, atau bahkan mengetahui jenis datanya.Pengendali: Paket Controller inilah yang membuat aplikasi menyenangkan untuk digunakan. Ini bertanggung jawab untuk menafsirkan ketukan, tekanan, klik, dan kemiringan perangkat Anda, dan mengirimkannya untuk membuat pembaruan pada Model atau Tampilan. Pengontrol harus “tipis”, yang berarti bahwa ia harus menafsirkan input dan meneruskan sinyal yang sesuai ke tempat yang tepat, agar aplikasi dapat melakukan penyesuaian sesuai dengan apa yang diharapkan pengguna. Ketika informasi mengalir antara Model dan Tampilan, Pengendali akan bertindak sebagai penghubung. Paket pengontrol membuat aplikasi menyenangkan untuk digunakan. Ini bertanggung jawab untuk menafsirkan klik, penekanan, klik, dan kemiringan perangkat dan mengirimkannya ke model atau tampilan untuk pembaruan. Pengontrolnya harus “tipis”, artinya harus menafsirkan masukan dan mengirimkan sinyal yang sesuai ke tempat yang tepat sehingga aplikasi menyesuaikan dengan harapan pengguna. Pengontrol bertindak sebagai penghubung ketika informasi mengalir antara model dan tampilan.

Jadi, bagaimana dengan iOS dan “jenis MVC yang salah”? Jadi, bagaimana dengan iOS dan “MVC yang salah”? Saat mengembangkan untuk iOS, Pola Model-View-Controller sering kali berbentuk Pola MassiveViewController. Saat mengembangkan untuk iOS, pola Model-View-Controller sering kali berbentuk pola MassiveViewController. Lihatlah contoh kelas Controller ini.
import UIKit
class MassiveViewController: UIViewController {
private var stackView: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
stack.alignment = .center
stack.distribution = .fillProportionally
return stack
}()
private var photoView = UIImageView(image: UIImage(named: "SomeImage"))
private var descriptionLabel: UILabel = {
let label = UILabel()
label.textColor = .black
label.text = "Descriptive text"
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
self.stackView.addArrangedSubview(self.photoView)
self.stackView.addArrangedSubview(self.descriptionLabel)
self.view.addSubview(self.stackView)
self.stackView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
self.stackView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
self.stackView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor).isActive = true
self.stackView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor).isActive = true
let photoTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handlePhotoViewTap(_:)))
self.photoView.addGestureRecognizer(photoTapRecognizer)
let labelTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleDescriptionLabelTap(_:)))
self.descriptionLabel.addGestureRecognizer(labelTapRecognizer)
}
@objc private func handlePhotoViewTap(_ sender: UITapGestureRecognizer) {
// Do something mind blowing
}
@objc private func handleDescriptionLabelTap(_ sender: UITapGestureRecognizer) {
// Do some other cool thing
}
}
MassiveViewController.swift
Saya yakin ini terlihat familier dan Anda mungkin pernah menulis sesuatu seperti ini, saya tahu saya pernah. Jadi apa masalahnya di sini? Saya yakin ini terlihat familier, Anda mungkin pernah menulis sesuatu seperti ini, saya tahu saya pernah. Jadi apa masalahnya?
Masalah dengan file ini adalah tidak memisahkan kekhawatiran. Itu tidak mengadopsi Pola MVC, dan oleh karena itu kelasnya menjadi sangat besar dan akan sangat sulit dipertahankan jika kita melakukan perubahan pada aplikasi di masa depan. Masalah dengan dokumen ini adalah tidak memisahkan kekhawatiran. Tidak mengikuti pola MVC, sehingga kelas menjadi sangat besar dan akan sulit dipertahankan jika kita melakukan perubahan pada aplikasi di kemudian hari.
Melihat kodenya, Anda dapat melihat bahwa ini seharusnya merupakan kelas Controller, namun ia juga mengambil tanggung jawab View, menyiapkan elemen visual yang hanya ada untuk menampilkan data kepada pengguna. Melihat kodenya, Anda dapat melihat bahwa ini seharusnya merupakan kelas Controller, tetapi juga mengambil tanggung jawab tampilan, menyiapkan elemen visual yang hanya digunakan untuk menampilkan data kepada pengguna.
Bagaimana cara memperbaikinya?
class PlainMVCView: UIView {
private var stackView: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
stack.alignment = .center
stack.distribution = .fillProportionally
return stack
}()
private var photoView = UIImageView(image: UIImage(named: "SomeImage"))
private var descriptionLabel: UILabel = {
let label = UILabel()
label.textColor = .black
label.text = "Descriptive text"
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
private func setupView() {
self.stackView.addArrangedSubview(self.photoView)
self.stackView.addArrangedSubview(self.descriptionLabel)
self.addSubview(self.stackView)
self.stackView.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor).isActive = true
self.stackView.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor).isActive = true
self.stackView.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor).isActive = true
self.stackView.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor).isActive = true
}
public func addPhotoViewGestureRecognizer(_ recognizer: UIGestureRecognizer) {
self.photoView.addGestureRecognizer(recognizer)
}
public func addDescriptionLabelGestureRecognizer(_ recognizer: UIGestureRecognizer) {
self.descriptionLabel.addGestureRecognizer(recognizer)
}
}
BiasaMVCView.swift
class PlainMVCViewController: UIViewController {
override func loadView() {
self.view = PlainMVCView()
}
override func viewDidLoad() {
let photoTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handlePhotoViewTap(_:)))
// This will work for now, because we know the specific type of the view.
// However, force casting should be avoided in general. More on that in the next example.
(self.view as! PlainMVCView).addPhotoViewGestureRecognizer(photoTapRecognizer)
let labelTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleDescriptionLabelTap(_:)))
(self.view as! PlainMVCView).addDescriptionLabelGestureRecognizer(labelTapRecognizer)
}
@objc private func handlePhotoViewTap(_ sender: UITapGestureRecognizer) {
// Do something mind blowing
}
@objc private func handleDescriptionLabelTap(_ sender: UITapGestureRecognizer) {
// Do something a little less mind blowing, but still cool
}
}
PlainMVCViewController.swift
Di atas adalah kode yang sama, tetapi dipecah menjadi beberapa bagian yang sesuai dengan satu tanggung jawab MVC. Perhatikan bahwa kelas Pengendali menyusut sedikit. Namun, kelas View berukuran hampir sama dengan file aslinya. Jadi apa yang sebenarnya kita menangkan? Ini adalah bagian tersulit tentang MVC, karena jika Anda perlu menulis lebih banyak kode, sepertinya Anda tidak selalu mendapatkan hasil darinya. Di atas adalah kode yang sama, tetapi dibagi menjadi beberapa bagian yang sesuai dengan tanggung jawab masing-masing MVC. Perhatikan bahwa kelas pengontrol sedikit menyusut. Namun ukuran kelas tampilannya hampir sama dengan ukuran file aslinya. Jadi apa sebenarnya yang kita menangkan? Ini adalah bagian yang lebih rumit dari MVC karena jika Anda perlu menulis lebih banyak kode, mungkin Anda tidak selalu mendapatkan hasil yang diharapkan.
Dengan memfaktorkan ulang kode seperti ini, ia akan mematuhi Prinsip Pemisahan Kekhawatiran. Kelas Controller hanya menangani masalah Controller dan, selain menginisialisasi objek View yang sesuai, sepenuhnya terpisah dari segala sesuatu yang berhubungan dengan menampilkan sesuatu kepada pengguna. Ini akan menjadi fitur berharga seiring berkembangnya aplikasi, karena suatu hari Anda mungkin ingin mengganti kelas Controller dengan yang baru dan lebih baik. Jika Pengontrol dipisahkan dari Tampilan, itu berarti lebih sedikit pekerjaan untuk Anda dan kemungkinan bug dan kesalahan berkurang. Perhatikan juga bahwa kami mendefinisikan dua metode dalam Tampilan kami untuk membantu Pengendali yang mungkin ingin mendengarkan peristiwa tap di salah satu bagian tampilan yang “menarik”, agar tidak melanggar Hukum Demeter. Dengan memfaktorkan ulang kode dengan cara ini, ia akan mematuhi prinsip pemisahan kepentingan. Kelas pengontrol hanya menangani masalah pengontrol dan sepenuhnya terpisah dari segala sesuatu yang terkait dengan menampilkan konten kepada pengguna, kecuali menginisialisasi objek tampilan yang sesuai. Ini akan menjadi fitur berharga seiring berkembangnya aplikasi Anda, karena suatu hari Anda mungkin ingin mengganti kelas pengontrol dengan kelas pengontrol baru yang lebih baik. Jika pengontrol dipisahkan dari tampilan, Anda memiliki lebih sedikit pekerjaan yang harus dilakukan dan kemungkinan bug dan kesalahan berkurang. Perhatikan juga bahwa kami mendefinisikan dua metode dalam tampilan untuk membantu pengontrol yang mungkin ingin mendengarkan peristiwa di bagian tampilan yang “menarik” agar tidak melanggar Hukum Demeter.
Apakah itu saja?
Nah, jika kita beralih dari aspek MVC murni, Anda dapat melihat bahwa kelas Controller kita sangat bergantung pada satu implementasi kelas View. Ini bertentangan dengan prinsip ekstensibilitas dan penggandengan, jadi mari kita coba memperbaikinya dengan beberapa obat generik dan abstraksi.
Nah, jika kita meninggalkan sisi MVC murni, Anda dapat melihat bahwa kelas pengontrol kita sangat bergantung pada implementasi tunggal kelas tampilan. Ini bertentangan dengan prinsip ekstensibilitas dan penggandengan, jadi mari kita coba memperbaikinya dengan beberapa obat generik dan abstraksi.
protocol Controller {
associatedtype ViewType
var currentView: ViewType { get }
}
class BaseViewController<ViewType: UIView>: UIViewController, Controller {
override func loadView() {
self.view = ViewType()
}
var currentView: ViewType {
/*
Because of how we implemented loadView(), this cast should never fail.
However, linters and such tools may complain if we force cast and we want
to be good developers, so we perform Optional binding just to be safe.
*/
if let view = self.view as? ViewType {
return view
} else {
let view = ViewType()
self.view = view
return view
}
}
}
BaseController.swift
protocol View {
func setupView()
}
class BaseView: UIView, View {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
func setupView() {
// Use this method to do any set up that is shared between all your views,
// for example drawing backgrounds, etc.
}
}
BaseView.swift
@objc protocol MVCViewActionDelegate: AnyObject {
func photoViewWasTapped()
func descriptionLabelWasTapped()
}
class MVCView: BaseView {
private var stackView: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
stack.alignment = .center
stack.distribution = .fillProportionally
return stack
}()
private var photoView = UIImageView(image: UIImage(named: "SomeImage"))
private var descriptionLabel: UILabel = {
let label = UILabel()
label.textColor = .black
label.text = "Descriptive text"
return label
}()
public weak var actionDelegate: MVCViewActionDelegate?
override func setupView() {
super.setupView()
self.stackView.addArrangedSubview(self.photoView)
self.stackView.addArrangedSubview(self.descriptionLabel)
self.addSubview(self.stackView)
self.stackView.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor).isActive = true
self.stackView.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor).isActive = true
self.stackView.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor).isActive = true
self.stackView.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor).isActive = true
let photoTapEvent = UITapGestureRecognizer(target: self.actionDelegate, action: #selector(MVCViewActionDelegate.photoViewWasTapped))
self.photoView.addGestureRecognizer(photoTapEvent)
let labelTapEvent = UITapGestureRecognizer(target: self.actionDelegate, action: #selector(MVCViewActionDelegate.descriptionLabelWasTapped))
self.descriptionLabel.addGestureRecognizer(labelTapEvent)
}
}
MVCView.swift
class MVCViewController: BaseViewController<MVCView>, MVCViewActionDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.currentView.actionDelegate = self
}
func photoViewWasTapped() {
// Do some mind blowing stuff
}
func descriptionLabelWasTapped() {
// Do some other cool junk
}
}
MVCViewController.swift
Sekarang kita tiba-tiba mendapatkan 4 file (7 file, jika Anda memisahkan protokol ke dalam filenya sendiri). Apakah ini benar-benar perlu?? Sekarang kami tiba-tiba memiliki 4 file (7 file jika Anda memisahkan protokol ke dalam filenya sendiri). Apakah ini benar-benar perlu??Apa yang terjadi di sini adalah kami mengabstraksi segala kemungkinan perilaku umum dari kelas Controller dan View, dan memasukkannya ke dalam kelas Base. Kelas dasar sebenarnya tidak dimaksudkan untuk diinisialisasi sendiri, namun mendefinisikan dan menyediakan operasi dan aliran umum yang akan digunakan bersama oleh setiap subkelas. Apa yang terjadi di sini adalah kita mengabstraksi segala kemungkinan perilaku publik dari kelas pengontrol dan tampilan dan memasukkannya ke dalam kelas dasar. Kelas dasar sebenarnya tidak dimaksudkan untuk menginisialisasi dirinya sendiri, melainkan mendefinisikan dan menyediakan operasi dan aliran umum yang akan digunakan bersama oleh setiap subkelas.
Ini berarti bahwa semua subkelas Controller dan View di masa depan akan lebih kecil, lebih mudah dibaca, dan lebih mudah dipelihara karena Anda tidak perlu menduplikasi semua perilaku bersama. Anda akan berterima kasih kepada bintang-bintang jika Anda memutuskan untuk, misalnya, mengubah gambar latar belakang semua tampilan. Daripada mengubah baris yang sama di 20 kelas, Anda hanya perlu mengubahnya di satu kelas Dasar. Mudah! Ini berarti bahwa semua subkelas pengontrol dan tampilan di masa depan akan lebih kecil, lebih mudah dibaca, dan lebih mudah dipelihara karena Anda tidak perlu menduplikasi semua perilaku bersama. Misalnya, jika Anda memutuskan untuk mengubah gambar latar belakang semua tampilan, Anda akan berterima kasih kepada bintang-bintang. Tidak perlu mengubah baris yang sama di 20 kelas, cukup di satu kelas dasar. Sederhana!
Ini juga berarti bahwa jika Anda ingin mengubah jenis kelas View yang dikaitkan dengan Kontroler tertentu, maka pekerjaan yang dilakukan akan jauh lebih sedikit. Karena kita mengadopsi protokol MVCViewActionDelegate, mengganti kelas View semudah mengubah nama kelas dalam batasan generik, dan mengimplementasikan metode delegasi tindakan baru (jika ada yang menyertai View baru). Ini juga berarti bahwa jika Anda ingin mengubah tipe kelas tampilan yang terkait dengan pengontrol tertentu, Anda memiliki lebih sedikit pekerjaan. Karena kita telah mengadopsi protokol MVCViewActionDelegate, berpindah kelas tampilan semudah mengubah nama kelas dalam batasan umum dan menerapkan metode delegasi tindakan baru (jika tampilan baru dilengkapi dengan tindakan apa pun).
Mudah-mudahan, cara berpikir tentang MVC saat mengembangkan aplikasi iOS Anda menjadi tidak terlalu menakutkan dan menjadi lebih jelas setelah membaca ini. Jangan ragu untuk berkomentar jika ada pertanyaan, dan ikuti untuk mendapatkan notifikasi tentang artikel selanjutnya. Semoga setelah membaca artikel ini, pendekatan pemikiran tentang MVC saat mengembangkan aplikasi iOS menjadi lebih jelas dan tidak terlalu rumit. Jika Anda memiliki pertanyaan, silakan tinggalkan komentar dan nantikan artikel selanjutnya.
Kiat
imgView.contentMode = UIViewContentModeScaleAspectFill; Jika Anda menyetel properti ini, setel imgView.clipsToBounds = YES; jika tidak, jika gambarnya tidak sesuai, maka gambar tersebut akan hilang dari pandangan.
Saya baru-baru ini membuat permintaan untuk menyetel gambar untuk UIImageView. Ukuran UIImageView adalah 375X88 atau 375X64, dan ukuran gambar 1000X400. Lebar gambar harus tidak dapat dicegat, dan tingginya hanya dapat dicegat dari atas gambar. Saya menyelesaikannya dengan tangkapan layar saya sendiri. Cara pelaksanaannya adalah sebagai berikut:
CGFloat height = UIImageView.height / (UIImageView.width) * image.size.width;
CGImageRef imageRef = image.CGImage;
CGImageRef imageRefRect = CGImageCreateWithImageInRect(imageRef, CGRectMake(0, image.size.height - height, image.size.width, height));
UIImage *newImage = [[UIImage alloc] initWithCGImage:imageRefRect];
CGImageRelease(imageRefRect);
UIImageView.image = newImage;
Jadi bisakah persyaratan ini dipenuhi menggunakan contentMode UIImageView? Meskipun model ini telah digunakan sebelumnya, namun belum diringkas. Saya akan menggunakan kesempatan ini untuk merangkumnya: Demo uji: https://github.com/dandan2009/UIImageViewContentMode
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//最近做了个需求,给UIImageView的设置图片,UIImageView的大小是375X88或375X64,图片的大小是1000X400,要求图片宽度不能截取,高度只能截取图片的顶部,我是用自己截图完成的,实现方法如下的:
// 伪代码 image就是图片
// CGFloat height = UIImageView.height / (UIImageView.width) * image.size.width;
// CGImageRef imageRef = image.CGImage;
// CGImageRef imageRefRect = CGImageCreateWithImageInRect(imageRef, CGRectMake(0, image.size.height - height, image.size.width, height));
// UIImage *newImage = [[UIImage alloc] initWithCGImage:imageRefRect];
// CGImageRelease(imageRefRect);
// UIImageView.image = newImage;
// 那么用UIImageView的contentMode 能不能完成这个需求呢?虽然这个模式之前也用过,但是没有总结过,趁着这个机会总结一下
UIScrollView *scrollView = [[UIScrollView alloc] init];
scrollView.frame = self.view.bounds;
scrollView.backgroundColor = [UIColor greenColor];
scrollView.contentSize = CGSizeMake(self.view.frame.size.width, 1200);
[self.view addSubview:scrollView];
UIImage *iconImage = [UIImage imageNamed:@"icon"];
CGFloat imageWidth = iconImage.size.width;
CGFloat imageheight = iconImage.size.height;
UILabel *lab = [[UILabel alloc] initWithFrame:CGRectMake(10, 30, 370, 20)];
lab.text = @"1 UIImageView的尺寸刚好等于image的尺寸";
[scrollView addSubview:lab];
UIView *base1 = [[UIView alloc] initWithFrame:CGRectMake(lab.left, lab.bottom + 5, imageWidth + 10, imageheight + 10)];
base1.backgroundColor = [UIColor redColor];
[scrollView addSubview:base1];
UIImageView *imageView1 = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, imageWidth , imageheight )];
imageView1.image = iconImage;
imageView1.backgroundColor = [UIColor orangeColor];
[base1 addSubview:imageView1];
UILabel *labsec1 = [[UILabel alloc] initWithFrame:CGRectMake(base1.left, base1.bottom + 10, 370, 20)];
labsec1.text = @"2 以下测试是UIImageView的尺寸小于image的尺寸";
[scrollView addSubview:labsec1];
/// 默认contentMode UIViewContentModeScaleToFill
// 可以看到这个是把图片的拉伸或压缩,使图片的大小刚好等于imageview的大小
UILabel *lab2 = [[UILabel alloc] initWithFrame:CGRectMake(labsec1.left, labsec1.bottom + 10, 370, 20)];
lab2.text = @"2.1 默认contentMode UIViewContentModeScaleToFill";
[scrollView addSubview:lab2];
UIView *base2 = [[UIView alloc] initWithFrame:CGRectMake(lab2.left, lab2.bottom + 5, imageWidth + 10, imageheight + 10)];
base2.backgroundColor = [UIColor redColor];
[scrollView addSubview:base2];
UIImageView *imageView2 = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, imageWidth-20 , imageheight-40 )];
imageView2.backgroundColor = [UIColor orangeColor];
imageView2.image = iconImage;
[base2 addSubview:imageView2];
///ModeScaleAspectFit 图片在不超出UIImageView的情况下,保证图片不变形,就有可能导致UIImageView不被image冲满,有空白留出
UILabel *lab3 = [[UILabel alloc] initWithFrame:CGRectMake(base2.left, base2.bottom + 10, 370, 20)];
lab3.text = @"2.2 contentMode == ModeScaleAspectFit";
[scrollView addSubview:lab3];
UIView *base3 = [[UIView alloc] initWithFrame:CGRectMake(lab3.left, lab3.bottom + 5, imageWidth + 10, imageheight + 10)];
base3.backgroundColor = [UIColor redColor];
[scrollView addSubview:base3];
UIImageView *imageView3 = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, imageWidth-20 , imageheight-40 )];
imageView3.contentMode = UIViewContentModeScaleAspectFit;
imageView3.image = iconImage;
imageView3.backgroundColor = [UIColor orangeColor];
[base3 addSubview:imageView3];
/// ModeScaleAspectFill 图片在保证UIImageView被填满的情况下 ,保证图片不变形,就有可能导致image超出UIImageView
UILabel *lab4 = [[UILabel alloc] initWithFrame:CGRectMake(base3.left, base3.bottom + 10, 370, 20)];
lab4.text = @"2.3 contentMode == ModeScaleAspectFill";
[scrollView addSubview:lab4];
UIView *base4 = [[UIView alloc] initWithFrame:CGRectMake(lab4.left, lab4.bottom + 5, imageWidth + 10, imageheight + 10)];
base4.backgroundColor = [UIColor redColor];
[scrollView addSubview:base4];
UIImageView *imageView4 = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, imageWidth-20 , imageheight-40 )];
imageView4.backgroundColor = [UIColor orangeColor];
imageView4.contentMode = UIViewContentModeScaleAspectFill;
imageView4.image = iconImage;
[base4 addSubview:imageView4];
//ModeBottom 图片在保证UIImageView被填满, 且UIImageView底部不被超出的情况下,image按自己的实际大小完全展示,有可能导致image超出UIImageView
UILabel *lab5 = [[UILabel alloc] initWithFrame:CGRectMake(base4.left, base4.bottom + 10, 370, 20)];
lab5.text = @"2.4 contentMode == ModeBottom";
[scrollView addSubview:lab5];
UIView *base5 = [[UIView alloc] initWithFrame:CGRectMake(lab5.left, lab5.bottom + 35, imageWidth + 10, imageheight + 10)];
base5.backgroundColor = [UIColor redColor];
[scrollView addSubview:base5];
UIImageView *imageView5 = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, imageWidth-20 , imageheight-40 )];
imageView5.backgroundColor = [UIColor orangeColor];
imageView5.contentMode = UIViewContentModeBottom;
imageView5.image = iconImage;
[base5 addSubview:imageView5];
}


Bagikan
Ringkaslah perasaan mengerjakan pertanyaan algoritma:
Minggu lalu, saya mengerjakan soal algoritma selama hampir sehari, tetapi saya tidak menyelesaikannya. Aku terus saja menggaruknya. Efisiensinya sangat rendah, jadi di masa depan, saya akan membutuhkan waktu hingga setengah jam untuk menyelesaikan sebuah pertanyaan. Jika saya tidak dapat menyelesaikannya, saya akan melihat penerapan orang lain, dan kemudian menerapkannya kembali sendiri. Minggu ini saya mengikuti rutinitas ini.
Mengapa saya tidak menyelesaikan pertanyaan ini minggu ini?
- Tidak menguasai pengetahuan dasar bahasa C dan perpustakaan terkait, seperti strcpy, strtok, strcmp 2 Namun saya masih terlalu sedikit berlatih dan melakukan terlalu sedikit. Saya masih perlu membaca lebih banyak buku.
Saya pikir saya sudah memahami algoritma orang lain, namun saya masih membuat kesalahan saat menerapkannya sendiri, jadi saya harus berlatih!
Sekarang karena semakin banyak pertanyaan di LeetCode, hampir tidak mungkin untuk menyelesaikan semuanya. Apa yang harus saya lakukan? Menurut saya, bisa jadi seperti ini: Klasifikasikan pertanyaan-pertanyaannya, kerjakan beberapa dari setiap jenis pertanyaan terlebih dahulu, dan kemudian rangkum arah berpikir untuk menyelesaikan pertanyaan-pertanyaan tersebut. Anda dapat mengerjakan beberapa pertanyaan setiap minggu sesuai dengan situasi Anda sendiri. Lagi pula, Anda tidak bisa menggunakan kekerasan atau kebuntuan. Anda harus kembali pada niat awal yaitu melatih cara berpikir. Satu pertanyaan memakan waktu setengah jam 1 Jika Anda tidak dapat menemukan cara melakukannya dalam 0 menit, lihat saja ide orang lain, terapkan sendiri, lalu lihat jawaban luar biasa orang lain. Saat mengerjakan soal, Anda harus segera menyelesaikan beberapa algoritma dasar (seperti pengurutan, pencarian, pohon biner, grafik, dll.) dan membaca buku algoritma.
What to read next
Want more posts about ARTS?
Posts in the same category are usually the best next step for reading more on this topic.
View same categoryWant to keep following #iOS?
Tags are useful for related tools, specific problems, and similar troubleshooting notes.
View same tagWant to explore another direction?
If you are not sure what to read next, return to the homepage and start from categories, topics, or latest updates.
Back home