73586


<https://github.com/google/macops-
keychainminder/blob/b6ab824321fc677aa80a217348023dcdf416b68a/Common/Common.m>  
や  
<https://github.com/bikram990/NoMADLogin-
AD/blob/a8ff99b12335bbaa307052b4df27ca3163b8f663/Carthage/Checkouts/NoMAD-
ADAuth/NoMAD-ADAuth/KerbUtil.m>  
に記載されている `AuthorizationEnvironment`を生成するObjective-
CのコードをSwiftで書くにはどうすればよいでしょうか？

自分なりに、下記のように書きました

[code]

    var authorizationRef: AuthorizationRef?
    let authorizationFlags: AuthorizationFlags = [
        .extendRights,
        .interactionAllowed,
        .preAuthorize
    ]
    
    var userName = NSUserName()
    var password = "password"
    
    let userNameItem = AuthorizationItem(
        name: kAuthorizationEnvironmentUsername,
        valueLength: userName.lengthOfBytes(using: .utf8),
        value: UnsafeMutableRawPointer(&userName),
        flags: 0
    )
    let passwordItem = AuthorizationItem(
        name: kAuthorizationEnvironmentPassword,
        valueLength: password.lengthOfBytes(using: .utf8),
        value: UnsafeMutableRawPointer(&password),
        flags: 0
    )
    var items = [
        userNameItem,
        passwordItem
    ]
    
    var authorizationEnvironment = AuthorizationEnvironment(
        count: 2,
        items: &items
    )
    
    let status = AuthorizationCreate(
        nil,
        &authorizationEnvironment,
        authorizationFlags,
        &authorizationRef
    )
    
    以下略
    
[/code]

ところが下記の警告が出ています。

[code]

    Passing 'String' to parameter, but argument 'name' should be a pointer that outlives the call to 'init(name:valueLength:value:flags:)'
    
[/code]

や

[code]

    Inout expression creates a temporary pointer, but argument 'items' should be a pointer that outlives the call to 'init(count:items:)'
    
[/code]

や

[code]

    Initialization of 'UnsafeMutableRawPointer' results in a dangling pointer
    
[/code]

といったものです。

また作り出した`AuthorizationEnvironmentインスタンス`を`AuthorizationCreate`に渡した結果、Objective-
Cだとパスワードの要求をされなくなったのですが、Swiftで同様のコードを再現しようとすると、パスワードを要求されるので、おそらく`AuthorizationEnvironmentインスタンス`の生成がうまくできていないのだと推測しています。

蛇足かもしれませんが、はじめ `userName`変数は`let`で宣言していたのですが、

[code]

    Cannot pass immutable value as inout argument: 'userName' is a 'let' constant
    
[/code]

というコンパイルエラーが出たので意味もわからず `var` で宣言しなおして、コンパイルエラーを解消しています。（その他 password変数も同様の理由で
`var` で宣言しています）


CC BY-SA 4.0
2021-01-26T03:54:14.200
73580
2021-01-26T06:29:15.520
2021-01-26T04:34:03.740
9008
9008
question
swift objective-c macos
AuthorizationEnvironmentを生成するObjective-CのコードをSwiftで書く方法をおしえてください


> Passing 'String' to parameter, but argument 'name' should be a pointer that
> outlives the call to 'init(name:valueLength:value:flags:)'

Swiftでは、String型の値を`UnsafePointer<Int8>`型の引数に渡すことができますが、その時に生成されるポインタは一時的なもので、「渡されたポインタを後で使うために保持する」ような構造体を作成する場合には、そのまま使用してはいけません。この使い方は残念ながらある特定の条件が揃ってしまうと動いているように見えることがあるため、例えば「本番環境でだけ現れる厄介なエラー」なんかの原因になったりすることがあります。

できるだけ安全にそのようなポインタを作りたければ、書き換え可能な領域にメモリを割り当ててコピーを保持する必要があるでしょう。C標準関数の`strdup`なんかが使えます。

> Inout expression creates a temporary pointer, but argument 'items' should be
> a pointer that outlives the call to 'init(count:items:)'

配列をポインタ型の変数に渡すときにも同様の「一時的なポインタ」が作られるので、上記と同じことが言えます。文字列と同様に安定な領域にメモリを割り当ててコピーを作るか、この場合ならポインタが使用される可能性のある範囲を`withUnsafeMutableBufferPointer`などで囲んで使ってやることになります。

> Initialization of 'UnsafeMutableRawPointer' results in a dangling pointer

`UnsafeMutableRawPointer(&userName)`のような使い方は非常によく見られる(そこそこ有名なライブラリでも使われていたことがあると記憶しています)誤った使い方の典型例です。`UnsafeMutableRawPointer.init`に文字列を指定すると、一時的な領域が作られ、その領域のアドレスが渡されるのですが、その領域のアドレスは`UnsafeMutableRawPointer.init`が終了すると、無効になります。

先ほども書きましたが、この「誤った使い方」の厄介なところは、特定の状況ではそれが何度繰り返して実行しても動いているように見えてしまうことです。

**Xcodeのポインタに関する警告は絶対に無視しないようにしましょう。**

  * テスト環境では動くのに、本番環境では動かない
  * 特定の(多くの場合「以前の」)バージョンのiOSでは動くのに、別のバージョン(例えば「最新の」)では動かない

なんてことが起こると、「これはiOSのバグのせいに違いない」なんて書き込みをされる方がAppleのDevForumsなんかに時々現れるのですが、コードを見せてもらうと「ほら、こんなところで間違ったポインタの使い方をしています」なんてことになります。  
(誤ったマルチスレッド処理でも同じような状況が発生しますが。)

> Cannot pass immutable value as inout argument: 'userName' is a 'let'
> constant

こういったC言語ベースのAPIしか用意されていない場合、絶対に内容を書き換えないだろうポインタに`const`等の宣言がなされていないことがよくあって、その場合、mutableなポインタが必要になります。そのためにはその元になる変数をvarにする必要が出てくる、と言うわけで、この場合、「意味もわからず
`var` で宣言しなおし」なんてことも必要になるでしょう。

先に書いた「一時的ポインタをあとあと使用される場所に使おうとしている」のさえ気をつければ、そこらへんは「とにかく `var`
にする」と言う対応にならざるを得ません。

* * *

と言うわけでざっくりと上記の状況を解決すると、以下のようなコードになります。

[code]

        var authorizationRef: AuthorizationRef?
        let authorizationFlags: AuthorizationFlags = [
            .extendRights,
            .interactionAllowed,
            .preAuthorize
        ]
        
        let userName = NSUserName()
        let password = "password"
        
        let cAuthorizationEnvironmentUsername = strdup(kAuthorizationEnvironmentUsername)!
        defer {free(cAuthorizationEnvironmentUsername)}
        let cUserName = strdup(userName)!
        defer {free(cUserName)}
        
        let cAuthorizationEnvironmentPassword = strdup(kAuthorizationEnvironmentPassword)!
        defer {free(kAuthorizationEnvironmentPassword)}
        let cPassword = strdup(password)!
        defer {free(cPassword)}
        
        let userNameItem = AuthorizationItem(
            name: cAuthorizationEnvironmentUsername,
            valueLength: userName.utf8.count,
            value: cUserName,
            flags: 0
        )
        
        let passwordItem = AuthorizationItem(
            name: cAuthorizationEnvironmentPassword,
            valueLength: password.utf8.count,
            value: cPassword,
            flags: 0
        )
        var items = [
            userNameItem,
            passwordItem
        ]
        items.withUnsafeMutableBufferPointer {bufItems in
            var authorizationEnvironment = AuthorizationEnvironment(
                count: UInt32(bufItems.count),
                items: bufItems.baseAddress
            )
            let status =  AuthorizationCreate(
                nil,
                &authorizationEnvironment,
                authorizationFlags,
                &authorizationRef
            )
            //`authorizationRef`はこのブロック内で使う必要がある?
            //...
        }
    
[/code]

Swiftのポインタに関する警告を安全に黙らせることだけを考えて書いたので、実際に使う分には何か問題が出るかもしれません。その場合にはコメント等でお知らせください。


CC BY-SA 4.0
2021-01-26T05:43:28.057
73586
2021-01-26T06:29:15.520
2021-01-26T06:29:15.520
13972
13972
73580
answer