前段時間由于項目進度緊, 一直沒有倒開空來照顧豺狼的簡書, 寫博客是學(xué)習(xí)的一個非常好的途徑, 豺狼的技術(shù)并不好, 所以博客內(nèi)容也沒有特別高端, 只是把一些學(xué)到的知識在分享的過程中整合吸收而已, 能幫到各位看官當(dāng)然就更好了...

日常項目中我們總會遇到獲取DeviceId的需求, 比如定點推送消息, 未登錄收藏等功能, Keychain具體的解釋就不贅述了, 先摘錄一下網(wǎng)上關(guān)于獲取設(shè)備唯一標(biāo)識符的方法:這里吐槽下iOS對比安卓繁瑣的獲取方法,
iOS中獲取設(shè)備唯一標(biāo)示符的方法一直隨版本的更新而變化。iOS 2.0版本以后UIDevice提供一個獲取設(shè)備唯一標(biāo)識符的方法uniqueIdentifier,通過該方法我們可以獲取設(shè)備的序列號,這個也是目前為止唯一可以確認唯一的標(biāo)示符。好景不長,因為該唯一標(biāo)識符與手機一一對應(yīng),蘋果覺得可能會泄露用戶隱私,所以在 iOS 5.0之后該方法就被廢棄掉了;iOS 6.0系統(tǒng)新增了兩個用于替換uniqueIdentifier的接口,分別是:identifierForVendor,advertisingIdentifier,但這兩個接口會在應(yīng)用重新安裝時改變數(shù)值,并不是唯一的標(biāo)示符,所以開發(fā)者改為使用WiFi的mac地址來取代;iOS 7中蘋果又封殺mac地址,所以開發(fā)者再次改變思路使用KeyChain來保存獲取到的UDID,這樣以后即使APP刪了再裝回來,也可以從KeyChain中讀取回來。
由此我們知道在iOS7之后的策略是將每次重裝都會改變的UUID保存到Keychain中, 之后每次使用就從Keychain中取出來, 由于Keychain不會隨著APP的刪除而清空, 所以達到了唯一標(biāo)識符的作用. 腦洞大開下, 會不會出現(xiàn)超小幾率的UUID重復(fù)呢?
同樣的原理, 某些APP在卸載重裝之后依然可以記住你的密碼就是這樣實現(xiàn)的, 比如招商銀行的手機客戶端, 我猜想就是根據(jù)唯一標(biāo)識符來記錄具體的圖像密碼實現(xiàn)的, 而一些APP的密碼自動填充功能也是同樣的實現(xiàn)方法. 理論說到這里, 具體的還是要到實踐中去體驗.非廣告
首先豺狼看了下蘋果關(guān)于Keychain的Demo, 發(fā)現(xiàn)竟然是MRC的代碼, 有警告聽說還有內(nèi)存泄露...現(xiàn)在向大家推薦良心三方庫Sam Soffes的SSKeychain, SSKeychain的功能很簡單, 想要詳細了解的可以看SSKeychain Documentation.
將SSKeychain導(dǎo)入工程后, 需要添加Security.framework支持庫

然后在TARGETS-->Capabilities-->Keychain Sharing打開, 會提示登錄開發(fā)者賬號, 選擇證書, 打開之后會生成一個.entitlements文件

其中的AppIdentifierPrefix是的你的App IDs的Prefix

這樣準(zhǔn)備工作就完成了, 之后我們把UI設(shè)計好, 具體功能如下圖. label用來顯示設(shè)備唯一標(biāo)識符, 刪除APP之后并不會改變. 第一次輸入用戶名和密碼之后, 再次輸入用戶名會自動填充密碼. 點擊清空按鈕, 會把賬號對應(yīng)的本地密碼刪除. 點擊查詢會在控制臺輸出APP內(nèi)保存的所有賬號信息.

關(guān)于SSKeychain, 這里用到了如下幾個方法:
// 保存對應(yīng)賬戶密碼
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;
// 提取對應(yīng)賬戶密碼
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
// 刪除對應(yīng)賬戶密碼
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
// 獲取APP下所有賬戶
+ (NSArray *)accountsForService:(NSString *)serviceName;
詳細的代碼貼出來, 推薦下載Demo跑起來看看, 并不難, 都是對Keychain的最基本的用法
#import "ViewController.h"
#import "SSKeychain.h"
static NSString *kKeychainService = @"com.xuhaoran.keychaindemo";
static NSString *kKeychainDeviceId = @"KeychainDeviceId";
@interface ViewController () <UITextFieldDelegate>
@property (weak, nonatomic) IBOutlet UITextField *accountTextField;
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField;
@property (weak, nonatomic) IBOutlet UILabel *uuidLabel;
@end
@implementation ViewController
#pragma mark - life cycle
- (void)viewDidLoad {
[super viewDidLoad];
self.uuidLabel.text = [NSString stringWithFormat:@"設(shè)備號:\n%@", [self getDeviceId]];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self.accountTextField resignFirstResponder];
[self.passwordTextField resignFirstResponder];
}
#pragma mark - UITextFieldDelegate
- (void)textFieldDidBeginEditing:(UITextField *)textField {
if (textField == self.accountTextField) {
self.accountTextField.text = nil;
self.passwordTextField.text = nil;
}
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (textField == self.accountTextField) {
[self.passwordTextField becomeFirstResponder];
}
else if (textField == self.passwordTextField) {
[self loginAction:nil];
}
return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
if (textField == self.accountTextField) {
// 提取本地密碼
NSString *localPassword = [SSKeychain passwordForService:kKeychainService account:textField.text];
if (localPassword) {
self.passwordTextField.text = localPassword;
}
}
}
#pragma mark - responce action
- (IBAction)loginAction:(id)sender {
if (!self.accountTextField.text || !self.passwordTextField.text) {
[self showMsg:@"輸入賬號和密碼!"];
return;
}
[self showMsg:[NSString stringWithFormat:@"賬戶名:%@\n密碼:%@", self.accountTextField.text, self.passwordTextField.text]];
// 保存賬號密碼
[SSKeychain setPassword:self.passwordTextField.text forService:kKeychainService account:self.accountTextField.text];
}
- (IBAction)clearAction:(id)sender {
if (!self.accountTextField.text) {
return;
}
if ([SSKeychain deletePasswordForService:kKeychainService account:self.accountTextField.text]) {
[self showMsg:[NSString stringWithFormat:@"賬戶%@的密碼已清空!", self.accountTextField.text]];
self.passwordTextField.text = nil;
}
else {
[self showMsg:@"刪除失敗了"];
}
}
- (IBAction)searchAllAction:(id)sender {
NSArray *accounts = [SSKeychain accountsForService:kKeychainService];
NSLog(@"accounts:\n%@", accounts);
[self showMsg:@"看下控制臺輸出"];
}
#pragma mark - private method
- (NSString *)getDeviceId {
// 讀取設(shè)備號
NSString *localDeviceId = [SSKeychain passwordForService:kKeychainService account:kKeychainDeviceId];
if (!localDeviceId) {
// 保存設(shè)備號
CFUUIDRef deviceId = CFUUIDCreate(NULL);
assert(deviceId != NULL);
CFStringRef deviceIdStr = CFUUIDCreateString(NULL, deviceId);
[SSKeychain setPassword:[NSString stringWithFormat:@"%@", deviceIdStr] forService:kKeychainService account:kKeychainDeviceId];
localDeviceId = [NSString stringWithFormat:@"%@", deviceIdStr];
}
return localDeviceId;
}
- (void)showMsg:(NSString *)msg {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Tip" message:msg preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil];
[alert addAction:cancel];
[self showViewController:alert sender:nil];
}
@end
因為看網(wǎng)上大多都是簡單的方法介紹, 所以豺狼就寫了一個實戰(zhàn)型的Demo, 雖說是實戰(zhàn), 但也都是SSKeychain的基本用法, 希望對各位多有幫助, 如果喜歡請點個贊鼓勵一下豺狼, 謝謝.