關(guān)于MAC OS X##
去年十月份從iOS開發(fā)轉(zhuǎn)到OS X開發(fā),OS X主要由五大API來開發(fā),分別是`Cocoa`腥椒、`Carbon`虽惭、`POSIX`、`X11`和`Java`接谨。目前我用的是`Cocoa`來開發(fā)摆碉,OS X開發(fā)中考慮的問題更多一些,網(wǎng)上的資料更少一些脓豪,遇到問題后最好的解決辦法就是查看[官方文檔](https://developer.apple.com/search/?q=mac%20os%20x)巷帝。
什么時候需要授權(quán)?
在OS X開發(fā)中扫夜,我們會經(jīng)常遇到權(quán)限的問題楞泼,比如你安裝APP的時候,你如你需要刪除一些配置文件的時候都需要權(quán)限笤闯,不然就無法操作堕阔,我們的項目中需要做一個卸載工具和退域工具,里面涉及到刪除配置文件以及退出進程等一些需要權(quán)限的操作颗味。
具體內(nèi)容
刪除配置文件的時候超陆,通常情況下通過`NSTask`這個類打開終端,輸入命令進行操作的脱衙,如:
[NSTask launchedTaskWithLaunchPath:@"/usr/bin/sudo" arguments: [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchDaemons/com.tencent.MacScmClient.Daemon.plist",nil]];
這行代碼就是用NSTask
來調(diào)用終端來進行刪除文件的侥猬,大概意思就是在終端輸入sudo
,然后執(zhí)行launchctl
和unload
的命令捐韩,但是呢退唠,怎樣寫IE肯定是不行的,為什么呢荤胁,因為你沒有權(quán)限這么做瞧预,有興趣的同學(xué)可以在終端嘗試一下,輸入這個命令是需要輸入密碼的,所以怎么獲取權(quán)限呢垢油?
之前我們的工程中有獲取授權(quán)的代碼盆驹,這個下文再說,當時同事寫了一段代碼使用AppleScript來獲取權(quán)限的滩愁,我再網(wǎng)上找了下躯喇,也找到了原文,具體的代碼如下:
Boolean runProcessAsAdministrator( NSString *scriptPath, NSArray *arguments,BOOL isAdmin, NSString **output,NSString **errorDescription)
{
NSString * allArgs = [arguments componentsJoinedByString:@" "];
NSString *isAdminPre = @"";
if (isAdmin) {
isAdminPre = @"with administrator privileges";
}
NSString * fullScript = [NSString stringWithFormat:@"%@ %@", scriptPath, allArgs];
NSDictionary *errorInfo = [NSDictionary new];
NSString *script = [NSString stringWithFormat:@"do shell script \"%@\" %@", fullScript, isAdminPre];
NSLog(@"script = %@",script);
NSAppleScript *appleScript = [[NSAppleScript new] initWithSource:script];
NSAppleEventDescriptor * eventResult = [appleScript executeAndReturnError:&errorInfo];
// Check errorInfo/var/tmp
if (! eventResult)
{
// Describe common errors
*errorDescription = nil;
if ([errorInfo valueForKey:NSAppleScriptErrorNumber])
{
NSNumber * errorNumber = (NSNumber *)[errorInfo valueForKey:NSAppleScriptErrorNumber];
if ([errorNumber intValue] == -128)
*errorDescription = @"The administrator password is required to do this.";
}
// Set error message from provided message
if (*errorDescription == nil)
{
if ([errorInfo valueForKey:NSAppleScriptErrorMessage])
*errorDescription = (NSString *)[errorInfo valueForKey:NSAppleScriptErrorMessage];
}
return NO;
}
else
{
// Set output to the AppleScript's output
*output = [eventResult stringValue];
return YES;
}
}
這段代碼就是用AppleScript
來獲取權(quán)限的硝枉,使用方法如下:
NSString *output = @"";
NSString *error= @"";
runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchAgents/com.******.******.plist",nil],false, &output, &error);
但是這么寫會有一個問題廉丽,就是我是獲得權(quán)限了,但是卸載的時候往往不可能只刪除一個配置文件妻味,而是要刪除很多個配置文件正压,起初我是這么寫的:
runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchAgents/com.******.******.plist",nil],false, &output, &error);
NSLog(@"output = %@, error = %@", output, error);
runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchDaemons/com.******.******.Daemon.plist",nil], true, &output, &error);
NSLog(@"output = %@, error = %@", out
我就很開心的運行,但是問題來了责球,什么問題呢焦履?就是你刪除幾個配置文件就需要輸入幾次用戶名和密碼,這樣的話用戶體驗相當?shù)牟缓贸猓硐霠顟B(tài)是什么呢嘉裤?輸入一次用戶名和密碼之后,獲取的權(quán)限以后其他的刪除配置文件的操作就不需要再次獲取權(quán)限了校套,所以我就看了下前輩們是怎么寫的价脾,大概的代碼如下:
int main(int argc, char * argv[]) {
@autoreleasepool {
if (argc == 2) {
NSLog(@"AuthHelperTool executing self-repair");
OSStatus myStatus;
AuthorizationFlags myFlags = kAuthorizationFlagDefaults;
AuthorizationRef myAuthorizationRef;
myStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, myFlags, &myAuthorizationRef);
if (myStatus != errAuthorizationSuccess)
return myStatus;
AuthorizationItem myItems = {kAuthorizationRightExecute, 0, NULL, 0};
AuthorizationRights myRights = {1, &myItems};
myFlags = kAuthorizationFlagDefaults |
kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagPreAuthorize |
kAuthorizationFlagExtendRights;
myStatus = AuthorizationCopyRights (myAuthorizationRef, &myRights, NULL, myFlags, NULL );
if (myStatus != errAuthorizationSuccess)
return myStatus;
char *myToolPath = argv[1];
char *myArguments[] = {argv[1], "--fix", NULL};
FILE *myCommunicationsPipe = NULL;
myFlags = kAuthorizationFlagDefaults;
//這句是獲取管理員權(quán)限的代碼
myStatus = AuthorizationExecuteWithPrivileges(myAuthorizationRef, myToolPath, myFlags, myArguments, &myCommunicationsPipe);
return 0;
}
else if (argc == 3){
NSString *output = @"";
NSString *error= @"";
runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchAgents/com.******.******.plist",nil],false, &output, &error);
runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchDaemons/com.******.******.Daemon.plist",nil], true, &output, &error);
}
這段代碼是個可執(zhí)行文件,然后在ViewController
中調(diào)用的這個可執(zhí)行文件笛匙。我們看到這個獲取權(quán)限的方法是使用Authorization
的類來獲取權(quán)限的侨把,這個也是官方文檔中的獲取權(quán)限的方法,具體的可以參考這里
妹孙,繼續(xù)說上面的代碼秋柄,前文說了這個是個可執(zhí)行文件,是在ViewController
中調(diào)用的蠢正,代碼如下:
NSString *helperToolPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/ExitDomain"] ;
NSLog(@"path = %@",helperToolPath);
NSArray *args = [NSArray arrayWithObjects:helperToolPath, nil];
[NSTask launchedTaskWithLaunchPath:helperToolPath arguments:args];
從這兒看到骇笔,我們是用NSTask
這個類來打開終端來執(zhí)行這個可執(zhí)行文件的,參數(shù)為helperToolPath
也就是這個可執(zhí)行文件的路徑嚣崭,所以到了可執(zhí)行文件的mian
函數(shù)中笨触,argc
就是2,然后來執(zhí)行下面的獲取權(quán)限的代碼的雹舀,下面獲取權(quán)限的代碼有個需要注意的地方:
char *myToolPath = argv[1];
char *myArguments[] = {argv[1], "--fix", NULL};
FILE *myCommunicationsPipe = NULL;
myFlags = kAuthorizationFlagDefaults;
//這句是獲取管理員權(quán)限的代碼
myStatus = AuthorizationExecuteWithPrivileges(myAuthorizationRef, myToolPath, myFlags, myArguments, &myCommunicationsPipe);
這幾行代碼需要注意芦劣,首先myToolPath
這個參數(shù)就是我們從ViewController
中配置的那個參數(shù),也就是可執(zhí)行文件的路徑说榆,其次myArguments
里面多了個--fix
虚吟,這個不是命令寸认,只是為了使argc
的個數(shù)變?yōu)槿?/p>
myStatus = AuthorizationExecuteWithPrivileges(myAuthorizationRef, myToolPath, myFlags, myArguments, &myCommunicationsPipe);
這行代碼就是獲取管理員權(quán)限的代碼串慰,從官方文檔來看在10.1和10.7中是不能使用的偏塞,具體的可以看官方文檔,如果想測試自己的系統(tǒng)能不能用邦鲫,請參考github上的這個第三方庫中用的測試方法灸叼,然后這行代碼還有個作用,就是它又調(diào)用了一次這個可執(zhí)行文件掂碱,方法中有個參數(shù)是myToolPath
怜姿,這個就是可執(zhí)行文件的路徑,所以esle if
中的代碼才會執(zhí)行疼燥,按照這種方法寫完后就只需要輸入一次用戶名和密碼,用戶體驗明顯好轉(zhuǎn)蚁堤。
總結(jié)##
本文講了兩種獲取權(quán)限的方法醉者,一種是使用`AppleScript`來獲取權(quán)限,這樣寫是能獲取到權(quán)限披诗,但是如多刪除多個文件的話撬即,就需要輸入多次用戶名和密碼;另一種是用`Authorization`這個類來獲取權(quán)限呈队,先獲取到權(quán)限后剥槐,然后再執(zhí)行刪除配置文件的操作就可以了,需要注意的地方就是宪摧,我再使用第二種方法的時候`esle if`中調(diào)用的還是`AppleScript`刪除文件的方法粒竖,沒有用`NSTask`這個類,這個說明一下几于,這兒完全可以使用`NSTask`這個類做這些事情蕊苗,也就是下面這段代碼:
runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchAgents/com.******.******.plist",nil],false, &output, &error);
完全可以用這段替換掉:
[NSTask launchedTaskWithLaunchPath:@"/usr/bin/sudo" arguments: [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchDaemons/com.tencent.MacScmClient.Daemon.plist",nil]];
這個只是我項目中遇到的一些關(guān)于權(quán)限的問題以及解決辦法,還有很多不足之處沿彭,希望各路大神多多指正朽砰,我也會再繼續(xù)深入學(xué)習(xí),及時修改這篇文章喉刘。
參考資料
[https://developer.apple.com/search/?q=Authorization&type=Guides]
[http://stackoverflow.com/questions/29796363/how-to-add-root-privileges-to-my-osx-application]
[http://codego.net/213006/]
[http://www.tanhao.me/pieces/1279.html/]
[https://github.com/sveinbjornt/STPrivilegedTask]