1.SIP概念理解
讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領域值得信任、有價值的長期合作伙伴,公司提供的服務項目有:域名注冊、網(wǎng)站空間、營銷軟件、網(wǎng)站建設、榆次網(wǎng)站維護、網(wǎng)站推廣。
2.【協(xié)議學習】SIP基本場景分析
3.企業(yè)開源SIP項目
4.SIP常見問題及處理
5.SIP基礎入門
6.我的IOS端SIP電話開發(fā)歷程
7.我的SIP開發(fā)之路
8.SIP協(xié)議開源SIP服務器搭建和客戶端安裝
1.WebRTC官網(wǎng)
2.大佬的筆記
3.WebRTC中文網(wǎng)
4.RTC.Blacker -Android IOS WebRTC
5.iOS下音視頻通信-基于WebRTC
6.第六章 Webrtc服務器搭建
7.webrtc學習: 部署stun和turn服務器
8.webrtc編譯全過程
9.iOS下WebRTC音視頻通話(一)
10.iOS下WebRTC音視頻通話(二)-局域網(wǎng)內(nèi)音視頻通話
11.WebRTC樣本
12.iOS下音視頻通信的實現(xiàn)-基于WebRTC
1. WebRTC簡介及其與SIP互通
2.SIP和WebRTC有什么不同?
總結一下藍牙開發(fā)相關的知識點和注意事項,做個筆記,也希望你們能少踩坑
(公司部分藍牙項目為混編項目,藍牙相關處理均采用了Objective-C,故本文????均采用OC,Swift處理相同)
藍牙4.0包含兩個藍牙標準,它是一個是 雙模 的標準,它包含 傳統(tǒng)藍牙部分(也稱經(jīng)典藍牙) 和 低功耗藍牙部分(BLE) , 二者適用于不同的應用場景和應用條件。他們的特點如下
所以藍牙4.0是集成了傳統(tǒng)藍牙和低功耗藍牙兩個標準的,并不只是低功耗藍牙
藍牙4.0支持兩種部署方式: 雙模式 和 單模式 ,雙模同時支持經(jīng)典藍牙和低功耗藍牙,而單模則只支持其中一種。
二者更多細節(jié)詳見: 傳統(tǒng)藍牙和低功耗藍牙的區(qū)別
iOS中藍牙相關功能都封裝進了 CoreBluetooth 類中,其中有幾個常見的參數(shù)和概念
具體API參考 CoreBluetooth藍牙開發(fā)
保存到數(shù)組中的設備可通過 UUID 來進行區(qū)分。從 iOS7之后蘋果不提供外設的mac地址,外設的唯一標識換成了由mac封裝加密后的UUID,需要注意的是不同的手機獲取同一個外設的UUID是不同的,所以在不同手機之間UUID不是唯一的,但在本機上可以作為唯一標識(特殊情況手機刷機后也會改變UUID)。
如何獲取Mac地址
一般使用場景是根據(jù)Mac地址區(qū)分某個外設
注意點:
寫入數(shù)據(jù)時可能會遇到需要分包發(fā)送的情況,我們可以通過下面的API或許當前特征支持的最大的單條寫入長度
maxLength 一般取決于藍牙模塊內(nèi)部接收 緩沖區(qū) 的大小,很多硬件設備這個緩沖區(qū)的大小是 20 字節(jié), 這個大小也和特征的寫入權限有關,像具有寫入權限 withResponse 類的特征其大小一般為 512 字節(jié),當然這些都是取決于設備測的設置;
當我們單次發(fā)送的數(shù)據(jù)字節(jié)長度大于 maxLength 時,我們就需要采用分包的方式來發(fā)送數(shù)據(jù)了,
分包發(fā)送的邏輯類似于下面
這邊延時主要是設備側的接收模塊接收數(shù)據(jù)以及處理能力有限
外圍設備測和中心設備(大部分情況下是手機)保持藍牙連接的狀態(tài)下,如果長時間不產(chǎn)生交互,藍牙就會斷開,所以為了保持兩者持續(xù)的連接狀態(tài),需要做保活處理,也就是需要持續(xù)的發(fā)送心跳包(watchdog)。相應的處理是使用一個定時器定時向設備側發(fā)送符合設備協(xié)議格式的心跳包。
斷開連接很簡單,只需要調(diào)用 [self.centralManager cancelPeripheralConnection:peripheral] 傳入需要斷開連接的設備對象就行了。斷開連接時會自動調(diào)用 centralManager:didDisconnectPeripheral:error: 代理方法。
按照之前的慣例,當error為nil時表示斷開成功,error不為nil時斷開失敗。這種理解是錯誤的。
當你調(diào)用 cancelPeripheralConnection: 方法(主動斷開)斷開連接時error為nil ; 沒有調(diào)用這個方法(異常斷開)而斷開時error返回的是異常斷開的原因。也可以理解為主動調(diào)用斷開連接方法一定會斷開
接下來就是斷開重連的問題了,對藍牙功能進行封裝時肯定少不了斷開重連。首先斷開時可通過上面的代理方法的error是否為nil判斷是否是異常斷開,一般情況下異常斷開時是需要重連的
原因就是當設備斷開連接后 peripheral.services 為nil了,當然 service.characteristics 也是nil,所以需要在斷開連接時把保存這個設備對應的服務和特征全部清除,然后在連接成功時重新過一遍發(fā)現(xiàn)服務和發(fā)現(xiàn)特征的流程就好了。
iOS7 開始,Apple加入了Beacon圍欄檢測的API, ( iBeacon-維基百科 ), 其工作方式是,配備有低功耗藍牙(BLE)通信功能的設備使用 BLE 技術向周圍發(fā)送自己特有的 ID,接收到該 ID 的應用軟件會根據(jù)該 ID 采取一些行動。比如,在店鋪里設置 iBeacon 通信模塊的話,便可讓 iPhone 和 iPad 上運行一資訊告知服務器,或者由服務器向顧客發(fā)送折扣券及進店積分, 或者公司的手機打卡,只要手機靠近打卡器一定范圍,手機測就向打開器發(fā)送打卡信息,從而自動打卡。這種場景還有很多。 其中一個最重要的功能就是App的喚醒功能(殺死后也能喚醒)
舉一個我們的例子,我們的產(chǎn)品業(yè)務場景就是在進入車輛以后,需要使用藍牙連接我們的后裝車載設備以采集車輛信息和駕駛行為行程等,這里有一個問題就是在App被殺死的情況下如何喚醒App, 因為不可能要求用戶每次都主動去打開App,這樣體驗太差。我們的做法是通過iBeacon,當我們的車輛點火以后,設備測通電,發(fā)出 iBeacon廣播 ,App實現(xiàn)監(jiān)聽iBeacon相關功能后就可以喚醒我們App,然后在相應的回調(diào)的處理一些事情,比如通過藍牙連接設備。這里的前提條件是我們的硬件設備測包含iBeacon模塊,具有iBeacon功能,而且對iBeacon的廣播頻率也有一定的要求,長了可能喚醒的功能會不穩(wěn)定,官方建議的好像是100ms,頻率超高越耗電,但可以讓手機或其它監(jiān)聽設備越快地發(fā)現(xiàn)iBeacon。標準的BLE廣播距離是100m,這使Beacon在室內(nèi)位置跟蹤場景下的效果更理想。
關于iBeacon更多的使用及介紹請參考
蘋果核 - iOS端近場圍欄檢測(一) ——iBeacon
iBeacon技術初探
這篇文章主要介紹了實際應用 MVVM 的過程中的一些問題和解決方案
MVVM(Model View ViewModel)是一種 MVC(Model View Controller)的一種變型,來解決 MVC 中龐大復雜的 Controller 難以維護的問題。大致上講 MVVM 有幾個要求:
MVVM 和 MVC 有很多類似的特點,主要的不同有:
另外一點,MVVM 默認 View 和 View Controller 有一個一對一的關系,一般我們把這兩個看做一個整體,會以 .swift 文件 和 Storyboard 的形式出現(xiàn)。
View Model 的工作是處理所有的展示數(shù)據(jù)的邏輯。如果一個 model 中有一個 NSDate 對象, NSDateFormatter 就會在 View Model 中用來設置日期的展示形式。
View Model 不能接觸任何用戶界面的部分,View Model 文件中不應該 import UIKit ,View Controller 會觀察 View Model 去了解什么時候顯示新的數(shù)據(jù)(通過 KVO 或者 FRP(Functional Reactive Programming))
MVVM 和 MVC 有一個共同的弱點:沒有清楚的定義應該把網(wǎng)絡請求部分放在哪里。在實際操作過程中,我會把網(wǎng)絡請求放在 View Model 文件里面,但之后我打算把網(wǎng)絡請求放在自己獨立的一個類中,View Model 文件會擁有這個對象。
下面我們主要談一談實際應用 MVVM 過程中一些挑戰(zhàn):
例如你想構造這樣一個常用的界面,有一個 segment control 在屏幕頂部,屏幕的其他部分是一個 collection view,選擇不同的 segment,就會展示不同樣式的 collection view,元素的排列順序。我們定義了一個 enum 來枚舉所有的排列樣式:
那么這個 enum 在 MVVM 模式中應該放在哪里呢?因為這個 enum 決定了數(shù)據(jù)排列的順序,每個 cell 中的文字和按鈕的 title,這些都屬于展示的邏輯,所以這個 enum 看起來應該放在 view model 中。
然而,這些 layout 并不改變要展示的數(shù)據(jù),只是決定了要呈現(xiàn)的數(shù)據(jù)的排列方式和排列順序,從這個角度上來說 enum 又應該放在 view controller 中。
我的解決方法是把 enum 放在 view model 中,然后在 view model 中加一個對外的 Observable 或者 Signal 來表示使用了哪個 layout,基于用戶選擇的 segment,view model 更新這個值,然后在 view controller 中根據(jù)相應的 layout 改變 collection view 的樣式,view controller 也可以根據(jù)這個值來決定用哪個 cell reuse identifier
iOS 開發(fā)者在用 MVVM 和 FRP 寫應用的時候最常見的問題可能就是 ViewModel 怎么把數(shù)據(jù)展現(xiàn)給 ViewController。當 Model 層的數(shù)據(jù)發(fā)生變化更新的時候,ViewController 需要得到通知然后做出相應的 UI 更新,我們一般會用到兩種機制:
第一個選項很吸引人,因為可以在 View Controller 中決定怎么選擇觀察那些 property。然而,我不推薦在 Swift 中使用第一個選項,因為 Swift 在 KVO 中沒有類型檢查,你需要對 AnyObject 強制轉換類型很多次。
第二個選項是比較 Swift 的方式,基于 Swift 的 generics 特性,signals,sequences,observables 可以支持編譯過程中的類型檢查。
但有時候在 view model 增加這些 Signals 或者 Observables 有些困難。Swift 的初始化方法對于什么時候對 property 賦值有非常明確的規(guī)定。Signals 或者 Observables 需要使用 view model 內(nèi)部的狀態(tài),所以它們必須在 super.init() 之后才能創(chuàng)建,但是另一方面,我們在調(diào)用 super.init() 之前保證所有 property 已經(jīng)被賦值了,包括那些 Signal/Observable property。
這是個先有雞還是先有蛋的問題。
我采用比較簡單的解決方法:定義成 var 的隱式可選類型,這樣就可以在 super.init() 之后才給 property 賦值。這不是一個完美的解決辦法。我們可以用 lazy var property 的閉包賦值來代替上面的方法。在 Swift 不斷完善和更新的過程中,大家也可以探索其他更好的辦法。
舉一個很常用的例子,用戶點擊 collection view 中的一個 cell,跳轉到詳情頁面。用戶點擊的操作應該在 view controller 中處理,具體內(nèi)容是展現(xiàn)一個新的詳情頁面。但是 view controller 不能直接接觸 models,我們要如何用 MVVM 模式實現(xiàn)這樣的用戶交互呢?
我的解決方案是利用 Swift 的閉包。首先在 view model 中定義一個閉包:
然后在 view model 中添加一個 property:
接著我需要調(diào)用閉包,在 view model 中定義一個view controller 可以調(diào)用的函數(shù),這個函數(shù)的參數(shù)是可以決定使用什么數(shù)據(jù),一般情況下常用 index path:
現(xiàn)在當用戶選中一個 cell,會調(diào)用 view model 中的這個函數(shù),并且傳入 index path 參數(shù),view model 決定使用哪個數(shù)據(jù),并調(diào)用在 view controller 中定義的閉包,例如:
最后一個問題是怎么創(chuàng)建這個 view model。我們需要傳遞一個閉包給view model 的初始化函數(shù),然后用 lazy loading 來調(diào)用 view model 的初始化函數(shù)。
隨著項目的開發(fā)進行,對于ios程序崩潰及解決方法有一定的認識,現(xiàn)總結如下:
1:@selector提供的選擇器未定義,運行時造成程序崩潰。
2:程序中父類對象已銷毀,但后續(xù)操作還在使用其子對象。
3:多次release,造成對象引用技術為負數(shù), 然后后續(xù)還使用。
4:在程序的聲明處指定使用assign屬性,然而在dealloc的時候卻對其進行release。
對于崩潰問題可以嘗試使用 Project-Profile, 連上真機,選擇Devices, 然后彈出的instruments-》Zombie. 運行程序,直到程序崩潰。
這時候工具就會捕獲到崩潰代碼同時提供引用基數(shù)產(chǎn)生及釋放的全部過程。
如果你是通過xCode來查看崩潰的位置,有時候會不正確。這個要多加注意
UITableViewCell的重用機制體現(xiàn)在-(UITableViewCell)dequeueReusableCellWithIdentifier:(NSString)identifier這個方法中,他的基本意思就是在創(chuàng)建cell的時候為每一個cell都綁定一個identifier的標識。當cell從我們的視覺范圍中消失的時候,這個綁定了cell的標識就會被放到緩存池中。當tableView需要新的cell的時候,直接先去緩存池中尋找有沒有攜帶identifier的cell,若有的話直接復用;沒有的話,才去創(chuàng)建新的cell,并綁定標識identifier。所以從理論上講,倘若一屏最多顯示的cell個數(shù)為n個,那么需要攜帶identifier表示的cell最少只需n+1個。
查看UITableView頭文件,會找到NSMutableArray* visiableCells,和NSMutableDictnery* reusableTableCells兩個結構。visiableCells內(nèi)保存當前顯示的cells,reusableTableCells保存可重用的cells。
UITableView顯示之初,reusableTableCells為空,那么tableView dequeueReusableCellWithIdentifier:CellIdentifier返回nil。開始的cell都是通過[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]來創(chuàng)建,而且cellForRowAtIndexPath只是調(diào)用最大顯示cell數(shù)的次數(shù)。
比如:有100條數(shù)據(jù),iPhone一屏最多顯示10個cell。程序最開始顯示TableView的情況是:
1. 用[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]創(chuàng)建10次cell,并給cell指定同樣的重用標識(當然,可以為不同顯示類型的cell指定不同的標識)。并且10個cell全部都加入到visiableCells數(shù)組,reusableTableCells為空。
2. 向下拖動tableView,當cell1完全移出屏幕,并且cell11(它也是alloc出來的,原因同上)完全顯示出來的時候。cell11加入到visiableCells,cell1移出visiableCells,cell1加入到reusableTableCells。
3. 接著向下拖動tableView,因為reusableTableCells中已經(jīng)有值,所以,當需要顯示新的cell,cellForRowAtIndexPath再次被調(diào)用的時候,tableView dequeueReusableCellWithIdentifier:CellIdentifier,返回cell1。cell1加入到visiableCells,cell1移出reusableTableCells;cell2移出visiableCells,cell2加入到reusableTableCells。之后再需要顯示的Cell就可以正常重用了。
使用過程中,并不是只有拖動超出屏幕的時候才會更新reusableTableCells表,還有:
1. reloadData,這種情況比較特殊。一般是部分數(shù)據(jù)發(fā)生變化,需要重新刷新cell顯示的內(nèi)容時調(diào)用。在cellForRowAtIndexPath調(diào)用中,所有cell都是重用的。我估計reloadData調(diào)用后,把visiableCells中所有cell移入reusableTableCells,visiableCells清空。cellForRowAtIndexPath調(diào)用后,再把reuse的cell從reusableTableCells取出來,放入到visiableCells。
2. reloadRowsAtIndex,刷新指定的IndexPath。如果調(diào)用時reusableTableCells為空,那么cellForRowAtIndexPath調(diào)用后,是新創(chuàng)建cell,新的cell加入到visiableCells。老的cell移出visiableCells,加入到reusableTableCells。于是,之后的刷新就有cell做reuse了。
1、重取出來的cell是有可能已經(jīng)捆綁過數(shù)據(jù)或者加過子視圖的,所以,如果有必要,要清除數(shù)據(jù)(比如textlabel的text)和remove掉add過的 子視圖(使用tag)
2、刪除重用的cell的所有子視圖,從而得到一個沒有特殊格式的cell,供其他cell重用。
//刪除cell的所有子視圖
while ([cell.contentView.subviews lastObject] != nil)
{
[(UIView*)[cell.contentView.subviews lastObject] removeFromSuperview];
}
3、為每個cell指定不同的重用標識符(reuseIdentifier)來解決。重用機制是根據(jù)相同的標識符來重用cell的,標識符不同的cell不能彼此重用。
NSString *identifier = [NSString stringWithFormat:@"TimeLineCell%d%d",indexPath.section,indexPath.row];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
4、重用機制調(diào)用的就是dequeueReusableCellWithIdentifier這個方法,方法的意思就是“出列可重用的cell”,因而只要將它換為cellForRowAtIndexPath(只從要更新的cell的那一行取出cell),就可以不使用重用機制,因而問題就可以得到解決,但會浪費一些空間
iOS10系統(tǒng)常見問題有哪些?
1. iOS10系統(tǒng)采用了全新的鎖屏界面及解鎖方式,我可以不用按下Home鍵,只使用Touch ID直接解鎖iPhone嗎?
可以,具體設置方法為:設置 - 通用 - 輔助功能 - 主屏幕按鈕 - 把“輕觸打開”開關打開即可。
iOS10常見問題
2. 升級iOS10之后,為什么鎖屏快捷回復不能用?
對于iMessage和信息的鎖屏快捷回復,部分網(wǎng)友表示需要重啟 iPhone 才能夠使用。
3. 為什么我的iMessage在發(fā)信息的.時候,沒有官方宣傳的那些放大、縮小等效果?
網(wǎng)友Manimal表示,這個功能在開啟著“減弱動態(tài)效果”下不可使用。關于這個選項的設置方法為:設置 - 通用 - 輔助功能 - 減弱動態(tài)效果 - “開啟”改為“關閉”。
iOS10常見問題
4. 為什么我的壁紙變暗了?
如果在iOS9開啟過“降低白點值”,iOS10會默認將該選項打開,所以就算在控制中心亮度調(diào)最亮也還是暗。想要更改的話,設置方法為:設置 - 通用 - 輔助功能 - 顯示調(diào)節(jié) - 把降低白點值選擇“關閉”就可以。
iOS10常見問題
5. iMessage里面發(fā)送特效沒有屏幕和氣泡這兩個選項?
有可能是第三方輸入法沖突導致的。把第三方輸入法卸載了或者換另一個即可。