22239


NSNumberについてですが  
<https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6-ID67>  
AnyObject等の変換可能・不可能な型があり、その場合は数値をオブジェクトとして扱うための型「NSNumber」を利用しようという理解でいます。  
（違ったらすいません。。。）  
わからないのは、

[code]

    class Test : NSObject {
       var testUint64: UInt64 = 0x0000000000000000
    }
    
    var test1 = Test()
    var test2 = Test()
    test1.setValue(test2.testUint64, forKeyPath: "testUint64")
    
[/code]

この場合、setValueの第一引数はAnyObject?のため怒られるため、  
下記のようにUInt64→NSNumberに変えると問題なかったのですが、NSNumberのサイズ？はどうなっているのか…とよくわからない次第です。

[code]

    class Test : NSObject {
       var testUint64: NSNumber = 0x0000000000000000
    }
    
[/code]

上記については確かに怒られはしないのですが、  
実際、 UInt64（64bit の符号ありと符号なし）の値を  
get/setしたい場合はsetValueは使用できないということなのでしょうか。


CC BY-SA 3.0
2016-02-18T10:17:50.907
22233
2016-03-11T00:49:06.763
2016-03-11T00:49:06.763
12842
12842
question
swift
NSNumber型がよくわからない


そのあたりのことを整理して考えようと思うと、次の3つのことをすべて理解していないといけません。  
(1) NSNumberとはどんなクラスか  
(2) Swiftの数値型からObjective-Cへのブリッジング機構はどう働くか  
(3) KVCメソッドの一つであるsetValue( _:forKeyPath:)やsetValue(_ :forKey:)はどう働くか

まず(1)については、クラスリファレンスを熟読してみてください。  
[NSNumber Class
Reference](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/index.html#//apple_ref/occ/cl/NSNumber)  
実はNSNumber型の親クラスにNSValueクラスというのがあって、これは数値に限らず任意のstruct(参照型は持てない)を保持することができます。NSNumberは数値を扱うことに特化したサブラクラスにすることによって、

  * サポートする数値型(BOOLも含まれます)の値を直接指定してイニシャライザを呼ぶことができる
  * 内部の実際のデータ型によらず、値をサポートするデータ型に変換して取り出すことができる

ようになっています。  
したがって、

[code]

    var testUint64: NSNumber = NSNumber(unsignedLongLong: 0)
    
[/code]

とやると、testUint64に保持されているNSNumberは内部的にはUInt64として(実際には「与えられたデータの精度を損なうことなく保持できるデータ形式」で)データを保持します。(Swiftでは16進表現の桁数はどの数値型になるかとは無関係で、0x0000000000000000と桁数たっぷりで表現しても、他に何も指定がなければ整数リテラルのデフォルトであるInt型になりますのでご注意を。)

(2)については、あなたが引用されたリンクの通り(実際にはCGFloat型もNSNumberにブリッジされます)ですが、「NSNumberがサポートするデータ型」と「SwiftからObjective-
Cクラスへブリッジングできるデータ型」とは直接の関係はありません。上に書いたようにNSNumberはUInt64(Cならunsigned long
long)の数値データを精度を失うことなく保持することができますが、Swiftはブリッジングしてくれませんし、CGFloat型を直接扱うメソッドはNSNumberには定義されていませんが、Swiftはブリッジングしてくれます。  
また、Int,UInt,Float,Double,Bool,CGFloatを直接NSNumberに変換するときのほか、AnyObjectやAnyObject?など、NSNumber型の値を保持できる型に変換するときにもブリッジングしてくれます。  
(さらに複雑な例だと、`Array<Int>`は中身を全部NSNumberとして保持するNSArrayにブリッジングされます。)

(3)についてですが、Objective-
CのKVC機構はよくできていて、代入先がshortやらintやらのプリミティブ型であっても、NSNumberを`setValue(_:forKeyPath:)`や`setValue(_:forKey:)`に渡すと、正しく代入先のデータ型に変換して代入してくれます。

というわけで、(2)と(3)の合わせ技で考えると、(注「追記」を先に読んでください。)

[code]

    class Test : NSObject {
        var testUint64: UInt64 = 0x0000000000000000
    }
    
    var test1 = Test()
    var test2 = Test()
    test1.setValue(UInt(test2.testUint64), forKeyPath: "testUint64")
    test1.setValue(0xFFFFFFFF_FFFFFFFF as UInt, forKeyPath: "testUint64")
    
[/code]

は、動作することがお分かりになると思います。

したがって、  
_UInt64（64bit の符号ありと符号なし）の値を  
get/setしたい場合はsetValueは使用できないということなのでしょうか。_  
に対する答えとしては「使用できないわけではないが、一工夫必要になる」と言ったところです。

「追記」コメントで指摘して頂いたように、上記のコードはUIntが32bitの場合正しく動作しません。UInt/Intが32bitの場合の「一工夫」はこんな書き方になるでしょう。

[code]

    test1.setValue(NSNumber(unsignedLongLong: test2.testUint64), forKeyPath: "testUint64")
    test1.setValue(NSNumber(unsignedLongLong: 0xFFFFFFFF_FFFFFFFF), forKeyPath: "testUint64")
    
[/code]

iOS用のアプリの場合、まだ32bitアーキテクチャーの機種を無視するわけにはいかないでしょうから、こちらを先に書くべきでした。  
(3)に書いた「正しく代入先のデータ型に変換」は、なかなか賢いので代入したい値がUIntで確実に表せることがわかっている場合には使用可能ですが、かなりリスキーで、正直おすすめできない書き方です。(コメントでのご指摘ありがとうございます。)


CC BY-SA 3.0
2016-02-18T12:12:24.160
22239
2016-02-18T14:21:32.190
2016-02-18T14:21:32.190
13972
13972
22233
answer