objective-c – 64位NS_OPTIONS位掩码

上下文

我正在使用NS_OPTIONS宏来创建一个位掩码.我已经为它分配了一种类型的NSInteger,因为我正在构建一个64位平台,这应该给我总共63个“槽”可以使用(64位少一位用于签名).

这是枚举:

typedef NS_OPTIONS(NSInteger,LPSvgoPlugin) {
    LPSvgoPluginNone                            = 0,LPSvgoPluginCleanupAttrs                    = 1 << 0,LPSvgoPluginRemoveDoctype                   = 1 << 1,LPSvgoPluginRemoveXMLProcInst               = 1 << 2,LPSvgoPluginRemoveComments                  = 1 << 3,LPSvgoPluginRemoveMetadata                  = 1 << 4,LPSvgoPluginRemoveTitle                     = 1 << 5,LPSvgoPluginRemoveDesc                      = 1 << 6,LPSvgoPluginRemoveUselessDefs               = 1 << 7,LPSvgoPluginRemoveEditorsNSData             = 1 << 8,LPSvgoPluginRemoveEmptyAttrs                = 1 << 9,LPSvgoPluginRemoveHiddenElems               = 1 << 10,LPSvgoPluginRemoveEmptyText                 = 1 << 11,LPSvgoPluginRemoveEmptyContainers           = 1 << 12,LPSvgoPluginRemoveViewBox                   = 1 << 13,LPSvgoPluginCleanupEnableBackground         = 1 << 14,LPSvgoPluginMinifyStyles                    = 1 << 15,LPSvgoPluginConvertStyletoAttrs             = 1 << 16,LPSvgoPluginConvertColors                   = 1 << 17,LPSvgoPluginConvertPathData                 = 1 << 18,LPSvgoPluginConvertTransform                = 1 << 19,LPSvgoPluginRemoveUnkNownsAndDefaults       = 1 << 20,LPSvgoPluginRemoveNonInheritableGroupAttrs  = 1 << 21,LPSvgoPluginRemoveUselessstrokeAndFill      = 1 << 22,LPSvgoPluginRemoveUnusednS                  = 1 << 23,LPSvgoPluginCleanupIDs                      = 1 << 24,LPSvgoPluginCleanupNumericValues            = 1 << 25,LPSvgoPluginMoveElemsAttrsToGroup           = 1 << 26,LPSvgoPluginMoveGroupAttrsToElems           = 1 << 27,LPSvgoPluginCollapseGroups                  = 1 << 28,LPSvgoPluginRemoveRasterImages              = 1 << 29,LPSvgoPluginMergePaths                      = 1 << 30,LPSvgoPluginConvertShapetoPath              = 1 << 31,LPSvgoPluginSortAttrs                       = 1 << 32,LPSvgoPluginTransformsWithOnePath           = 1 << 33,LPSvgoPluginRemoveDimensions                = 1 << 34,LPSvgoPluginRemoveAttrs                     = 1 << 35,LPSvgoPluginAddClassesToSVGElement          = 1 << 36,LPSvgoPluginRemoveStyleElement              = 1 << 37
};

最高值是左移37位,远低于我应该可用的63位.

问题

我从该枚举创建一个掩码,如下所示:

LPSvgoPlugin defaultPluginMask = LPSvgoPluginNone;
        defaultPluginMask = (LPSvgoPluginCleanupAttrs
                               |LPSvgoPluginRemoveDoctype
                               |LPSvgoPluginRemoveXMLProcInst
                               |LPSvgoPluginRemoveComments
                               |LPSvgoPluginRemoveMetadata
                               |LPSvgoPluginRemoveDesc
                               |LPSvgoPluginRemoveUselessDefs
                               |LPSvgoPluginRemoveEditorsNSData
                               |LPSvgoPluginRemoveEmptyAttrs
                               |LPSvgoPluginRemoveHiddenElems
                               |LPSvgoPluginRemoveEmptyText
                               |LPSvgoPluginRemoveEmptyContainers
                               |LPSvgoPluginCleanupEnableBackground
                               |LPSvgoPluginMinifyStyles
                               |LPSvgoPluginConvertStyletoAttrs
                               |LPSvgoPluginConvertColors
                               |LPSvgoPluginConvertPathData
                               |LPSvgoPluginConvertTransform
                               |LPSvgoPluginRemoveUnkNownsAndDefaults
                               |LPSvgoPluginRemoveNonInheritableGroupAttrs
                               |LPSvgoPluginRemoveUselessstrokeAndFill
                               |LPSvgoPluginRemoveUnusednS
                               |LPSvgoPluginCleanupIDs
                               |LPSvgoPluginCleanupNumericValues
                               |LPSvgoPluginMoveElemsAttrsToGroup
                               |LPSvgoPluginMoveGroupAttrsToElems
                               |LPSvgoPluginCollapseGroups
                               |LPSvgoPluginMergePaths
                               |LPSvgoPluginConvertShapetoPath
                               );

当我使用以下内容记录defaultPluginMask的值时:

NSLog(@"maskValue: %li",(long)defaultPluginMask);

结果是-536879137.一旦我从掩码中删除了最后一个掩码添加(LPSvgoPluginConvertShapetoPath),我得到一个正值:1610604511

关键的是,LPSvgoPluginConvertShapetoPath左移31位,这将是32位NSInteger中的最大位位置.但是如果我使用sizeof()来记录defaultPluginMask的大小,它会将其报告为8个字节,这意味着它应该是64位.

我有点(哈!)困惑.我做了一个我没看到的基本错误吗?

解决方法

对于长数据类型,C标准规定最小范围需要从-2147483648到2147483647,即使用32位,包括符号位.

即使编译64位平台,也不能保证长变量将在64位上表示.为确保它具有最小64位存储空间,应使用long long.正如@Olaf建议的那样,为了精确指定存储大小,可以使用stdint.h类型(例如int64_t).

但是上面的观察结果似乎并不是你问题的根源(如果NSInteger和长大小是> = 8字节).

问题是要移位的值1是一个整数字,它适合32位并且将被存储(如果它不适合32位,例如5亿,它将存储在64位).并且1 <<< 31将是一个带符号的32位整数,其中1位于符号位置,因此为负数.如果稍后将其分配给64位有符号整数变量,则将对符号位进行扩展(二进制补码表示). 这可以从您的案例中观察到的值中看出,它们以64位十六进制表示为:

-536879137  =>  ffffffffdfffdfdf   - when 1 << 31 is present
 1610604511  =>          5fffdfdf   - when 1 << 31 is removed

下面我做了一个小实验,看看在这个“边界”附近会发生什么(应该注意的是,从1 <<< 32<<<<<<<<<<<<<<<<<<<<<在其他平台上可能发生与1 <<< 0<<<<<<<< 1< 1)相同的结果.

printf("%d %d %d %d\n",sizeof(int),sizeof(long),sizeof(long long),sizeof(void*)); 
// Prints: 4 4 8 8,running on an Windows 64-bit,compiled with gcc from MinGW
long long a[9] = {
    1   << 30,//  1073741824         40000000
    1LL << 30,//  1073741824         40000000
    1   << 31,// -2147483648 ffffffff80000000
    1LL << 31,//  2147483648         80000000
    1   << 32,//           0                0
    1LL << 32,//  4294967296        100000000
    1   << 33,//           0                0
    1LL << 33,//  8589934592        200000000
    5000000000,//  5000000000        12a05f200
};
// Prints of this loop are in the comments above
for (int i = 0; i < 9; i++)
    printf("%12lli %16llx\n",a[i],a[i]);

解决方案:如果要在结果不适合该文字的存储大小的操作中直接使用整数文字,则必须以下列后缀为:

> u / U表示未签名(如果需要)
> l / L很长时间
> ll / LL很长一段时间

可以通过将LL后缀应用于这些元素来解决问题中的问题:

LPSvgoPluginConvertShapetoPath              = 1LL << 31,LPSvgoPluginSortAttrs                       = 1LL << 32,LPSvgoPluginTransformsWithOnePath           = 1LL << 33,LPSvgoPluginRemoveDimensions                = 1LL << 34,LPSvgoPluginRemoveAttrs                     = 1LL << 35,LPSvgoPluginAddClassesToSVGElement          = 1LL << 36,LPSvgoPluginRemoveStyleElement              = 1LL << 37

相关文章

我正在用TitaniumDeveloper编写一个应用程序,它允许我使用Ja...
我的问题是当我尝试从UIWebView中调用我的AngularJS应用程序...
我想获取在我的Mac上运行的所有前台应用程序的应用程序图标....
我是一名PHP开发人员,我使用MVC模式和面向对象的代码.我真的...
OSX中的SetTimer在Windows中是否有任何等效功能?我正在使用...
我不确定引擎盖下到底发生了什么,但这是我的设置,示例代码和...