IOS基础:设备信息

2020-10-21  本文已影响0人  时光啊混蛋_97boy

原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容

目录

一、设备信息

1、真机运行效果

a、控制台输出结果
2020-09-07 15:25:22.604649+0800 DeviceInfoDemo[4543:775271] infoKey:device_model---infoValue:iPhone11,8
2020-09-07 15:25:22.605227+0800 DeviceInfoDemo[4543:775271] infoKey:localizedModel---infoValue:iPhone
2020-09-07 15:25:22.605661+0800 DeviceInfoDemo[4543:775271] infoKey:设备型号---infoValue:iPhone XR
2020-09-07 15:25:22.608200+0800 DeviceInfoDemo[4543:775271] infoKey:设备名称---infoValue:不才
2020-09-07 15:25:22.608756+0800 DeviceInfoDemo[4543:775271] infoKey:设备颜色(Private API)---infoValue:1
2020-09-07 15:25:22.609098+0800 DeviceInfoDemo[4543:775271] infoKey:设备外壳颜色(Private API)---infoValue:2
2020-09-07 15:25:22.609425+0800 DeviceInfoDemo[4543:775271] infoKey:app版本号---infoValue:1.0
2020-09-07 15:25:22.609687+0800 DeviceInfoDemo[4543:775271] infoKey:当前系统名称---infoValue:iOS
2020-09-07 15:25:22.609970+0800 DeviceInfoDemo[4543:775271] infoKey:当前系统版本号---infoValue:13.5.1
2020-09-07 15:25:22.610195+0800 DeviceInfoDemo[4543:775271] infoKey:设备支持最低系统版本---infoValue:12.0
2020-09-07 15:25:22.610417+0800 DeviceInfoDemo[4543:775271] infoKey:设备支持的最高系统版本---infoValue:(null)
2020-09-07 15:25:22.618473+0800 DeviceInfoDemo[4543:775271] infoKey:能否打电话---infoValue:能
2020-09-07 15:25:22.618668+0800 DeviceInfoDemo[4543:775271] infoKey:设备上次重启的时间---infoValue:Sun Sep  6 15:33:23 2020
2020-09-07 15:25:22.618792+0800 DeviceInfoDemo[4543:775271] infoKey:当前设备的总线频率---infoValue:0
2020-09-07 15:25:22.618895+0800 DeviceInfoDemo[4543:775271] infoKey:当前设备的主存大小---infoValue:0
b、APP显示的信息
Hardware

2、能够获取到的设备信息

a、工具类提供的获取设备信息的接口
/** 获取用户的本地化信息:货币类型,国家,语言,数字,日期格式的格式化 */
- (void)localInfo;
/** 获取设备型号 */
- (const NSString *)getDeviceName;
/** 获取设备颜色 */
- (NSString *)getDeviceColor;
/** 获取设备外壳颜色 */
- (NSString *)getDeviceEnclosureColor;
/** 获取设备Model */
- (NSString *)getDeviceModel;
/** 获取设备装机时的系统版本(最低支持版本) */
- (const NSString *)getInitialFirmware;
/** 获取设备可支持的最高系统版本 */
- (const NSString *)getLatestFirmware;
/** 获取设备上次重启的时间 */
- (NSDate *)getSystemUptime;
/** 获取总线程频率 */
- (NSUInteger)getBusFrequency;
/** 获取当前设备主存 */
- (NSUInteger)getRamSize;
b、调用接口获取信息
- (void)setupHardwareInfo
{
    // 获取用户的本地化信息:货币类型,国家,语言,数字,日期格式的格式化
    [[DeviceInfoManager sharedManager] localInfo];

    // 获取DeviceModel
    NSString *device_model = [[DeviceInfoManager sharedManager] getDeviceModel];
    [self addInfoWithKey:@"device_model" infoValue:device_model];
    
    // 获取localizedModel
    NSString *localizedModel = [UIDevice currentDevice].localizedModel;
    [self addInfoWithKey:@"localizedModel" infoValue:localizedModel];
    
    // 获取设备型号
    const NSString *deviceName = [[DeviceInfoManager sharedManager] getDeviceName];
    [self addInfoWithKey:@"设备型号" infoValue:[deviceName copy]];
    
    // 获取设备名称
    NSString *iPhoneName = [UIDevice currentDevice].name;
    [self addInfoWithKey:@"设备名称" infoValue:iPhoneName];
    
    // 获取设备颜色
    NSString *deviceColor = [[DeviceInfoManager sharedManager] getDeviceColor];
    [self addInfoWithKey:@"设备颜色(Private API)" infoValue:deviceColor];
    
    // 获取设备外壳颜色
    NSString *deviceEnclosureColor = [[DeviceInfoManager sharedManager] getDeviceEnclosureColor];
    [self addInfoWithKey:@"设备外壳颜色(Private API)" infoValue:deviceEnclosureColor];
    
    // 获取app名称
    NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
    NSLog(@"App应用名称:%@", appName);
    
    // 获取app版本号
    NSString *appVerion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
    [self addInfoWithKey:@"app版本号" infoValue:appVerion];
    
    // 获取app应用Build版本号
    NSString *appBuild = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
    NSLog(@"app应用Build版本号:%@", appBuild);
    
    // 获取当前系统名称
    NSString *systemName = [UIDevice currentDevice].systemName;
    [self addInfoWithKey:@"当前系统名称" infoValue:systemName];
    
    // 当前系统版本号
    NSString *systemVersion = [UIDevice currentDevice].systemVersion;
    [self addInfoWithKey:@"当前系统版本号" infoValue:systemVersion];
    
    // 设备支持最低系统版本
    const NSString *initialFirmware = [[DeviceInfoManager sharedManager] getInitialFirmware];
    [self addInfoWithKey:@"设备支持最低系统版本" infoValue:[initialFirmware copy]];
    
    // 设备支持的最高系统版本
    const NSString *latestFirmware = [[DeviceInfoManager sharedManager] getLatestFirmware];
    [self addInfoWithKey:@"设备支持的最高系统版本" infoValue:[latestFirmware copy]];
    
    // 能否打电话
    BOOL canMakePhoneCall = [DeviceInfoManager sharedManager].canMakePhoneCall;
    [self addInfoWithKey:@"能否打电话" infoValue:@(canMakePhoneCall ? "能" : "不能")];
    
    // 获取设备上次重启的时间
    NSDate *systemUptime = [[DeviceInfoManager sharedManager] getSystemUptime];
    [self addInfoWithKey:@"设备上次重启的时间" infoValue:systemUptime];
    
    // 当前设备的总线频率
    NSUInteger busFrequency = [[DeviceInfoManager sharedManager] getBusFrequency];
    [self addInfoWithKey:@"当前设备的总线频率" infoValue:@(busFrequency)];
    
    // 当前设备的主存大小(随机存取存储器(Random Access Memory))
    NSUInteger ramSize = [[DeviceInfoManager sharedManager] getRamSize];
    [self addInfoWithKey:@"当前设备的主存大小" infoValue:@(ramSize)];
}

3、获取设备信息接口的具体实现

a、需要导入的头文件
// 下面是获取mac地址需要导入的头文件
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
#import <sys/sockio.h>
#import <sys/ioctl.h>
#import <arpa/inet.h>

#import "sys/utsname.h" //获取设备Model
#import <AdSupport/AdSupport.h> //获取广告标识符
#include <ifaddrs.h> //获取ip需要的头文件
#include <mach/mach.h> //获取CPU信息所需要引入的头文件
b、方法的实现
// 获取用户的本地化信息:货币类型,国家,语言,数字,日期格式的格式化
- (void)localInfo
{
    NSArray *languageArray = [NSLocale preferredLanguages];
    NSString *language = [languageArray objectAtIndex:0];
    NSLog(@"语言:%@", language);//en
    
    NSLocale *local = [NSLocale currentLocale];
    NSString *country = [local localeIdentifier];
    NSLog(@"国家:%@", country); //en_US
}

// 获取设备型号
- (const NSString *)getDeviceName
{
    return [[DeviceDataLibrery sharedLibrery] getDiviceName];
}

// 获取设备颜色
- (NSString *)getDeviceColor
{
    return [self getDeviceColorWithKey:@"DeviceColor"];
}

// 获取设备外壳颜色,私有API,上线会被拒
- (NSString *)getDeviceEnclosureColor
{
    return [self getDeviceColorWithKey:@"DeviceEnclosureColor"];
}

// 获取设备Model
- (NSString *)getDeviceModel
{
    struct utsname systemInfo;
    uname(&systemInfo);
    NSString *deviceModel = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
    return deviceModel;
}

// 获取设备装机时的系统版本(最低支持版本)
- (const NSString *)getInitialFirmware
{
    return [[DeviceDataLibrery sharedLibrery] getInitialVersion];
}

// 获取设备可支持的最高系统版本
- (const NSString *)getLatestFirmware
{
    return [[DeviceDataLibrery sharedLibrery] getLatestVersion];
}

// 能否打电话
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
- (BOOL)canMakePhoneCall
{
    __block BOOL can;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        can = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel://"]];
    });
    return can;
}
#endif

// 获取设备上次重启的时间
- (NSDate *)getSystemUptime
{
    NSTimeInterval time = [[NSProcessInfo processInfo] systemUptime];
    return [[NSDate alloc] initWithTimeIntervalSinceNow:(0 - time)];
}

// 获取总线程频率
- (NSUInteger)getBusFrequency
{
    return [self getSystemInfo:HW_BUS_FREQ];
}

// 获取当前设备主存
- (NSUInteger)getRamSize
{
    return [self getSystemInfo:HW_MEMSIZE];
}

#pragma mark - Private Method

// 获取设备颜色,私有API,上线会被拒
- (NSString *)getDeviceColorWithKey:(NSString *)key
{
    UIDevice *device = [UIDevice currentDevice];
    SEL selector = NSSelectorFromString(@"deviceInfoForKey:");
    if (![device respondsToSelector:selector])
    {
        selector = NSSelectorFromString(@"_deviceInfoForKey:");
    }
    
    if ([device respondsToSelector:selector])
    {
        // 消除警告“performSelector may cause a leak because its selector is unknown”
        IMP imp = [device methodForSelector:selector];
        NSString * (*func)(id, SEL, NSString *) = (void *)imp;
        
        return func(device, selector, key);
    }
    return @"unKnown";
}

- (NSUInteger)getSystemInfo:(uint)typeSpecifier
{
    size_t size = sizeof(int);
    int result;
    int mib[2] = {CTL_HW, typeSpecifier};
    sysctl(mib, 2, &result, &size, NULL, 0);
    return (NSUInteger)result;
}

4、UIDevice中对状态信息的监控

a、真机运行在控制台的输出结果
2020-10-28 18:17:33.877164+0800 DeviceInfoDemo[621:101454] 近距离
2020-10-28 18:17:36.834020+0800 DeviceInfoDemo[621:101454] 远距离
2020-10-28 18:17:36.954133+0800 DeviceInfoDemo[621:101454] 设备方向改变
b、添加状态通知:即将某种状态的监控信息添加到通知中心
- (void)registerNotification
{
    // 添加设备方向的监控通知,状态发生变化是就会自动调用对应的方法执行
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeOrientation) name:UIDeviceOrientationDidChangeNotification object:nil];
    
    // 添加距离状态的监控通知
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(changeProximityState) name:UIDeviceProximityStateDidChangeNotification object:nil];
}
c、开启监控开关: 状态通知都对应有一个开关来控制是否开启对应的监控和通知
- (void)openNotification
{
    //打开设备方向监测,这是用方法控制
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];

    //不需要时可以关闭设备方向监控
    [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];

    //打开电池状态和电池电量监测开关,不需要时可以关闭
    [UIDevice currentDevice].batteryMonitoringEnabled=YES;

    //打开手机距离传感器监测开关,不需要时可以关闭
    [UIDevice currentDevice].proximityMonitoringEnabled=YES;
}
d、状态发生变化是就会自动调用对应的方法执行
// 设备方向改变时调用该方法
-(void)changeOrientation
{
    NSLog(@"设备方向改变");
}

// 设备离用户的距离状态发生变化时调用该方法
- (void)changeProximityState
{
    if ([UIDevice currentDevice].proximityState)
    {
        NSLog(@"近距离");
    }
    else
    {
        NSLog(@"远距离");
    }
}

二、电量信息

1、真机运行效果

a、控制台输出结果
2020-09-07 16:07:41.215870+0800 DeviceInfoDemo[4630:791750] 电池状态改变,reload对应的cell,进行更新UI操作
2020-09-07 16:07:41.216055+0800 DeviceInfoDemo[4630:791750] 电池状态改变,reload对应的cell,进行更新UI操作
2020-09-07 16:07:41.216303+0800 DeviceInfoDemo[4630:791750] infoKey:电池电量---infoValue:0.85
2020-09-07 16:07:41.216456+0800 DeviceInfoDemo[4630:791750] infoKey:电池容量---infoValue:2942 mA
2020-09-07 16:07:41.216560+0800 DeviceInfoDemo[4630:791750] infoKey:当前电池剩余电量---infoValue:2500.70 mA
2020-09-07 16:07:41.216729+0800 DeviceInfoDemo[4630:791750] infoKey:电池电压---infoValue:0.00 V
2020-09-07 16:07:41.216871+0800 DeviceInfoDemo[4630:791750] infoKey:电池状态---infoValue:Charging
b、APP显示的信息
电量

2、能够获取到的电量信息

a、工具类提供的获取电量信息的接口
@protocol BatteryInfoDelegate

- (void)batteryStatusUpdated;

@end

@property (nonatomic, weak) id<BatteryInfoDelegate> delegate;

#pragma mark - 电量术语

// 电池容量,单位 mA 毫安
@property (nonatomic, assign) NSUInteger capacity;
// 电池电压,单位 V 福特
@property (nonatomic, assign) CGFloat voltage;
// 电池电量
@property (nonatomic, assign) NSUInteger levelPercent;
// 当前电池剩余电量
@property (nonatomic, assign) NSUInteger levelMAH;
@property (nonatomic, copy)   NSString *status;

#pragma mark - 监测电池电量

/** 开始监测电池电量 */
- (void)startBatteryMonitoring;
/** 停止监测电池电量 */
- (void)stopBatteryMonitoring;
b、调用接口获取信息
- (void)setupBatteryInfo
{
    BatteryInfoManager *batteryManager = [BatteryInfoManager sharedManager];
    batteryManager.delegate = self;
    // 开始监测电池电量
    [batteryManager startBatteryMonitoring];
    
    // 获得电池电量
    CGFloat batteryLevel = [[UIDevice currentDevice] batteryLevel];
    NSString *levelValue = [NSString stringWithFormat:@"%.2f", batteryLevel];
    [self addInfoWithKey:@"电池电量" infoValue:levelValue];
    
    // 获得电池容量
    NSInteger batteryCapacity = batteryManager.capacity;
    NSString *capacityValue = [NSString stringWithFormat:@"%ld mA", batteryCapacity];
    [self addInfoWithKey:@"电池容量" infoValue:capacityValue];
    
    // 获得当前电池剩余电量
    CGFloat batteryMAH = batteryCapacity * batteryLevel;
    NSString *mahValue = [NSString stringWithFormat:@"%.2f mA", batteryMAH];
    [self addInfoWithKey:@"当前电池剩余电量" infoValue:mahValue];
    
    // 获得电池电压
    CGFloat batteryVoltage = batteryManager.voltage;
    NSString *voltageValue = [NSString stringWithFormat:@"%.2f V", batteryVoltage];
    [self addInfoWithKey:@"电池电压" infoValue:voltageValue];
    
    // 获得电池状态
    NSString *batterStatus = batteryManager.status ? : @"unkonwn";
    [self addInfoWithKey:@"电池状态" infoValue:batterStatus];
}

// 当电池状态改变时,会调用该方法,应该在此处reload对应的cell,进行更新UI操作
- (void)batteryStatusUpdated
{
    NSLog(@"电池状态改变,reload对应的cell,进行更新UI操作");
}

3、获取电量信息接口的具体实现

a、监测电池电量
- (void)startBatteryMonitoring
{
    if (!self.batteryMonitoringEnabled)
    {
        self.batteryMonitoringEnabled = YES;
        
        UIDevice *device = [UIDevice currentDevice];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryLevelUpdated:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryStatusUpdated:) name:UIDeviceBatteryStateDidChangeNotification object:nil];
        
        [device setBatteryMonitoringEnabled:YES];
        
        // If by any chance battery value is available - update it immediately
        if ([device batteryState] != UIDeviceBatteryStateUnknown)
        {
            [self doUpdateBatteryStatus];
        }
    }
}
b、电池状态更改
- (void)batteryLevelUpdated:(NSNotification*)notification
{
    [self doUpdateBatteryStatus];
}

- (void)batteryStatusUpdated:(NSNotification*)notification
{
    [self doUpdateBatteryStatus];
}

- (void)doUpdateBatteryStatus
{
    float batteryMultiplier = [[UIDevice currentDevice] batteryLevel];
    self.levelPercent = batteryMultiplier * 100;
    self.levelMAH =  self.capacity * batteryMultiplier;
    
    switch ([[UIDevice currentDevice] batteryState])
    {
        case UIDeviceBatteryStateCharging:// 充电
            if (self.levelPercent == 100)
            {
                self.status = @"Fully charged";
            }
            else
            {
                self.status = @"Charging";
            }
            break;
        case UIDeviceBatteryStateFull:// 满电
            self.status = @"Fully charged";
            break;
        case UIDeviceBatteryStateUnplugged:// 未插入
            self.status = @"Unplugged";
            break;
        case UIDeviceBatteryStateUnknown:// 未知
            self.status = @"Unknown";
            break;
    }
    
    // 状态更改
    [self.delegate batteryStatusUpdated];
}

三、磁盘内存信息

1、真机运行效果

a、控制台输出结果
2020-09-07 18:13:35.435340+0800 DeviceInfoDemo[5009:838778] infoKey:当前 App 所占内存空间---infoValue:320 bytes
2020-09-07 18:13:35.435774+0800 DeviceInfoDemo[5009:838778] infoKey:磁盘总空间---infoValue:== 122060.27 MB == 119.20 GB
2020-09-07 18:13:35.436224+0800 DeviceInfoDemo[5009:838778] infoKey:磁盘已使用空间---infoValue: == 88011.07 MB == 85.95 GB
2020-09-07 18:13:35.436491+0800 DeviceInfoDemo[5009:838778] infoKey:磁盘空闲空间---infoValue: 34049.20 MB == 33.25 GB
2020-09-07 18:13:35.436622+0800 DeviceInfoDemo[5009:838778] infoKey:系统总内存空间---infoValue: 2831.33 MB == 2.76 GB
2020-09-07 18:13:35.436802+0800 DeviceInfoDemo[5009:838778] infoKey:空闲的内存空间---infoValue: 101.25 MB == 0.10 GB
2020-09-07 18:19:32.184034+0800 DeviceInfoDemo[5032:841261] infoKey:已使用的内存空间---infoValue: 2083.86 MB == 2.03 GB
2020-09-07 18:13:35.437295+0800 DeviceInfoDemo[5009:838778] infoKey:活跃的内存---infoValue:正在使用或者很短时间内被使用过 814.98 MB == 0.79 GB
2020-09-07 18:13:35.437410+0800 DeviceInfoDemo[5009:838778] infoKey:最近使用过---infoValue:但是目前处于不活跃状态的内存 725.95 MB == 0.71 GB
2020-09-07 18:13:35.437514+0800 DeviceInfoDemo[5009:838778] infoKey:用来存放内核和数据结构的内存---infoValue:framework、用户级别的应用无法分配 612.47 MB == 0.60 GB
2020-09-07 18:13:35.437619+0800 DeviceInfoDemo[5009:838778] infoKey:可释放的内存空间:内存吃紧自动释放---infoValue:大对象存放所需的大块内存空间 9.88 MB == 0.01 GB
b、APP显示的信息
image.png

2、能够获取到的磁盘内存信息

a、工具类提供的获取磁盘内存信息的接口
#pragma mark - 磁盘

/** 获取本 App 所占磁盘空间 */
- (NSString *)getApplicationSize;
/** 获取磁盘总空间 */
- (int64_t)getTotalDiskSpace;
/** 获取未使用的磁盘空间 */
- (int64_t)getFreeDiskSpace;
/** 获取已使用的磁盘空间 */
- (int64_t)getUsedDiskSpace;

#pragma mark - 内存

/** 获取总内存空间 */
- (int64_t)getTotalMemory;
/** 获取活跃的内存空间 */
- (int64_t)getActiveMemory;
/** 获取不活跃的内存空间 */
- (int64_t)getInActiveMemory;
/** 获取空闲的内存空间 */
- (int64_t)getFreeMemory;
/** 获取正在使用的内存空间 */
- (int64_t)getUsedMemory;
/** 获取存放内核的内存空间 */
- (int64_t)getWiredMemory;
/** 获取可释放的内存空间 */
- (int64_t)getPurgableMemory;
b、调用接口获取信息
- (void)setupDiskInfo
{
    // 获得当前App所占内存空间
    NSString *applicationSize = [[DeviceInfoManager sharedManager] getApplicationSize];
    [self addInfoWithKey:@"当前 App 所占内存空间" infoValue:applicationSize];
    
    // 获得磁盘总空间
    int64_t totalDisk = [[DeviceInfoManager sharedManager] getTotalDiskSpace];
    NSString *totalDiskInfo = [NSString stringWithFormat:@"== %.2f MB == %.2f GB", totalDisk/1024/1024.0, totalDisk/1024/1024/1024.0];
    [self addInfoWithKey:@"磁盘总空间" infoValue:totalDiskInfo];
    
    // 获得磁盘已使用空间
    int64_t usedDisk = [[DeviceInfoManager sharedManager] getUsedDiskSpace];
    NSString *usedDiskInfo = [NSString stringWithFormat:@" == %.2f MB == %.2f GB", usedDisk/1024/1024.0, usedDisk/1024/1024/1024.0];
    [self addInfoWithKey:@"磁盘已使用空间" infoValue:usedDiskInfo];
    
    // 获得磁盘空闲空间
    int64_t freeDisk = [[DeviceInfoManager sharedManager] getFreeDiskSpace];
    NSString *freeDiskInfo = [NSString stringWithFormat:@" %.2f MB == %.2f GB", freeDisk/1024/1024.0, freeDisk/1024/1024/1024.0];
    [self addInfoWithKey:@"磁盘空闲空间" infoValue:freeDiskInfo];
    
    // 系统总内存空间
    int64_t totalMemory = [[DeviceInfoManager sharedManager] getTotalMemory];
    NSString *totalMemoryInfo = [NSString stringWithFormat:@" %.2f MB == %.2f GB", totalMemory/1024/1024.0, totalMemory/1024/1024/1024.0];
    [self addInfoWithKey:@"系统总内存空间" infoValue:totalMemoryInfo];
    
    // 获得空闲的内存空间
    int64_t freeMemory = [[DeviceInfoManager sharedManager] getFreeMemory];
    NSString *freeMemoryInfo = [NSString stringWithFormat:@" %.2f MB == %.2f GB", freeMemory/1024/1024.0, freeMemory/1024/1024/1024.0];
    [self addInfoWithKey:@"空闲的内存空间" infoValue:freeMemoryInfo];
    
    // 获得已使用的内存空间
    int64_t usedMemory = [[DeviceInfoManager sharedManager] getUsedMemory];
    NSString *usedMemoryInfo = [NSString stringWithFormat:@" %.2f MB == %.2f GB", usedMemory/1024/1024.0, usedMemory/1024/1024/1024.0];
    [self addInfoWithKey:@"已使用的内存空间" infoValue:usedMemoryInfo];
    
    // 获得正在使用或者很短时间内被使用过的内存
    int64_t activeMemory = [[DeviceInfoManager sharedManager] getActiveMemory];
    NSString *activeMemoryInfo = [NSString stringWithFormat:@"正在使用或者很短时间内被使用过 %.2f MB == %.2f GB", activeMemory/1024/1024.0, activeMemory/1024/1024/1024.0];
    [self addInfoWithKey:@"活跃的内存" infoValue:activeMemoryInfo];
    
    // 获得最近使用过但是目前处于不活跃状态的内存
    int64_t inActiveMemory = [[DeviceInfoManager sharedManager] getInActiveMemory];
    NSString *inActiveMemoryInfo = [NSString stringWithFormat:@"但是目前处于不活跃状态的内存 %.2f MB == %.2f GB", inActiveMemory/1024/1024.0, inActiveMemory/1024/1024/1024.0];
    [self addInfoWithKey:@"最近使用过" infoValue:inActiveMemoryInfo];
    
    // 获得用来存放内核和数据结构的内存
    int64_t wiredMemory = [[DeviceInfoManager sharedManager] getWiredMemory];
    NSString *wiredMemoryInfo = [NSString stringWithFormat:@"framework、用户级别的应用无法分配 %.2f MB == %.2f GB", wiredMemory/1024/1024.0, wiredMemory/1024/1024/1024.0];
    [self addInfoWithKey:@"用来存放内核和数据结构的内存" infoValue:wiredMemoryInfo];
    
    // 获得大对象存放所需的大块内存空间,内存吃紧自动释放
    int64_t purgableMemory = [[DeviceInfoManager sharedManager] getPurgableMemory];
    NSString *purgableMemoryInfo = [NSString stringWithFormat:@"大对象存放所需的大块内存空间 %.2f MB == %.2f GB", purgableMemory/1024/1024.0, purgableMemory/1024/1024/1024.0];
    [self addInfoWithKey:@"可释放的内存空间:内存吃紧自动释放" infoValue:purgableMemoryInfo];
}

3、获取电量信息接口的具体实现

a、获取磁盘信息
// 获取本 App 所占磁盘空间
- (NSString *)getApplicationSize
{
    unsigned long long documentSize   =  [self getSizeOfFolder:[self getDocumentPath]];
    unsigned long long librarySize   =  [self getSizeOfFolder:[self getLibraryPath]];
    unsigned long long cacheSize =  [self getSizeOfFolder:[self getCachePath]];
    
    unsigned long long total = documentSize + librarySize + cacheSize;
    
    NSString *applicationSize = [NSByteCountFormatter stringFromByteCount:total countStyle:NSByteCountFormatterCountStyleFile];
    return applicationSize;
}

// 获取磁盘总空间
- (int64_t)getTotalDiskSpace
{
    NSError *error = nil;
    NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
    if (error) return -1;
    
    int64_t space =  [[attrs objectForKey:NSFileSystemSize] longLongValue];
    if (space < 0) space = -1;
    return space;
}

// 获取未使用的磁盘空间
- (int64_t)getFreeDiskSpace
{
    NSError *error = nil;
    NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
    if (error) return -1;
    int64_t space =  [[attrs objectForKey:NSFileSystemFreeSize] longLongValue];
    if (space < 0) space = -1;
    return space;
    
}

// 获取已使用的磁盘空间
- (int64_t)getUsedDiskSpace
{
    int64_t totalDisk = [self getTotalDiskSpace];
    int64_t freeDisk = [self getFreeDiskSpace];
    if (totalDisk < 0 || freeDisk < 0) return -1;
    
    int64_t usedDisk = totalDisk - freeDisk;
    if (usedDisk < 0) usedDisk = -1;
    return usedDisk;
}
b、获取内存信息
// 获取总内存空间
- (int64_t)getTotalMemory
{
    int64_t totalMemory = [[NSProcessInfo processInfo] physicalMemory];
    if (totalMemory < -1) totalMemory = -1;
    return totalMemory;
}

// 获取活跃的内存空间
- (int64_t)getActiveMemory
{
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.active_count * page_size;
}

// 获取不活跃的内存空间
- (int64_t)getInActiveMemory
{
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.inactive_count * page_size;
}

// 获取空闲的内存空间
- (int64_t)getFreeMemory
{
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.free_count * page_size;
}

// 获取正在使用的内存空间
- (int64_t)getUsedMemory
{
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return page_size * (vm_stat.active_count + vm_stat.inactive_count + vm_stat.wire_count);
}

// 获取存放内核的内存空间
- (int64_t)getWiredMemory
{
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.wire_count * page_size;
}

// 获取可释放的内存空间
- (int64_t)getPurgableMemory
{
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.purgeable_count * page_size;
}

// DocumentPath
- (NSString *)getDocumentPath
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *basePath = [paths firstObject];
    return basePath;
}

// LibraryPath
- (NSString *)getLibraryPath
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
    NSString *basePath = [paths firstObject];
    return basePath;
}

// CachePath
- (NSString *)getCachePath
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *basePath = [paths firstObject];
    return basePath;
}

// getSizeOfFolder
- (unsigned long long)getSizeOfFolder:(NSString *)folderPath
{
    NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:nil];
    NSEnumerator *contentsEnumurator = [contents objectEnumerator];
    
    NSString *file;
    unsigned long long folderSize = 0;
    
    while (file = [contentsEnumurator nextObject])
    {
        NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:file] error:nil];
        folderSize += [[fileAttributes objectForKey:NSFileSize] intValue];
    }
    return folderSize;
}

四、CPU信息

1、真机运行在控制台输出结果

2020-09-07 17:38:08.723640+0800 DeviceInfoDemo[4720:822749] infoKey:CPU 处理器名称---infoValue:A12 Bionic
2020-09-07 17:38:08.723983+0800 DeviceInfoDemo[4720:822749] infoKey:CPU总数目---infoValue:6
2020-09-07 17:38:08.724229+0800 DeviceInfoDemo[4720:822749] infoKey:CPU使用的总比例---infoValue:1.025270104408264
2020-09-07 17:38:08.724328+0800 DeviceInfoDemo[4720:822749] infoKey:CPU 频率---infoValue:1
2020-09-07 17:38:08.724492+0800 DeviceInfoDemo[4720:822749] infoKey:单个CPU使用比例---infoValue:0.05<-->0.27<-->0.21<-->0.05<-->0.19<-->0.25<-->

2、能够获取到的CPU信息

a、工具类提供的获取磁盘内存信息的接口
/** 获取CPU处理器名称 */
- (NSString *)getCPUProcessor;
/** 获取CPU数量 */
- (NSUInteger)getCPUCount;
/** 获取CPU总的使用百分比 */
- (float)getCPUUsage;
/** 获取单个CPU使用百分比 */
- (NSArray *)getPerCPUUsage;
/** 获取CPU频率 */
- (NSUInteger)getCPUFrequency;
b、调用接口获取信息
- (void)setupCPUInfo
{
    // 获取CPU处理器名称
    NSString *cpuName = [[DeviceInfoManager sharedManager] getCPUProcessor];
    [self addInfoWithKey:@"CPU 处理器名称" infoValue:cpuName];
    
    // 获取CPU总数目
    NSUInteger cpuCount = [[DeviceInfoManager sharedManager] getCPUCount];
    [self addInfoWithKey:@"CPU总数目" infoValue:@(cpuCount)];
    
    // 获取CPU使用的总比例
    CGFloat cpuUsage = [[DeviceInfoManager sharedManager] getCPUUsage];
    [self addInfoWithKey:@"CPU使用的总比例" infoValue:@(cpuUsage)];
    
    // 获取CPU 频率
    NSUInteger cpuFrequency = [[DeviceInfoManager sharedManager] getCPUFrequency];
    [self addInfoWithKey:@"CPU 频率" infoValue:@(cpuFrequency)];
    
    // 获取单个CPU使用比例
    NSArray *perCPUArr = [[DeviceInfoManager sharedManager] getPerCPUUsage];
    NSMutableString *perCPUUsage = [NSMutableString string];
    for (NSNumber *per in perCPUArr)
    {
        [perCPUUsage appendFormat:@"%.2f<-->", per.floatValue];
    }
    [self addInfoWithKey:@"单个CPU使用比例" infoValue:perCPUUsage];
}

3、获取CPU信息接口的具体实现

a、获取CPU处理器名称
- (NSString *)getCPUProcessor
{
    return [[DeviceDataLibrery sharedLibrery] getCPUProcessor] ? : @"unKnown";
}
b、获取CPU数量
- (NSUInteger)getCPUCount
{
    return [NSProcessInfo processInfo].activeProcessorCount;
}
c、获取CPU频率
- (NSUInteger)getCPUFrequency
{
    return [self getSystemInfo:HW_CPU_FREQ];
}
d、获取单个CPU使用百分比
- (NSArray *)getPerCPUUsage
{
    processor_info_array_t _cpuInfo, _prevCPUInfo = nil;
    mach_msg_type_number_t _numCPUInfo, _numPrevCPUInfo = 0;
    unsigned _numCPUs;
    NSLock *_cpuUsageLock;
    
    int _mib[2U] = { CTL_HW, HW_NCPU };
    size_t _sizeOfNumCPUs = sizeof(_numCPUs);
    int _status = sysctl(_mib, 2U, &_numCPUs, &_sizeOfNumCPUs, NULL, 0U);
    if (_status)
        _numCPUs = 1;
    
    _cpuUsageLock = [[NSLock alloc] init];
    
    natural_t _numCPUsU = 0U;
    kern_return_t err = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &_numCPUsU, &_cpuInfo, &_numCPUInfo);
    if (err == KERN_SUCCESS)
    {
        [_cpuUsageLock lock];
        
        NSMutableArray *cpus = [NSMutableArray new];
        for (unsigned i = 0U; i < _numCPUs; ++i)
        {
            Float32 _inUse, _total;
            if (_prevCPUInfo)
            {
                _inUse = (
                          (_cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER]   - _prevCPUInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER])
                          + (_cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM] - _prevCPUInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM])
                          + (_cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE]   - _prevCPUInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE])
                          );
                _total = _inUse + (_cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE] - _prevCPUInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE]);
            }
            else
            {
                _inUse = _cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER] + _cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM] + _cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE];
                _total = _inUse + _cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE];
            }
            [cpus addObject:@(_inUse / _total)];
        }
        
        [_cpuUsageLock unlock];
        if (_prevCPUInfo)
        {
            size_t prevCpuInfoSize = sizeof(integer_t) * _numPrevCPUInfo;
            vm_deallocate(mach_task_self(), (vm_address_t)_prevCPUInfo, prevCpuInfoSize);
        }
        return cpus;
    }
    else
    {
        return nil;
    }
}
e、获取CPU总的使用百分比
- (float)getCPUUsage
{
    float cpu = 0;
    NSArray *cpus = [self getPerCPUUsage];
    if (cpus.count == 0) return -1;
    for (NSNumber *n in cpus)
    {
        cpu += n.floatValue;
    }
    return cpu;
}

五、网络信息

1、概念解释

广告位标识符:在同一个设备上的所有App都会取到相同的值,是苹果专门给各广告提供商用来追踪用户而设的,用户可以在 设置|隐私|广告追踪里重置此id的值,或限制此id的使用,故此id有可能会取不到值,但好在Apple默认是允许追踪的,而且一般用户都不知道有这么个设置,所以基本上用来监测推广效果,是戳戳有余了。

UUID:Universally Unique Identifier的缩写,中文意思是通用唯一识别码。它是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指 定。这样,每个人都可以建立不与其它人冲突的 UUID。在此情况下,就不需考虑数据库建立时的名称重复问题。苹果公司建议使用UUID为应用生成唯一标识字符串。

2、真机运行效果

a、控制台输出结果
2020-09-07 17:03:55.340667+0800 DeviceInfoDemo[4683:810709] infoKey:广告位标识符idfa:---infoValue:5BE8FE64-29FC-40C6-9EB1-36B2B5F65ECB
2020-09-07 17:03:55.341933+0800 DeviceInfoDemo[4683:810709] infoKey:唯一识别码uuid---infoValue:A4997507-9A41-4CFC-A9ED-25FB96B6AE51
2020-09-07 17:03:55.342129+0800 DeviceInfoDemo[4683:810709] infoKey:device_token_crc32真机测试才会显示---infoValue:
2020-09-07 17:03:55.342558+0800 DeviceInfoDemo[4683:810709] infoKey:macAddress---infoValue:020000000000
2020-09-07 17:03:55.342975+0800 DeviceInfoDemo[4683:810709] infoKey:deviceIP---infoValue:169.254.215.144
2020-09-07 17:03:55.343368+0800 DeviceInfoDemo[4683:810709] infoKey:蜂窝地址---infoValue:172.25.39.187
2020-09-07 17:03:55.343775+0800 DeviceInfoDemo[4683:810709] infoKey:WIFI IP地址---infoValue:fe80::14f5:22a1:b6df:3ead
b、APP显示的信息
获取IP地址

3、能够获取到的网络信息

a、工具类提供的获取网络信息的接口
/** 获取广告标识符 */
- (NSString *)getIDFA;
/** 获取mac地址 */
- (NSString *)getMacAddress;
/** 获取ip地址 */
- (NSString *)getDeviceIPAddresses;
/** 获取WIFI IP地址 */
- (NSString *)getIpAddressWIFI;
/** 获取蜂窝地址 */
- (NSString *)getIpAddressCell;
b、调用接口获取信息
- (void)setupAddressInfo
{
    // 获取广告位标识符
    NSString *idfa = [[DeviceInfoManager sharedManager] getIDFA];
    [self addInfoWithKey:@"广告位标识符idfa:" infoValue:idfa];
    
    // 获取UUID
    NSString *uuid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    [self addInfoWithKey:@"唯一识别码uuid" infoValue:uuid];
    
    // 这个 !还存在问题
    NSString *device_token_crc32 = [[NSUserDefaults standardUserDefaults] objectForKey:@"device_token_crc32"] ? : @"";
    [self addInfoWithKey:@"device_token_crc32真机测试才会显示" infoValue:device_token_crc32];

    // 获取mac地址
    NSString *macAddress = [[DeviceInfoManager sharedManager] getMacAddress];
    [self addInfoWithKey:@"macAddress" infoValue:macAddress];
    
    // 获取设备的IP地址
    NSString *deviceIP = [[NetWorkInfoManager sharedManager] getDeviceIPAddresses];
    [self addInfoWithKey:@"deviceIP" infoValue:deviceIP];
    
    // 获取蜂窝地址
    NSString *cellIP = [[NetWorkInfoManager sharedManager] getIpAddressCell];
    [self addInfoWithKey:@"蜂窝地址" infoValue:cellIP];
    
    // 获取WIFI IP地址
    NSString *wifiIP = [[NetWorkInfoManager sharedManager] getIpAddressWIFI];
    [self addInfoWithKey:@"WIFI IP地址" infoValue:wifiIP];
}

4、获取网络信息接口的具体实现

a、需要引入的头文件
#import "NSData+CRC32.h"

// 获取ip需要的头文件
#include <ifaddrs.h>
#include <sys/socket.h> // Per msqr
#import <sys/ioctl.h>
#include <net/if.h>
#import <arpa/inet.h>
b、获取广告标识符
- (NSString *)getIDFA
{
    return [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
}
c、获取mac地址
- (NSString *)getMacAddress
{
    int mib[6];
    size_t len;
    char *buf;
    unsigned char *ptr;
    struct if_msghdr *ifm;
    struct sockaddr_dl *sdl;
    
    mib[0] = CTL_NET;
    mib[1] = AF_ROUTE;
    mib[2] = 0;
    mib[3] = AF_LINK;
    mib[4] = NET_RT_IFLIST;
    
    if ((mib[5] = if_nametoindex("en0")) == 0)
    {
        printf("Error: if_nametoindex error/n");
        return NULL;
    }
    
    if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
    {
        printf("Error: sysctl, take 1/n");
        return NULL;
    }
    
    if ((buf = malloc(len)) == NULL)
    {
        printf("Could not allocate memory. error!/n");
        return NULL;
    }
    
    if (sysctl(mib, 6, buf, &len, NULL, 0) < 0)
    {
        printf("Error: sysctl, take 2");
        return NULL;
    }
    
    ifm = (struct if_msghdr *)buf;
    sdl = (struct sockaddr_dl *)(ifm + 1);
    ptr = (unsigned char *)LLADDR(sdl);
    
    NSString *outstring = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];
    free(buf);
    
    return [outstring uppercaseString];
}
d、获取ip地址
- (NSString *)getDeviceIPAddresses
{
    
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    
    NSMutableArray *ips = [NSMutableArray array];
    
    int BUFFERSIZE = 4096;
    
    struct ifconf ifc;
    
    char buffer[BUFFERSIZE], *ptr, lastname[IFNAMSIZ], *cptr;
    
    struct ifreq *ifr, ifrcopy;
    
    ifc.ifc_len = BUFFERSIZE;
    ifc.ifc_buf = buffer;
    
    if (ioctl(sockfd, SIOCGIFCONF, &ifc) >= 0)
    {
        
        for (ptr = buffer; ptr < buffer + ifc.ifc_len; )
        {
            
            ifr = (struct ifreq *)ptr;
            int len = sizeof(struct sockaddr);
            
            if (ifr->ifr_addr.sa_len > len)
            {
                len = ifr->ifr_addr.sa_len;
            }
            
            ptr += sizeof(ifr->ifr_name) + len;
            if (ifr->ifr_addr.sa_family != AF_INET) continue;
            if ((cptr = (char *)strchr(ifr->ifr_name, ':')) != NULL) *cptr = 0;
            if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) continue;
            
            memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
            ifrcopy = *ifr;
            ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
            
            if ((ifrcopy.ifr_flags & IFF_UP) == 0) continue;
            
            NSString *ip = [NSString  stringWithFormat:@"%s", inet_ntoa(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr)];
            [ips addObject:ip];
        }
    }
    
    close(sockfd);
    NSString *deviceIP = @"";
    
    for (int i=0; i < ips.count; I++)
    {
        if (ips.count > 0)
        {
            deviceIP = [NSString stringWithFormat:@"%@",ips.lastObject];
        }
    }
    return deviceIP;
}
e、获取WIFI IP地址
- (NSString *)getIpAddressWIFI
{
    return [self ipAddressWithIfaName:@"en0"];
}
f、获取蜂窝地址
- (NSString *)getIpAddressCell
{
    return [self ipAddressWithIfaName:@"pdp_ip0"];
}

- (NSString *)ipAddressWithIfaName:(NSString *)name
{
    if (name.length == 0) return nil;
    NSString *address = nil;
    struct ifaddrs *addrs = NULL;
    
    if (getifaddrs(&addrs) == 0)
    {
        struct ifaddrs *addr = addrs;
        while (addr)
        {
            if ([[NSString stringWithUTF8String:addr->ifa_name] isEqualToString:name])
            {
                sa_family_t family = addr->ifa_addr->sa_family;
                switch (family)
                {
                    case AF_INET:// IPv4
                    {
                        char str[INET_ADDRSTRLEN] = {0};
                        inet_ntop(family, &(((struct sockaddr_in *)addr->ifa_addr)->sin_addr), str, sizeof(str));
                        if (strlen(str) > 0)
                        {
                            address = [NSString stringWithUTF8String:str];
                        }
                    } break;
                        
                    case AF_INET6:// IPv6
                    {
                        char str[INET6_ADDRSTRLEN] = {0};
                        inet_ntop(family, &(((struct sockaddr_in6 *)addr->ifa_addr)->sin6_addr), str, sizeof(str));
                        if (strlen(str) > 0)
                        {
                            address = [NSString stringWithUTF8String:str];
                        }
                    }
                    default: break;
                }
                if (address) break;
            }
            addr = addr->ifa_next;
        }
    }
    freeifaddrs(addrs);
    return address ? address : @"该设备不存在该ip地址";
}

5、判断IPv4和IPv6并获取地址

a、需要用到以下文件和宏

// 需要用到以下文件
# import <ifaddrs.h>
# import <arpa/inet.h>
# import <net/if.h>
// 宏
#define IOS_CELLULAR    @"pdp_ip0"
#define IOS_WIFI        @"en0"
#define IOS_VPN         @"utun0"
#define IP_ADDR_IPv4    @"ipv4"
#define IP_ADDR_IPv6    @"ipv6"

b、获取到硬件设备的各个IP

// 获取到硬件设备的各个IP
+(NSDictionary *)getDeviceIPAddresses
{
    NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
    // retrieve the current interfaces - returns 0 on succes
    struct ifaddrs *interfaces;
    if(!getifaddrs(&interfaces))
    {
        // Loop through linked list of interfaces
        struct ifaddrs *interface;
        for(interface = interfaces; interface; interface = interface->ifa_next)
        {
            // deeply nested code harder to read
            if(!(interface->ifa_flags & IFF_UP))
            {
                continue;
            }
            const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
            char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
            if(addr && (addr->sin_family == AF_INET || addr->sin_family == AF_INET6))
            {
                NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
                NSString *type;
                if(addr->sin_family == AF_INET)
                {
                    if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN))
                    {
                        type = IP_ADDR_IPv4;
                    }
                }
                else
                {
                    const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
                    if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN))
                    {
                        type = IP_ADDR_IPv6;
                    }
                }
                if(type)
                {
                    NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
                    addresses[key] = [NSString stringWithUTF8String:addrBuf];
                }
            }
        }
        // Free memory
        freeifaddrs(interfaces);
    }
    return [addresses count] ? addresses : nil;
}

c、把拿到的地址打印一下 ,会看到各种不同的地址,如pdp_ip0/ipv4en0/ipv4en0/ipv6 等。

2020-07-29 14:09:47.782043+0800 Demo[88428:24193761] addresses: {
    "awdl0/ipv6" = "fe80::a8a9:aff:fef2:3196";
    "en0/ipv4" = "10.130.11.159";
    "en0/ipv6" = "fe80::1049:1471:c55e:6a1d";
    "en1/ipv4" = "10.130.168.169";
    "en1/ipv6" = "fe80::c59:8913:4837:6f5e";
    "en5/ipv4" = "169.254.62.42";
    "en5/ipv6" = "fe80::c17:b446:86c9:2269";
    "llw0/ipv6" = "fe80::a8a9:aff:fef2:3196";
    "lo0/ipv4" = "127.0.0.1";
    "lo0/ipv6" = "fe80::1";
    "utun0/ipv6" = "fe80::cfd6:352a:e0a:561d";
    "utun1/ipv6" = "fe80::9b18:e1e0:3e6a:2b25";
    "utun2/ipv6" = "fe80::3660:5e7:7b88:748c";
    "utun3/ipv6" = "fe80::93ac:7e8b:9b5d:1208";
    "utun4/ipv6" = "fe80::ec2a:d9b2:b54:1941";
    "utun5/ipv6" = "fe80::a033:c7cf:1848:d69c";
    "utun6/ipv6" = "fe80::d1bf:ed25:ef85:9880";
    "utun7/ipv6" = "fe80::e82b:d4de:202b:79bb";
    "utun8/ipv6" = "fe80::4043:b856:1f83:3b0e";
    "utun9/ipv6" = "fe80::95b4:12c8:e493:397a";
}

顾名思义这就是不同网络环境下的IPv4IPv6地址,其中我们需要用到这几个:

#define IOS_CELLULAR    @"pdp_ip0" // 蜂窝网络
#define IOS_WIFI        @"en0" // WiFi网络
#define IOS_VPN         @"utun0" // VPN

#define IP_ADDR_IPv4    @"ipv4"
#define IP_ADDR_IPv6    @"ipv6"

其他的当然也有用,比如打开热点之后的也能在里面找到。

d、拿到地址之后判断我们常用的这三个地址中有没有IPv6的可用地址,因为用不同设备多获取几次地址,你就会发现其中有不可用地址,比如IPv4127.0.0.1169.254.120.241IPv6中 以fe80开头的地址。所以 ,如果地址中有可用的IPv6那就说明当前设备的网络环境有支持IPv6,接下来我们就按照这个规则来判断是否支持IPv6

// 判断是否支持IPv6
+(BOOL)isSupportIpv6
{
    NSArray *searchArray =
    @[ IOS_VPN @"/" IP_ADDR_IPv6,
       IOS_VPN @"/" IP_ADDR_IPv4,
       IOS_WIFI @"/" IP_ADDR_IPv6,
       IOS_WIFI @"/" IP_ADDR_IPv4,
       IOS_CELLULAR @"/" IP_ADDR_IPv6,
       IOS_CELLULAR @"/" IP_ADDR_IPv4 ] ;
    
    NSDictionary *addresses = [self getDeviceIPAddresses];
    NSLog(@"addresses: %@", addresses);
    
    __block BOOL isSupportIpv6 = NO;
    [searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) {
        NSLog(@"key: %@---addresses[key]: %@",key, addresses[key] );
        
        if ([key rangeOfString:@"ipv6"].length > 0  && ![[NSString stringWithFormat:@"%@",addresses[key]] hasPrefix:@"(null)"] )
        {
            if (![addresses[key] hasPrefix:@"fe80"])
            {
                isSupportIpv6 = YES;
            }
        }
    }];
    return isSupportIpv6;
}

输出结果为:

2020-07-29 14:17:51.637787+0800 Demo[88453:24198960] key: utun0/ipv6---addresses[key]: fe80::cfd6:352a:e0a:561d
2020-07-29 14:17:51.638453+0800 Demo[88453:24198960] key: utun0/ipv4---addresses[key]: (null)
2020-07-29 14:17:51.638522+0800 Demo[88453:24198960] key: en0/ipv6---addresses[key]: fe80::1049:1471:c55e:6a1d
2020-07-29 14:17:51.638593+0800 Demo[88453:24198960] key: en0/ipv4---addresses[key]: 10.130.11.159
2020-07-29 14:17:51.638657+0800 Demo[88453:24198960] key: pdp_ip0/ipv6---addresses[key]: (null)
2020-07-29 14:17:51.638723+0800 Demo[88453:24198960] key: pdp_ip0/ipv4---addresses[key]: (null)

e、已经判断好IPv6了,然后该取IP地址了,这个时候呢因为有IPv6存在的原因,所以取地址也需要区分一下,下面是取IP的代码:

// 获取IP地址
+ (NSString *)getNetworkIPAddress
{
    NSArray *searchArray = [self isSupportIpv6] ?
    @[ IOS_WIFI @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4 ] :
    @[ IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6 ] ;
    
    NSDictionary *addresses = [self getIPAddresses];
    
    __block NSString *address;
    [searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
     {
        address = addresses[key];
        if([self isValidatIP:address])
        {
            *stop = YES;
        }
    }];
    NSLog(@"IPAddress: %@", address);
    return address ? address : @"0.0.0.0";
}

看到这个地方,肯定有人有疑问了,为啥这个取值的数组searchArray里面还是包含有IPv4IPv6?可以看到IPv4IPv6的顺序是不一样的, 我的理解是这个地方的设备访问肯定是有顺序的,比如我蜂窝网络虽然支持IPv6,但是我现在连上了WiFi,这个WiFiIPv4的,那设备肯定是优先使用WiFi进行网络访问。

如何判断一个字符串是不是IP地址呢?从IPv4的特点入手:由4段数字组成,其中有三个'.'符号,每个数字的范围都在0~255之间,且每个数都是合法的数字(前面没有零)。所以我们可以先把这个字符串用‘.'分隔开,再统计一下'.'的个数,最后判断每个数字是否合法即可。判断IPv6的思路和IPv4类似,拆分并且判断分隔符的个数,依次检查每个数的正确性,区别是IPv6的地址不需要判断前导零,但是需要注意大小写都是合法的。

实现方案一:正则表达式

+ (BOOL)isText:(NSString *)text pattern:(NSString *)pattern
{
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",pattern];
    return [predicate evaluateWithObject:text];
}

/**
 * 判断字符串是否为IP地址
 * param iPAddress IP地址字符串
 * return BOOL 是返回YES,否返回NO
 */
+ (BOOL)isIPAddress:(NSString *)iPAddress
{
    NSString *pattern = @"^(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5]).(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5]).(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5]).(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])$";
    return [self isText:iPAddress pattern:pattern];
}

实现方案二:数字范围

// 判断字符串是否为IP地址
+ (BOOL)isValidIP:(NSString *)ipStr
{
    if (nil == ipStr)
    {
        return NO;
    }
    
    NSArray *ipArray = [ipStr componentsSeparatedByString:@"."];
    if (ipArray.count == 4)
    {
        for (NSString *ipnumberStr in ipArray)
        {
            int ipnumber = [ipnumberStr intValue];
            if (!(ipnumber>=0 && ipnumber<=255))
            {
                return NO;
            }
        }
        return YES;
    }
    return NO;
}

输出结果为:

2020-07-29 14:17:51.640431+0800 Demo[88453:24198960] IPAddress: 10.130.11.159
2020-07-29 14:17:51.640534+0800 Demo[88453:24198960] IP地址为:10.130.11.159

这是在模拟器在不支持IPv6的情况下获得的结果。


六、判断iOS设备是否安装了特定的app并跳转

1、应用间相互跳转应用场景

2、应用间相互跳转实现原理

在iOS中打开一个应用程序只需要拿到这个应用程序的协议头即可,所以我们只需配置应用程序的协议头即可。通过设置跳转到应用B的URL Schemes(自定义的协议头),应用B将其自身“绑定”到一个自定义URL Schemes上,就可以从应用A中利用应用B的URL Schemes启动应用B了。

知道appURL Schemes可以使用openUrl来获取iOS是否安装了某款软件,比如这样[[UIApplication sharedApplication] canOpenURL:@"damon://"],会返回一个bool值,为true则安装了,否则没有安装。

获取app的url schemes 的方法 :把相应的appipa 安装文件下载下来,把文件 .ipa的后缀改成.zip,然后解压,打开 Payload/xxx.app/Info.plist这个文件,找到 URL types下的URL Schemes 下的数组对应的值就是这个 appURL Scheme 了 。

验证一个 URL Scheme 是否正确的方法:在真机设备(此设备要安装了待验证的 app)里面打开 Safari,然后在地址栏中键入该应用的 URL Scheme,后加 ://,比如 Weico 的,在地址栏中键入 weico:// ,然后点击确定,如果能正常调用出 Weico,即代表这个 URL Scheme 正确可用。

3、应用A跳转到应用B

判断某个特定的App是否被安装一般都是用来进行跳转或打开的,如果安装了,我们该如何实现跳转呢?

步骤一:用Xcode创建两个iOS应用程序项目,项目名称分别为App-AApp-B
步骤二:选择项目App-B -> TARGETS -> Info -> URL Types -> URL Schemes,设置App-BURL SchemesAppB

AppB

步骤三:在应用程序App-A中添加一个用来点击跳转的Button,并监听点击事件,添加跳转代码。

- (void)jumpToAppB
{
    // 1.获取应用程序App-B的URL Scheme
    NSURL *appBUrl = [NSURL URLWithString:@"AppB://?AppA"];
    
    // 2.判断手机中是否安装了对应程序
    // iOS10 以后,canOpenURL与openURL合并为一个,可以实现动态跳转不用配置
    if ([[UIApplication sharedApplication] canOpenURL:appBUrl])
    {
        NSLog(@"打开应用程序App-B");
    }
    else
    {
        NSLog(@"没有安装");
    }
    
    // 3.实现动态跳转并且加上配置
    /*
    [[UIApplication sharedApplication] openURL:appBUrl options:@{UIApplicationOpenURLOptionsSourceApplicationKey : @YES} completionHandler:^(BOOL success) {

        if (!success)
        {

            UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"不能完成跳转" message:@"请确认App已经安装" preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"确定"style:UIAlertActionStyleCancel handler:nil];
            [alertController addAction:cancelAction];
            [self  presentViewController:alertController animated:YES completion:nil];
        }
        else if(back)
        {
            [self dismissViewControllerAnimated:YES completion:nil];
        }
    }];
    */
}

步骤四:需要将跳转的UrlScheme添加到白名单即可。添加方法:info.plist 增加LSApplicationQueriesSchemesarray类型),把要跳转的app的UrlScheme加进去。

UrlScheme

运行效果如下:

从A跳转到B

4、跳转到特定界面

很多时候,我们做应用程序之间的跳转并不只是跳转到其他程序就可以了,而是要跳转到其他程序的特定页面上。比如我们在浏览网页时,会有分享到微信朋友圈或是分享给微信朋友,这就需要跳转到微信朋友圈界面或是微信朋友选择界面。具体如何做呢?

步骤一:在APP-B中创建两个界面WriterViewControllerWasteMaterialsViewController
步骤二:在应用程序App-A中添加两个用来点击跳转的Button,一个跳转到WriterViewController,一个跳转到WasteMaterialsViewController,并监听点击事件,添加跳转代码。

- (void)jumpToAppBWriterViewController
{
    // 1.获取应用程序App-B的Page1页面的URL
    NSURL *appBUrl = [NSURL URLWithString:@"AppB://WriterViewController"];

    // 2.判断手机中是否安装了对应程序并跳转
    if ([[UIApplication sharedApplication] canOpenURL:appBUrl])
    {
        [[UIApplication sharedApplication] openURL:appBUrl options:@{UIApplicationOpenURLOptionsSourceApplicationKey : @YES} completionHandler:^(BOOL success) {

            if (!success)
            {

                UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"不能完成跳转" message:@"请确认App已经安装" preferredStyle:UIAlertControllerStyleAlert];
                UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"确定"style:UIAlertActionStyleCancel handler:nil];
                [alertController addAction:cancelAction];
                [self  presentViewController:alertController animated:YES completion:nil];
            }
            else if(self.isBack)
            {
                [self dismissViewControllerAnimated:YES completion:nil];
            }
        }];
    }
    else
    {
        NSLog(@"没有安装");
    }
}

- (void)jumpToAppBWasteMaterialsViewController
{
    // 1.获取应用程序App-B的Page1页面的URL
    NSURL *appBUrl = [NSURL URLWithString:@"AppB://WasteMaterialsViewController"];

    // 2.判断手机中是否安装了对应程序并跳转
    if ([[UIApplication sharedApplication] canOpenURL:appBUrl])
    {
        [[UIApplication sharedApplication] openURL:appBUrl options:@{UIApplicationOpenURLOptionsSourceApplicationKey : @YES} completionHandler:^(BOOL success) {

            if (!success)
            {

                UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"不能完成跳转" message:@"请确认App已经安装" preferredStyle:UIAlertControllerStyleAlert];
                UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"确定"style:UIAlertActionStyleCancel handler:nil];
                [alertController addAction:cancelAction];
                [self  presentViewController:alertController animated:YES completion:nil];
            }
            else if(self.isBack)
            {
                [self dismissViewControllerAnimated:YES completion:nil];
            }
        }];
    }
    else
    {
        NSLog(@"没有安装");
    }
}

步骤三:在应用App-B中通过AppDelegate监听跳转,进行判断,执行不同页面的跳转。

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
    NSString *sourceId = options[@"UIApplicationOpenURLOptionsSourceApplicationKey"];
    NSLog(@"openURL:url=%@;sourceId=%@;options=%@",url,sourceId,options);
    
    // 1.获取导航栏控制器
    UINavigationController *rootNavigationVC = (UINavigationController *)self.window.rootViewController;

    // 2.每次跳转前必须是在根控制器(细节)
    [rootNavigationVC popToRootViewControllerAnimated:NO];

    // 3.根据字符串关键字来跳转到不同页面
    if ([url.absoluteString containsString:@"WriterViewController"]) //跳转到应用App-B的WriterViewController页面
    {
        // 进行跳转
        WriterViewController *vc = [[WriterViewController alloc] init];
        [rootNavigationVC pushViewController:vc animated:YES];
    }
    else if ([url.absoluteString containsString:@"WasteMaterialsViewController"])// 跳转到应用App-B的WasteMaterialsViewController页面
    {
        // 进行跳转
        WasteMaterialsViewController *vc = [[WasteMaterialsViewController alloc] init];
        [rootNavigationVC pushViewController:vc animated:YES];
    }

    return YES;
}

运行效果如下:

跳转到特定界面

5、从应用B跳转回应用A

a、实现方案

我们想要从应用B再跳转回应用A,那么在跳转到应用B的时候,还应将应用A的URL Schemes传递过来,这样我们才能判断应该跳转回哪个应用程序。我们指定一个传递URL的规则:协议头://应用B的URL Schemes?应用A的URL Schemes。即:AppB://WriterViewController?AppAAppB是跳转过来的应用App-BURL SchemesWriterViewController是用来区别跳转页面的标识;? 是分割符;AppA是跳转回的应用App-AURL Schemes

我们根据传递来的数据,进行反跳回去。之前我们在应用App-B中通过AppDelegate执行不同页面的跳转。在对应方法中我们可以拿到完整的URL,在主控制器ViewController中设定一个属性,将该URL保存在主控制器中。在将要跳转的页面控制器中定义一个属性,用于接受、截取出跳转回的应用(即App-A)的URL Schemes,执行跳转。

b、具体步骤

步骤一:因为我们想要跳转回应用A,首先我们要先设置应用App-AURL Schemes,将其设置为AppA。同时在应用App-B中添加白名单。
步骤二:App-B项目中的两个页面各添加一个Button,用于跳转回App-A
步骤三:App-A中修改传递的URL,分别修改为:AppB://WriterViewController?AppAAppB://WasteMaterialsViewController?AppA
步骤四:在将要跳转的页面控制器WasteMaterialsViewControllerWriterViewController中定义一个属性@property (nonatomic, copy) NSString *urlString;,用于接受、截取出跳转回的应用(即App-A)的URL Schemes,执行跳转。
步骤五:App-BAppDelegate中传递URL

// 进行跳转
WriterViewController *vc = [[WriterViewController alloc] init];
// 保存完整的App-A的URL给主控制器
vc.urlString = url.absoluteString;
[rootNavigationVC pushViewController:vc animated:YES];

步骤六:实现跳转回APP-A的方法。

- (void)backToAppA
{
    // 1.拿到对应应用程序的URL Scheme
    NSString *urlSchemeString = [[self.urlString componentsSeparatedByString:@"?"] lastObject];
    NSString *urlString = [urlSchemeString stringByAppendingString:@"://"];
    
    // 2.获取对应应用程序的URL
    NSURL *appAUrl = [NSURL URLWithString:urlString];
    
    // 3.判断是否可以打开
    if (appAUrl && [[UIApplication sharedApplication] canOpenURL:appAUrl])
    {
        [[UIApplication sharedApplication] openURL:appAUrl options:@{UIApplicationOpenURLOptionsSourceApplicationKey : @YES} completionHandler:^(BOOL success) {

            if (!success)
            {

                UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"不能完成跳转" message:@"请确认App已经安装" preferredStyle:UIAlertControllerStyleAlert];
                UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"确定"style:UIAlertActionStyleCancel handler:nil];
                [alertController addAction:cancelAction];
                [self  presentViewController:alertController animated:YES completion:nil];
            }
            else if(self.isBack)
            {
                [self dismissViewControllerAnimated:YES completion:nil];
            }
        }];
    }
    else
    {
        NSLog(@"没有安装");
    }
}

运行效果如下:

跳转回App-A

6、常见Scheme

//调用邮件客户端(Apple Mail)
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"mailto://admin@eyecm.com"]];

//拨号(Phone Number)
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"tel://10086"]];

//调用短信(SMS)
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"sms:10086"]];

//调用浏览器(Safari Browser)
NSURL*url =[NSURL URLWithString:@"http://eyecm.com"];
[[UIApplication sharedApplication] openURL:url];

//调用应用商店(AppStore)
NSURL*appStoreUrl =[NSURL URLWithString:@"http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=291586600&amp;amp;mt=8"];
[[UIApplication sharedApplication] openURL:appStoreUrl];

7、跳转到APP Store更新APP

a、真机运行效果
控制台输出结果
2020-10-29 10:39:19.310354+0800 DeviceInfoDemo[749:64988] 接口返回的APP信息包括:{
    advisories =     (
        "\U9891\U7e41/\U5f3a\U70c8\U7684\U6210\U4eba/\U6027\U6697\U793a\U9898\U6750"
    );
    appletvScreenshotUrls =     (
    );
    artistId = 614694882;
    artistName = WeChat;
    artistViewUrl = "https://apps.apple.com/cn/developer/wechat/id614694882?uo=4";
    averageUserRating = "4.4047900000000002052047420875169336795";
    averageUserRatingForCurrentVersion = "4.4047900000000002052047420875169336795";
    bundleId = "com.tencent.xin";
    contentAdvisoryRating = "17+";
    currency = CNY;
    currentVersionReleaseDate
2020-10-29 10:39:19.311923+0800 DeviceInfoDemo[749:64988] APP的当前版本:1.0,AppStore 上软件的最新版本:7.0.17
2020-10-29 10:39:23.520783+0800 DeviceInfoDemo[749:64988] 新版本更新内容:本次更新:
- 可前往“我 > 设置”开启青少年模式,并设置青少年模式下的访问范围。
- 可以在会话的表情面板搜索表情。

最近更新:
- 可跟随系统的设置,切换为深色模式。
- 优化了语音消息的发送体验,上滑转文字更方便了。
2020-10-29 10:39:31.858534+0800 DeviceInfoDemo[749:64926] AppStore 上APP的地址:https://apps.apple.com/cn/app/%E5%BE%AE%E4%BF%A1/id414478124?uo=4
2020-10-29 10:39:31.882374+0800 DeviceInfoDemo[749:64926] 进入到了APP Store更新APP
APP显示的信息
更新内容的提示 跳转到App Store
b、判断是否最新版本号(大于或等于为最新)
- (BOOL)isLastestVersion:(NSString *)currentVersion compare:(NSString *)lastestVersion
{
    if (currentVersion && lastestVersion)
    {
        // 拆分成数组
        NSMutableArray *currentItems = [[currentVersion componentsSeparatedByString:@"."] mutableCopy];
        NSMutableArray *lastestItems = [[lastestVersion componentsSeparatedByString:@"."] mutableCopy];
        
        // 如果数量不一样补0
        NSInteger currentCount = currentItems.count;
        NSInteger lastestCount = lastestItems.count;
        
        if (currentCount != lastestCount)
        {
            // 取绝对值
            NSInteger count = labs(currentCount - lastestCount);
            for (int i = 0; i < count; ++i)
            {
                if (currentCount > lastestCount)
                {
                    [lastestItems addObject:@"0"];
                }
                else
                {
                    [currentItems addObject:@"0"];
                }
            }
        }
        
        // 依次比较
        BOOL isLastest = YES;
        for (int i = 0; i < currentItems.count; ++i)
        {
            NSString *currentItem = currentItems[i];
            NSString *lastestItem = lastestItems[i];
            if (currentItem.integerValue != lastestItem.integerValue)
            {
                isLastest = currentItem.integerValue > lastestItem.integerValue;
                break;
            }
        }
        return isLastest;
    }
    return NO;
}
c、更新APP
// 跳转到App Store的链接itms-apps://itunes.apple.com/cn/app/id(你的APPID)
// AppID的查询方法为APP Store里面的分享链接 https://apps.apple.com/cn/app/%E7%9F%A5%E4%B9%8E-%E6%9C%89%E9%97%AE%E9%A2%98-%E4%B8%8A%E7%9F%A5%E4%B9%8E/id432274380
- (void)checkVersion
{
    // 你的项目ID
    NSString *storeAppID = @"414478124";// 微信的AppID
    
    // 参数appid指的是你在app在创建后的唯一标识,在iTunes Connect里可以查找到此信息
    NSString *storeAppIDString = [NSString stringWithFormat:@"http://itunes.apple.com/cn/lookup?id=%@",storeAppID];
    NSURL *url = [NSURL URLWithString:storeAppIDString];
    
    // 联网检测项目在AppStore上的版本号
    // 此接口将返回一个JSON格式的字串内容,其中一个就是版本信息,如"version":"1.2.0"
    [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSDictionary * dataDic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
        NSArray *results = dataDic[@"results"];
        if (results && results.count > 0)
        {
            NSDictionary *response = results.firstObject;
            
            NSLog(@"接口返回的APP信息包括:%@",response);
            
            // APP的当前版本
            NSString *currentVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
            // AppStore 上软件的最新版本
            NSString *lastestVersion = response[@"version"];
            
            NSLog(@"APP的当前版本:%@,AppStore 上软件的最新版本:%@",currentVersion,lastestVersion);
            
            if (currentVersion && lastestVersion && ![self isLastestVersion:currentVersion compare:lastestVersion])
            {
                // 新版本更新内容
                NSString *releaseNotes = response[@"releaseNotes"];
                NSString *alertContent = [NSString stringWithFormat:@"%@\n\n是否前往 AppStore 更新版本?",releaseNotes];
                NSLog(@"新版本更新内容:%@",releaseNotes);
                
                // 给出提示是否前往 AppStore 更新
                UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"检测到有版本更新" message:alertContent preferredStyle:UIAlertControllerStyleAlert];
                
                [alert addAction:[UIAlertAction actionWithTitle:@"前往" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
                    
                    // AppStore 上APP的地址
                    NSString *trackViewUrl = response[@"trackViewUrl"];
                    NSLog(@"AppStore 上APP的地址:%@",trackViewUrl);
                    if (trackViewUrl)
                    {
                        NSURL *appStoreURL = [NSURL URLWithString:trackViewUrl];
                        if ([[UIApplication sharedApplication] canOpenURL:appStoreURL])
                        {
                            [[UIApplication sharedApplication] openURL:appStoreURL options:@{} completionHandler:nil];
                            NSLog(@"进入到了APP Store更新APP");
                        }
                    }
                }]];
                [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
                });
            }
        }
    }] resume];
}

Demo

Demo在我的Github上,欢迎下载。
DeviceInfoDemo

参考文献

[iOS]判断IPv4和IPv6并获取地址,以及获取运营商、信号强度
iOSAPP开发项目搭建
iOS应用之间的跳转
史上最全的iOS各种设备信息获取总结

上一篇下一篇

猜你喜欢

热点阅读