我正在尝试移植一个方法来挂载异步的卷,从Objective C到Swift。 但是我遇到了一些代码问题。我希望有人能给我一个答案。
问题是: kNAUIOptionKey,kNAUIOptionNoUI和kNetFSUseGuestKey:未解析的标识符。我找不到Swift中的等价物。
kCFBooleanTrue:CFBoolean不能转换为UnsafePointer。
> / *和* /之间的代码很难转换为Swift,而且欢迎提供一些帮助。import Foundation
import Cocoa
import NetFS
func mountVolumeAsync(#username:String, #password:String, #ipadres:String, #proto:String, #mountpoint:String) -> Bool{
var theURLPath: NSURL
if username.lowercaseString == "guest" {
println("Volume will be mounted as Guest user.....")
let thestring = "\(proto)://\(ipadres)/\(mountpoint)"
let theURLPath = NSURL(string:thestring)
if theURLPath == nil {
println("Path to file is invalid.")
return false
}
} else {
let thestring = "\(proto)://\(username):\(password)@\(ipadres)/\(mountpoint)"
let theURLPath = NSURL( string:thestring)
if theURLPath == nil {
println("Path to file is invalid.")
return false
}
}
var mount_options = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, nil, nil);
if (mount_options != nil) {
CFDictionarySetValue(mount_options, kNAUIOptionKey, kNAUIOptionNoUI);
if username.lowercaseString == "guest" {
CFDictionarySetValue( mount_options, kNetFSUseGuestKey, kCFBooleanTrue);
}
}
var status = false
/* ---- Objective C Code to port to Swift Code ------
AsyncRequestID requestID = NULL;
dispatch_queue_t queue =dispatch_get_main_queue();
NetFSMountURLAsync(
CFBridgingRetain(theURLPath),
NULL,
(__bridge CFStringRef) username,
(__bridge CFStringRef) passwd,
mount_options,
NULL,
&requestID,
queue,
^(int status, AsyncRequestID requestID, CFArrayRef mountpoints) {
NSLog(@"mounted: %d - %@", status, (__bridge NSArray *) mountpoints);
});
if (!status) {
NSString *msg = [NSString stringWithFormat:@"[%@] is not mounted! Check your params",mntpt];
return NO;
}
*/
return status
}
我仍在努力解决这个问题,但不幸的是我没有成功。 没有人回答这个问题,我仍在寻找答案。 我将代码更改为以下内容并进行编译,但不会运行。 在调试时我会发现以下错误:
打印open_options的说明:
(CFMutableDictionary!) open_options = 1 key/value pair {
[0] =
<Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x4f4955414e80).
The process has been returned to the state before expression evaluation.>
我在NetFS框架中找到了这个信息:
kNAUIOptionKey = UIOption抑制身份验证对话框UI。 *
支持以下mount_options字典键:
我希望有人可以提供帮助。
func mountVolumeAsync(#username:String, #password:String, #ipadres:String, #proto:String, #mountpoint:String) -> Bool{
var theURLPath: NSURL? = nil
if username.lowercaseString == "guest" {
// "Volume will be mounted as Guest user....."
let thestring = StringWithFormat("%@://%@/%@",proto,ipadres,mountpoint)
let theURLPath = NSURL(string:thestring)
if theURLPath == nil {
println("Path to file is invalid.")
return false
}
} else {
let thestring = StringWithFormat("%@://%@:%@@%@/%@",proto,username,password,ipadres,mountpoint)
let theURLPath = NSURL(string:thestring)
if theURLPath == nil {
println("Path to file is invalid.")
return false
}
}
var mount_options = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, nil, nil);
var open_options = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, nil, nil);
if open_options != nil {
CFDictionarySetValue(open_options,"kNAUIOptionKey","NoUI")
}
if (mount_options != nil) {
//CFDictionarySetValue(mount_options,kNAUIOptionKey"UIOption")
if username.lowercaseString == "guest" {
let kNetFSUseGuestKey = "Guest"
//CFDictionarySetValue( mount_options, kNetFSUseGuestKey, kCFBooleanTrue);
CFDictionarySetValue(open_options,kNetFSUseGuestKey, "kCFBooleanTrue")
}
}
var status = false
var requestID: AsyncRequestID = nil
let queue = dispatch_get_main_queue()
NetFSMountURLAsync(
theURLPath,
nil,
username as NSString,
password as NSString,
mount_options,
open_options,
&requestID,
queue)
{(stat:Int32, requestID:AsyncRequestID, mountpoints:CFArray!) -> Void in
println("mounted: \(stat) - \(mountpoints)")
}
if status == false {
let msg = "[\(mountpoint) is not mounted! Check your params"
println(msg)
return false
}
}
答案 0 :(得分:1)
你的Swift代码崩溃是因为将一个Swift字符串传递给一个接受UnsafePointer
的函数,例如CFDictionarySetValue
,会向它传递一个指向编码为UTF-8的字符串字符数据的指针,而不是字符串对象。此外,字符数据仅在函数调用结束之前有效,之后将被释放。
您正在寻找的符号在Swift中不存在,因为它们依赖于宏扩展。 Swift支持#define
用于愚蠢的令牌(如#define FOO 1
),但不支持像CFSTR
这样的宏。您希望使用的任何此类密钥都需要复制到Swift端。要查找它们的值,请在C / Objective-C文件中使用它们并按住Command键单击它们以转到它们的定义。你会发现:
#define kNAUIOptionKey CFSTR("UIOption")
#define kNAUIOptionNoUI CFSTR("NoUI")
#define kNetFSUseGuestKey CFSTR("Guest")
kCFBooleanTrue
由于同样的原因而丢失(尽管它不使用CFSTR
)。
您有两个选项可以将CFSTR
字符串添加到Swift代码中。第一个也是最简单的就是将值声明为Swift变量:
let kNAUIOptionKey = "UIOption"
let kNAUIOptionNoUI = "NoUI"
let kNetFSUseGuestKey = "Guest"
第二个选项是通过桥接标题公开值(使用不同的名称以避免在C方面发生冲突):
NSString* NAUIOptionKey;
NSString* NAUIOptionNoUI;
NSString* NetFSUseGuestKey;
然后在Objective-C文件的顶层有这样的东西:
#include <NetFS/NetFS.h>
NSString* NAUIOptionKey = (NSString*)kNAUIOptionKey;
NSString* NAUIOptionNoNui = (NSString*)kNAUIOptionNoUI;
NSString* NetFSUseGuestKey = (NSString*)kNetFSUseGuestKey;
NSString
和CFStringRef
是同样的事情。记录的行为是从一个到另一个很好(它被称为免费桥接)。
此解决方案可能是最向前兼容的,因为它不需要您知道定义的值扩展到什么。 Apple理论上可以改变SDK版本之间的字符串值,因此Swift版本可能会在未来中断(虽然编译后的二进制文件将继续工作,并且只要Apple支持它,针对相同SDK的编译将继续工作,因此它不是很大交易)。
kCFBooleanTrue
与NSNumber(bool: true)
相同,因为再次进行免费桥接。
同样地,NSDictionary
是免费的桥接到CFDictionaryRef
,所以我鼓励你使用它而不是CFDictionary API,因为所有的void指针都很难使用Swift。这在Swift中特别有吸引力,因为编译器认识到NSDictionary*
和CFDictionaryRef
是相同的东西。
那就是:
import Foundation
let kNAUIOptionKey = "UIOption"
let kNAUIOptionNoUI = "NoUI"
let kNetFSUseGuestKey = "Guest"
func exampleCFDictionary() -> CFDictionaryRef {
var dict = NSMutableDictionary()
dict[kNAUIOptionKey] = kNAUIOptionNoUI
dict[kNetFSUseGuestKey] = NSNumber(bool: true)
return dict
}
println(exampleCFDictionary())
我担心你会自己为NetFS本身,但这应该可以解决你的字典问题。你显然不需要单独的函数来创建字典,我这样做是因为它比复制你的代码更简单。
最后,我们鼓励您创建short, self-contained and correct examples您遇到的问题。很多人都不了解NetFS,但是看起来你的Swift和Core Foundation类型比NetFS更多。有了一个好的SSCCE,你可以将代码减少到这三行:
var openOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nil, nil)
CFDictionarySetValue(openOptions, "kNAUIOptionKey", "NoUI")
println(openOptions)
很多人在这里知道这有什么问题,你可能会在几分钟内而不是三个月后得到答案。
答案 1 :(得分:0)
我做了一些修改,并使其与Xcode 7 beta 5和Swift 2.0一致。 Async比Sync版本更好,因为它不会阻塞线程。我不得不桥接任何Objective-C参数。常量完美地让它们独自存在,当我记录它们的名字时,它们会达到正确的值。这消除了上述一些额外的工作。但代码确实给了我一个很好的起点,所以我很感谢这两个条目。
我确实使用过CFMutableDictionary和NSMutableDictionary。 CFMutableRef不起作用。此类型最近可能在方法中已更改或被误读。开放和坐骑选项也是倒退的。
一些观察:只有afp://支持密码更改。密码更改标志也不起作用
public var kNetFSChangePasswordKey: String { get }
当用作打开选项时会导致无限循环。也许我错了。如果有人能够得到那面旗帜,那将会很好。我尝试在开放选项字典中设置它并将其设置为true,除了无休止地旋转之外没有太多运气。也许这是API中的一个错误,并没有人将其覆盖。
作为一种解决方法,当我希望用户更改密码时,我有一个密码更改的复选框,然后我通过afp://连接密码以及作为空白“”发送的密码,这允许用户在登录时手动更改密码,如果用户被迫通过服务器更改密码,它将起作用。在smb3下,用户没有该选项,它将跳过来自服务器的任何强制密码授权。然后,下次用户登录更改密码标志时,它将通过SMB3连接。如果您想知道为什么使用SMB3,因为它明显快于Apple文件协议。微软终于做对了。去搞清楚。
此外,afp和smb上的服务器与URL上的.local扩展名连接得更快。如果您尝试afp://没有它,您可能会遇到无限循环(可能是DNS问题)。但是。通过SMB连接时,.local也更快。虽然SMB将在没有.local扩展名的情况下连接,但它总是与.local附加即时连接。我不是通过互联网连接,但是当Google Fiber在我们的商店时,我们可能会尝试使用它。
func openOptionsDict() -> CFMutableDictionary {
let dict = NSMutableDictionary()
dict[kNAUIOptionKey] = kNAUIOptionAllowUI
dict[kNetFSUseGuestKey] = false
return dict
}
func mountOptionsDict() -> CFMutableDictionary {
let dict = NSMutableDictionary()
return dict
}
func asyncMountShare(serverAddress: String, shareName: String, userName: String, password: String) {
let fm = NSFileManager.defaultManager()
let mountPoint = "/Volumes/".stringByAppendingString(shareName)
var isDir : ObjCBool = false
if fm.fileExistsAtPath(mountPoint, isDirectory:&isDir) {
if isDir {
unmount(mountPoint, 0)
print("unmount \(mountPoint)")
}
}
let escapedAddress = serverAddress.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())
let shareAddress = NSURL(string: escapedAddress!)!
let openOptions : CFMutableDictionary = openOptionsDict()
let mount_options : CFMutableDictionary = mountOptionsDict()
var requestID: AsyncRequestID = nil
let queue = dispatch_get_main_queue()
NetFSMountURLAsync(shareAddress, nil, userName as NSString, password as NSString, openOptions, mount_options, &requestID, queue)
{(stat:Int32, requestID:AsyncRequestID, mountpoints:CFArray!) -> Void in
print("msg: \(stat) mountpoint: \(mountpoints)")
}
}