每天梳理1~2個問題,堅持一下(五)

今天回來補下10月的部分,簡書9月10月的時候沒法寫文章,并且這段時間趕進度天天加班,所以留下了很多天沒有更新過,大致記了下部分問題,慢慢補上去。

19.10.08

構建環信好友體系

環信自有的體系無法很輕易地擴展我們的業務,主要體現在主鍵關聯、業務姓名、業務頭像、好友關系、群關系等等,需要我們自己去設計一套關聯方案使得環信體系與我們業務建立聯系。

關聯對后端來說比較容易,對兩者主鍵進行關聯即可建立兩個對象間的聯系,但對前端來說,要考慮一個實時更新的問題,即有更新則需盡可能快速反應到頁面上,例如好友更新了一個姓名,在聊天列表中我希望盡可能快地獲取到這個信息,再例如群里加入了一個人,我需要立刻就了解到這個人在我們業務中的信息。這類更新往往是一個被動知道的狀態,即更新者不會也不可能進行廣播消息,我們能獲取的途徑有兩種,第一種像服務器請求這個人的最新信息,第二種通過環信消息系統。第一種的劣勢很明顯,因為是被動狀態請求時機無法獲知,同時會對服務器產生負擔;第二種前面已經說了不可能進行廣播,所以需要把握更新的時機。

目前項目中我們的設計如下(分為2步):

1.打開App時向服務器請求數據,進行一次好友更新維護,并同步到本地數據庫。這個步驟有兩個目的:a.斷網狀態保證能正常顯示數據;b.處理無法實時更新時消息列表信息顯示的問題。這個保證了歷史狀態(你更新的這一刻)最新。

2.每次發送消息時帶上自身業務的識別信息放在ext中,當ext內容與緩存模型不一致時,更新緩存模型,并同步到數據庫。我可能不知道群中新加入發言的那個人他在我們業務中是誰,但他肯定知道他自己是誰。發送者知道自身是誰,基于這點我們可以解決實時更新的問題,只要這個人給我發送消息,我就知道這個人是誰,在App運行中去實時更新這個人的狀態。但他的弊端也是有的,就是無發言時我們無法更新其狀態,這時就需要1來去解決這個問題。這個保證了實時狀態的最新。

上述1和2的結合也會存在部分空檔期:App運行階段消息列表中某個人更新了狀態并且未向你發送消息,那么他的狀態更新則會推遲到下一次App啟動。

19.10.09

外部業務如何并入現有業務線

這里還以環信的場景為例,在會話列表中,我們維護的不單單只是環信的一組會話列表,還有我們自有的業務場景,例如系統消息、紅包信息快捷操作等等,當這些業務和環信這種第三方業務混在一起時我們應該如何進行操作?

操作前要明確一點,任何其它業務和自有業務的混合都應該是以自有業務為主,其它業務為輔,也就意味著如果強迫一方做出改變,那么改變的一方不應該是我們的業務體系。

我們需要考慮兩點,一是方式二是時機。

方式指的是我們如何搭建一座橋梁,讓三方業務通過這座橋梁并入到自有業務體系中,而又不影響二者的內在邏輯。比較好的方式就是模型的組合并入。以開頭的場景來說,環信自有模型為A,我們自有業務模型為B,為了使A和B達到并線并統一外在,可以使用模型C,C中引用A或B,對外統一用模型C操作邏輯,區分則以引用的A或B的存在為條件。這樣一來不改變也不打破原有的業務體系,通過組合包裝,使得兩個體系并入在一起。

時機指的是我們在何時進行并入操作。一般來說,這個時機是產生模型并且沒有進行處理的時刻。還以上述場景為例,當我調用環信的方法拿到所有的會話列表時,用模型C包裝原始模型,包裝完畢之后環信的業務線就需要開始并入到我們自有的業務線中(也就是整合模型列表),此時環信業務停止,開展自有業務。業務停止并不意味著不再管環信的業務,而是以自己業務的模型去處理,不再以環信的模型處理其業務。

19.10.10

繼承環信好友頭像和姓名的更新

這部分三端討論了不少時間,在之前的設計中又進行了一些優化和改進。

主要數據:好友群組等信息
次要數據:群成員頭像姓名映射信息

1.本地數據庫作為數據支撐。每次使用App時都會同步一遍主要數據(數據同步點1,主動),同步完成后進行增量更新,隨后釋放內存,每次取值從數據庫中拿出。(此處數據庫是做了優化,拿出過的值在一定時間內會保存在內存中,避免了反饋存取);

2.發送消息攜帶此刻此人的信息(數據同步點2,被動)。由于App只更新了一次數據信息,所以在App運行的過程中,需要合適的點去進行數據庫的更新和同步,這個點就在信息的傳遞時機,例如收發消息,推送等等;

3.單個信息源查詢(數據同步點2,被動)。當出現遺留數據未同步到數據庫時,便調用單個信息源查詢接口,查詢單個信息,保存在數據庫中。

19.10.11

Realm主鍵問題

createOrUpdateInRealm:withValue

這個方法作為增量更新方法的使用前提是需要指定了主鍵才能使用,如果不指定則會報錯。
一個場景:每個用戶在每個群中都有不同的昵稱,如果我不想做多表關聯如何使用這個方法?群ID或者用戶ID此時并不是唯一,并不能作為主鍵使用。那么我們如何保證主鍵唯一性?可以使用群ID和用戶ID拼接起作為主鍵,群ID和用戶ID拼接之后值唯一,可以作為增量更新的依據。

19.10.14

后臺返回的數據類型

不同的接口返回類型可能會有很多種,我們目前返回類型各種各樣,并沒有統一的界定。前兩天的一次改動讓我認識到這里的返回類型需要做一個統一的規定:全部使用字典類型。之前的一個接口由于簡單,只需要一串id,后臺直接把id拋了回來,以至于后期加字段時必須要重新改變數據結構或者重新寫接口,因為當前的字符串類型無法進行擴展,這就大大增加了時間成本,對已經上線的App來說增加了不必要的風險。修改數據結構對于后臺和前端來說都是非常不友好的一件事,不到萬不得已不應該去修改結構,那么從一開始就使用最容易擴展的結構才是正確的選擇。

19.10.15
layer.borderColor處在當前視圖的最上層

有如下代碼:

UIView *backView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 150, 150)];
    backView.backgroundColor = [UIColor lightGrayColor];
    backView.layer.borderColor = [UIColor redColor].CGColor;
    backView.layer.borderWidth = 5;
    [self.view addSubview:backView];
    
    //
    UIView *maskView = [[UIView alloc] initWithFrame:CGRectMake(-50, -50, 100, 100)];
    maskView.backgroundColor = [UIColor yellowColor];
    [backView addSubview:maskView];

達到的效果如圖所示:


Border.png

可以看到這么一個現象,父視圖A如果給了border,那么在當前這個父視圖上,此border的層級為最高,任何加載到A上的子視圖都無法遮擋border。

那么子視圖的border是否遵循層級關系呢?我給子視圖加上border,效果如下:


子視圖border.png

可以看到,子視圖的border并沒有因為層級關系處在上層而遮擋到父視圖的border,父視圖的border依舊在層級最高位。

那么單純的Layer是否會影響到呢?猜測是不會,因為view其實操作的就是layer,為了嚴謹一點還是測試了下,結果是不會影響。

可以看出,border并沒有遵循普通的視圖層級關系,父視圖的border總是凌駕于當前加到這個父視圖的所以視圖之上。如果想遮擋到這個view的border,就要從他的父視圖入手,代碼改為下面這樣:

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 150, 150)];
    [self.view addSubview:view];
    
    UIView *backView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 150, 150)];
    backView.backgroundColor = [UIColor lightGrayColor];
    backView.layer.borderColor = [UIColor redColor].CGColor;
    backView.layer.borderWidth = 5;
    [view addSubview:backView];
    
    
    
    //
    UIView *maskView = [[UIView alloc] initWithFrame:CGRectMake(-50, -50, 100, 100)];
    maskView.backgroundColor = [UIColor yellowColor];
    maskView.layer.borderWidth = 5;
    maskView.layer.borderColor = [UIColor greenColor].CGColor;
    [view addSubview:maskView];

還有一個比較值得注意的地方,borderd的寬度是往內擴展的,我們打開層級關系很容易看到這個:


Border內擴展

19.10.16
iOS13之后persent默認不再全屏

iOS13之后的persent默認是分頁形式而不是全屏形式,相比全屏的模式,分頁模式下即使沒有dismiss的按鈕,下滑操作同樣可以觸發這個效果,也就是用戶可以隨意關閉這個彈出的頁面,這對于一些需要強制用戶選擇的地方產生了一些問題,而且分頁模式下,多個彈窗會像卡片一樣錯層疊加狀態,越上層的越往下偏移,這和UI上的差距也有點明顯。

對于改動來說,一開始我想在基類中重寫persent的方法,但由于persent的模式和push執行又不太一樣,有點問題,所以舍棄了,目前我在基類VC中寫了一個自定義的persent方法,在這個方法中去判斷系統版本,進行全屏化設置。

19.10.17
梳理不同枚舉下頁面的展示和操作問題

枚舉一旦過多,如果組織不好代碼便會非常冗雜。如何組織這些代碼讓枚舉即使多樣也不會產生混亂?我覺得主要從以下幾步操作:
1.提取不同和相同
枚舉的意義在于區分不同的狀態,那么提取不同部分就是首要的任務,利用switch case區分不同。對于相同的部分,統一寫好接口進行處理;
2.組織流程,讓分支分開與匯流
每種頁面都有其不同的業務流程,如果把這個業務流程比作一條水流,那么枚舉就好像一個又一個的石頭,切開這部分水流,然后再次匯到了一起。我們頁面的流程也是這樣,分支判斷語句負責分流然后匯總。

現在的項目里有一個文章發布頁面,這個頁面的業務流就是發布填充到頁面上的數據,根據上面的方式,我們要做的像下面這樣:
1.由于存在正常、草稿、審核修改三種枚舉狀態,那么這里的主要不同存在兩處,一處是數據源,另外一處是在執行返回操作時是否保存操作,剩下的例如UI布局、文本校驗、上傳文件等等操作都是相同的;
2.整個業務流程來說,像如下分部:a數據流入->b整合數據 ->cUI基本交互 ->d發布或保存,我們根據枚舉,a和d是主要的分流部分,那么對a我們進行三種數據流入的區分,區分之后把數據匯入到b進行整合,在d處再次進行分流,根據不同的枚舉進行操作(例如修稿草稿類的把當前草稿進行刪除),最后匯集到發布或者保存草稿的狀態。

19.10.18
重用狀態下對不可重用模型的依賴

tableview的重用機制使得無法利用自身去標記事件,需要借助外界不可重用的對象,我們比較常用的就是Model對象,若干個Model放在數組中對應若干個cell。

由于cell本身是重用的,在需要特殊標識時無法通過自身的去進行判斷,必須要借助model這類不重用對象進行標識。例如我在cell上展開了一個下拉圖,如果不進行標識,那么在重用機制下,滑動到上面或下面某個區域時這個cell會再次被重用到,那么這個下拉圖還是原來的狀態。因此基于重用機制的控件的處理必須要用非重用的模型進行標記處理。

19.10.21
Lottie兼顧性能作出無損效果的動畫

動畫.gif

像上面的這種動畫,打開文件可以看到這個gif是由141張靜態圖片組合成的,直接使用Imageview的動畫組或者第三方去加載gif的話是非常消耗資源的,甚至在一級界面UI常駐的情況下,gif會大量占用內存而且無法釋放掉,很容易會收到內存警告。一些情況下,gif的加載甚至比視頻文件還要占用資源。

Lottie是一個支持多平臺的動畫庫,使用json文件配合素材實現動畫效果,Lottie的動畫核心基礎是CAKeyframeAnimation 關鍵幀動畫,因此它的優勢在于對內存資源的占用非常小,通過GPU和很少的CPU進行調度。它的制作也非常簡單,UI使用AE等工具繪制好動畫之后直接導出json文件即可,基本無損還原動畫。

官方Git:https://github.com/airbnb/lottie-ios

官方Git目前沒有提供OC版本的,我在網上收集了個:http://www.lxweimin.com/p/b2c0d1109c4d

需要注意一點的是,雖然Lottie對內存消耗的很小,但是會占用一部分CPU,如果動畫一直循環,則CPU的占用無法停止,多個動畫則會產生占用上的疊加,雖然CPU在沒有滿負荷的時候并不會產生明顯的感覺,但長時間使CPU處在高位有一點會明顯,那就是手機會發熱,這點在動畫上可能并不是很明顯,之前做視頻格式轉換使用FFmpeg時非常明顯(占用CPU進行轉換),1分鐘90%左右的占用會使手機明顯升溫,耗電量明顯加速。

19.10.22
列表加載大圖對內存的影響

在做一個列表的時候,我發現滑動時候產生了卡頓,前兩頁還好點,當我加載的越多,卡頓越明顯,看了一下內存也是在嗖嗖嗖地網上漲,滑個七八頁的時候就收到了內存警告,排除了其他原因,最后鎖定到了加載的圖片上,這里出了問題。我把要加載的鏈接放到瀏覽器里,好家伙,基本都是1M以上,我這一梭子滑下去就要加載20M+,連續滑來滑去那還受得了。

之前對加載大圖的認知是放在了查看高清大圖上,先把圖片下載到本地,再以引用的方式加載,防止內存崩潰掉。在tableview上倒是很少注意,因為一般上傳的圖片也經過了壓縮,不會出現很大的圖片,這次的圖片不知道是怎么弄上去的,應該是某一端沒有做限制,導致傳的圖片比較大。因為圖片目前都是在OSS保存著,所以解決這個問題也很方便,直接對圖片進行fill裁剪,裁剪出ImageView三倍的寬高即可。

我用XR測試了一下,在列表頁面想要保持流暢的滑動,加載的主圖(頭像之類小的不算在內)個數應該小于6個,每個大小保持在200KB以下,副圖比如頭像之類的,大小最好控制在50KB以下。

如果沒有使用OSS這類的存儲,對于這種情況需要強制輸入端限制上傳文件的大小,對于已經存在的大圖,可以采用下載的方式,統一寫一個方法,對下載好的圖片,大于500kb的進行裁剪壓縮處理,加載壓縮過的圖片。

從這里也可以看出OSS這類云端存儲的一些優勢,對于圖片的各種裁剪提供了很多便利的方式,包括對圓角的處理也很方便,如果后期能加上一些濾鏡效果的話就更棒了。

19.10.23
HUD的二次封裝問題

今天發現之前封裝的HUD似乎有點問題,多網絡請求時沒法消失。
改進了一下,設計了兩個方案:
1.加載到window上,全局保持一個HUD單例,只要記得hidden就不會出現不消失的情況,但這種情況的劣勢也非常的明顯,那就是HUD出現時全局無法操作,所以這個暫時拋棄。

2.加載到某個VC上,這個VC上最多只有一個HUD對象。這個方案比加載到window好很多,左滑手勢可以生效,還可以返回上級頁面不至于卡住。在設計上我直接在baseVC中加入兩個方法,showHudWithText:和hidden。show方法中會懶加載一個hud(被屬性指針持有),保證這個VC只擁有一個hud對象,不會反復alloc多個。這樣一來只要show和hidden成對出現,就能保證這個唯一的hud對象的消失。

另外還有若干的方案,例如和網絡請求綁定、用單獨的工具類持有等,前者適用范圍過窄,后者是一個不錯的方法。這個問題其實只要保證操作對象hud是一個唯一的就可以(至少是當前唯一),才不會出現并發問題。

19.10.24
數據流入阻塞

這個是最近測試時候的問題,在之前ERP項目中也會存在。
流程1-10,要測試的部分是789,但由于這樣或者那樣的原因,導致流程卡死在5,并且短時間內無法解決。目前的項目中很多次出現了這樣的問題,為此浪費了大量的時間。由于前期后臺的業務并沒有梳理清晰,每次做出改動都影響到了前期的流程,一旦影響,那么流程直接卡死后續的無法測試。
回顧一下之前的ERP項目,流程也是非常的長,每個節點的關聯性也非常地強,但阻塞流程的情況卻很少,后臺的老哥們確實做出了很大的貢獻。當時我們的后臺管理比較嚴格,建表命名關聯都是由同一個人在操作,其他人去寫邏輯,這保證了基礎框架的穩定,同時在數據上不會出現過多的冗余。而目前的工程,后臺對于核心部分的管理過于分散,每個人都可以操作核心業務,這使得當前的代碼十分混亂,每個人都寫自己的一套代碼,沒有在一個統一的框架和約束下,使得代碼各種冗余復雜,牽一發動全身。另外之前的項目中我們自動化測試引入的非常早,隨后一直保持了與Master分支一致的迭代,這使得我們再效率上大大提升,主要體現在快速校驗主流程已提測接口、快速創建可用數據、快速模擬異常情況等等。而目前的工程來說,由于后臺的接口不穩定,修補頻率過高,引入自動化測試可能出現加重工作負擔的情況,所以也暫時擱置一邊。
目前這個問題,估計后臺需要對主流程的代碼進行重構才能解決。

19.10.25
給collectionView添加個頭視圖

collectionView和tableview不同,collectionView是沒有一個獨立的頭視圖的,但是我們可以自己造一個出來。看了網上的幾個方案,我整理出了一個比較好的方案措施,并且已經實際進行了驗證,并且這個效果對繼承與scrollView的視圖通用。

我們知道,scrollView有一個屬性是contentInset,在這里能起到布局起始位置的作用,例如下面這樣會向下偏移150進行布局:

_myCollectionView.contentInset = UIEdgeInsetsMake(150, 0, 0, 0);

這樣在這個collectionView起始位置就預留了150高度的位置,但是按照正常布局來說,設置的frame是會從150以上進行布局的,這個時候就需要設置負值來使其在150以內的區域,像下面這樣:

UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, -150, self.view.width, 150)];
[self.myCollectionView addSubview:headerView];

需要注意一點,采用這樣方式之后,mjheader之類的刷新控件位置需要重新設置不然會被遮擋。

19.10.28
處理非一般的App啟動三方數據登入問題

一般來說,第三方登入或者綁定都會在首頁這個位置進行操作,手上有一個app比較特殊,是根據角色來區分App內tabbar上的Item的個數,也就是這個App可能不存在首頁這個概念,但當時沒有注意這個問題,還是按照普通App的模式,在首頁進行第三方登入和信息的綁定,在提測時遇到了IM消息接收不到,極光推送無法到達的情況。那么現在就面臨了一個問題,并沒有一個確定的業務流匯總的頁面,如何處理三方數據登入問題呢?

既然找不到匯總點,那么就從分支點進行處理:
例如登錄成功之后,跳轉到A或B或C頁面,那么這個分支點就可以一次三方數據登入的操作,先執行登入再執行跳轉,這是登錄流程的處理;正常打開App時,也去找這個分支點,例如在AppDelegate中,通過已登錄用戶的權限來進行判斷時先進行三方登錄操作。

這種App的流程和一般正常的流程有些差別,正常的流程是在匯總點處理事務,而上面這種則是在分支點處理事務,由于分支點有多個,所以要處理多個地方。總之,對流程把控來說,分支點和匯總點往往是處理事務比較好的切入點。

19.10.29
git stash

stash是git一個非常好用的命令,不過開發的時候發現同事用的并不是很多。這個命令的作用是將當前的變更保留在一個獨立的區域,清空當前的工作環境。一個比較實用的場景:我在某個分支上寫了一些代碼,但突然來了一些緊急需求,需要切換到另外的分支去處理事務,這個時候就可以用git stash命令將當前分支的更改保存到獨立的區域,然后切換到需要處理的分支,待需要處理的分支處理完畢后,切回一開始的分支,應用之前stash保存的變更即可。

19.10.30
整合自身數據源和三方數據源

這里指的是拖入第三方的數據源與自身的數據源由于模型的不同,如何進行一個數據源的同步,在各種操作之后還能保持一致性。這個問題在之前環信處理的時候已經提過一次,進行數據源的并線,至于采取自有向第三方并線還是第三方向自有并線,要看其規模,如果自有的model涉及面非常小,可以向第三方并線,舉一個最近的例子。

在文章發布VC中,我需要一組圖片進行發布,這里的圖片視圖頁面是個第三方控件,用的模型是B,這個發布VC中我可能出現輸入型數據源,即草稿帶入或者審核駁回之后服務器帶入,這個時候我需要處理模型的差異,假設自有模型為A,三方模型為B,由于模型A涉及面非常小,這個時候為了簡化設計我們可以直接向三方模型B中組合A,即B中持有一個A,在輸入數據源時創建A的同時創建B持有A,這樣共用一組數據源,從輸入到UI操作到提交,數據源都保持一致性。

當然,從自有模型到三方數據源中的模型大多還需要進行一次轉換才可以直接被使用,這里我選擇的是遵從三方原始的賦值方式,例如這個圖片拖拽頁面三方用的是Image賦值,由于有被駁回的情況存在,我的模型可能出現只有url的場景,這個時候我采取的是url下載轉化成Image,而不是在其源碼中進行修改用url賦值,這也遵循了封閉原則。

19.10.31
多參數接口統一外化參數模型

最近在整理一些之前的代碼,在整合部分接口的過程中發現接口的參數會出現很多問題:不是多了就是少了,改動接口參數之后全局的接口都要相應地區調整。這個問題在對某個視圖賦值的時候比較常見,比如上個版本我只需要顯示加入的人數,接口中我只預留了peopleNum這個參數,但在下個版本中又需要添加活動時間的顯示,這樣一來就需要對接口進行改動,在開放封閉的原則下這顯然是設計有問題的。
對于這種UI類賦值的接口來說,考慮到后期的擴展情況,接口在設計時使用某個類的模型對象作為參數顯然是更合理的,后期的擴展不需要更改接口本身。
而對于工具類的接口來說,接口在設計之初,所需的參數和返回值便是可以確定的,這類接口反而不應該使用可變的外化模型作為參數,而是使用確定參數,更加明確接口要表達的含義。并且作為工具類的接口,所需的參數應該是最直接不需要二次加工的參數,除非是泛型或者反射機制,否則不應該在方法的實現中再去判斷參數類型再去進行轉化分類。

19.11.1
collectionView flowLayout剩下一個Item時自動居中問題

系統默認的flowlayout在某個分區僅存在一個item存在時會自動將其居中,需要手動修改改變其位置,為了修正這個問題我寫了個layout繼承自flowlayout,

在.h文件中我預留了一個距左邊的距離參數leftOffest

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray <UICollectionViewLayoutAttributes *>*attributes = [super layoutAttributesForElementsInRect:rect];
    
    for (UICollectionViewLayoutAttributes *att in attributes) {
        if (att.indexPath.row == 0) {
            att.frame = CGRectMake(self.sectionInset.left + self.leftOffest, att.frame.origin.y, att.frame.size.width, att.frame.size.height);
        }
    }
    return attributes;
}

一開始我以attributes.count == 1的方式來判斷僅剩下一個item,后來發現這個現象在每個分區都會存在,所以直接用row == 0去判斷。

需要注意的一點是:在實際操作中我發現這個方法不單單對item生效,對分組的頭和尾也會生效。

19.11.4
NSDateFormatter對性能嚴重消耗

NSDateFormatter這個對象非常消耗性能,之前測試過,初始化時消耗的時間遠遠高于設置日期格式時的時間。
在靜態頁面上這個消耗可能在體驗上不是很明顯,但在tableview或者collectionView上面就非常直觀的感受到了這個卡頓現象。
如何處理這種初始化消耗性能的對象在滑動視圖上的問題呢?
對于這種初始化時消耗大量資源的對象,單例無疑是一個非常好的選擇,單例初始化一個對象,全局使用,減少了不必要的消耗。
上面的初始化性能消耗是可以避免的,NSDateFormatter在使用時stringFromDate/dateFromString這兩個方法也很消耗性能,但這里似乎無法做出好的優化,只能盡可能地做緩存,減少調用。
項目里有一處是顯示發布時間,需要根據時間戳和當前時間的對比進行兩次轉化:時間戳->真實時間->當前需要展示的時間方案。由于是在滑動視圖上,我做了如下的優化方案:
1.單例保持NSDateFormatter對象的初始化;
2.在視圖滑動停止時開始進行時間轉換操作,滾動時不進行操作;
2.在模型中加入realDate字段,轉換后的要展示的時間進行一次緩存,有則直接從緩存中取,不再進行stringFromDate的操作。

之前考慮過一個方案,就是異步處理這些耗時操作,但在cell重用狀態下,異步操作反而會出現更多異常的情況,所以暫時只在滑動停止時進行了處理,這里還需要研究一下。

19.11.5
帶返回參數的block和帶block參數的block

有空梳理下,老是把這兩個弄混。。。

1.帶返回參數的block,即時回調結果,像下面這樣:

typedef CGFloat(^speedBlock)(CGFloat x);

@interface ViewController ()

@property (nonatomic,copy) speedBlock speed;

@end

self.speed = ^CGFloat(CGFloat x) {
        return 20 + x;
    };
    
    
CGFloat result = self.speed(10);
NSLog(@"%f",result);

很簡單,打印出30,這種方式一般用的多一點,用于 【即時】 回調出結果的情況。

2.帶block參數的block,用于執行某個結果,像下面這樣:

typedef void(^numActionBlock)(void(^)(NSInteger num));

@interface ViewController ()

@property (nonatomic,copy) numActionBlock numAction;

@end

NSInteger startNum = 123;
    
    
    self.numAction = ^(void(^act)(NSInteger num)) {
        act(startNum + 2);
    };
    
    self.numAction(^(NSInteger num) {
        NSLog(@"%ld",num);
    });

最簡單的對比來說,在使用場景上,第一種block適用于即時返回計算值或者比較結果的場景,偏向于即時“結果”的獲取,獲取之后直接在當前函數方法中使用,,例如下載進度、快速枚舉等;第二種block適合執行某個操作的場景,同時也可以控制回調執行時間,例如延遲執行某個方法時第一種block顯然不合適。

19.11.6
無網絡時緩存點贊等網絡請求

產品有個需求是用戶在無網絡狀態時的點贊操作緩存起來,等待有網絡時統一提交給后臺。
這個需求的路子繞的有多遠,整理下拋給產品:

先說下正常流程:
1.在無網絡狀態下,保留一個請求緩存的隊列,里面保存著每組請求接口和參數,當有網絡時異步進行請求處理;
2.假如現在有20個操作需要同步到網絡,那么這20個請求如何進行處理?并行or串行?如果某個失敗了是進行輪詢還是放棄請求?

異常流程:
1.目前我們的請求全都是校驗token的,token超時如何處理?客戶端本身是不知道token超時的,需要發出請求之后給予回應中;
2.產生錯誤返回時是否屏蔽錯誤信息?正常看來肯定不能莫名其妙突然彈出一個“文章已被刪除”之類的信息;
3.若產生失敗,但本地數據又和服務器數據不同步,以哪一方為準?

拋給產品,估計最后這個需求不了了之。。。

19.11.7
正常超時和異常超時

正常超時指的是客戶端發出請求超出一定時間后服務器無任何響應操作,此時服務器無響應,之后的一段時間服務器也無響應,異常超時指的是客戶端達到一定時間后認為其超時,但在若干時間后服務器給出了應答。簡單地說,超時時間給的太短,但為了不影響用戶體驗,一般來說我們都會給個15s左右的超時限制,太長的時間未響應本身就是程序上的問題。

如何避免這類情況的發生?我們從事件發生原因來看,造成這種現象的原因無外乎是服務器進行了復雜耗時的操作,常見的比如多表查詢、下載上傳資源文件、服務器等待另一服務端響應等等。

1.多表查詢。后臺在表結構設計上需要下點功夫,尤其是ERP類型的業務,業務關聯性非常強,前端發送請求時可以適當增加參數輔助后臺快速查詢;
2.上傳下載資源文件。這個本就不應該在服務器上進行操作,耗費服務器資源,包括用服務器進行視頻轉碼操作,這些都應該避免,資源文件的操作盡量采用第三方云端服務,例如阿里OSS,七牛云等;
3.服務器等待另一端服務器響應。這種一般是進行了跨服務器業務,同樣地,這個業務如果簡單完全可以在前端進行操作,但如果涉及安全方面則必須要有自己的服務器操作,這個時間是無法避免的,而且協調起來不那么容易。一個比較妥協的方案就是異步無限期等待,發起之后就異步掛起,響應回來之后全局彈出。

19.11.8
涉及UI類第三方庫盡量使用拖入工程的方式

TZImagePickerController這個相冊選擇的第三方相信很多人都用過,我們這個工程中也不例外,集中使用了pod管理。由于部分UI需要高度自定義,所以直接就在源代碼中進行了修改,為了防止有誤操作導致版本變更,強制指定了固定了版本。但即使這樣,還是出現了一些問題,(git對pod操作的一些忽略,兼容13迫不得已需要升級三方版本等等),最終不得不重新將其拖入工程中進行修改,不再使用Pod管理。

對于這類UI類型的庫,如果預見后期需要對UI有各種改動,比較穩妥的方式就是拖入工程而不是使用pod管理,一旦要修改庫的源代碼,使用pod管理反而會增加很多麻煩,誤操作被新版本覆蓋時甚至丟失代碼。但拖入工程的方式也有個很大的弊端,就是系統產生兼容性問題時需要手動修改。相比較來說,拖入操作更可控一些,出了問題也很容易定位到。

19.11.11
帶上梯子讓終端pod飛起

pod的操作越來越慢,沒有梯子往往需要等待很久最后給你來個超時。之前保存的那個鏈接打不開了,手動記錄一下方式:
1.你要有個梯子,找到本地socket5地址;
2.在終端輸入一下命令,強制終端走梯子:export https_proxy="本地socket5地址"
3.這個命令只對當前的終端窗口有效,關閉則需要重新設置。

19.11.13
反射模式使問題復雜化

目前的項目中多次用model進行cell的反射,由于要依靠Model,產生了很多“無用”狀態的Model,僅僅是為了反射到對應的cell,這類model文件數量還不算少,造成了文件的冗余。

回到為什么使用反射模式的問題上,反射能消除分支節點的if...else...判斷,加強代碼的擴展性,保證了開放封閉的原則,在會出現大量if...else...的地方使用效果很好,但對于確定性的比較少量的分支節點判斷,使用反射模式反而會使得問題有些復雜化,主要就體現在上面這種情況,搭建反射的資源過度冗余,對于這類問題以后要注意。

19.11.14
persent和push的執行

persent和push在代碼執行上有著差別,persent是逐行執行,push是有點類似于FMDB的事務提交方式,把這部分都設置完成之后才處理。

有下面代碼:

- (void)action {
    
    QViewController *vc = [[QViewController alloc] init];
    [self presentViewController:vc animated:YES completion:nil];
//    [self.navigationController pushViewController:vc animated:YES];
    NSLog(@"222");
}

在QVC的ViewDidLoad中我打印了111,上述代碼先打印111,后打印222。改變代碼如下

- (void)action {
    
    QViewController *vc = [[QViewController alloc] init];
//    [self presentViewController:vc animated:YES completion:nil];
    [self.navigationController pushViewController:vc animated:YES];
    NSLog(@"222");
}

上面代碼先打印222,后打印111。上面簡單的例子已經足以說明present和push執行時在生命周期上的區別:對于present的VC,在present代碼之后再賦值就已經遲了,而push則不會。

19.11.15

今天開始需要周六上班來延長春節假期了,工期比較趕暫時休整一段。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內容

  • 以前看書喜歡看小說,大學時,開始喜歡看人物傳記。印象最深的是《蘇東坡傳》(林語堂著,張振玉譯)。當然還有好多科學家...
    linyujing閱讀 418評論 0 1
  • 中醫對于肥胖的理解,乃為痰濕,是外寒內火所致。要想徹底改善肥胖的問題,就要通過祛除外寒和內火來完成。其實不光是肥胖...
    YANG_慢病調理閱讀 1,296評論 0 17
  • 天氣晴 星期二 今天忙完了以后,發現女兒在看電視寫作業,好不專心啊!于是我就說了她幾句,雖然有點小情緒但不太...
    陌上清歌的簡書閱讀 56評論 0 1
  • 這是迪斯尼junior頻道的動畫片,現在國內有中文版,班里有小朋友很喜歡我就下來看。竟然發現自己非常喜歡這個小姑娘...
    summerlight閱讀 345評論 0 0