美文网首页
IOS基础:设备信息

IOS基础:设备信息

作者: 时光啊混蛋_97boy | 来源:发表于2020-10-21 09:24 被阅读0次

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

    目录

    • 一、设备信息
      • 1、真机运行效果
      • 2、能够获取到的设备信息
      • 3、获取设备信息接口的具体实现
      • 4、UIDevice中对状态信息的监控
    • 二、电量信息
      • 1、真机运行效果
      • 2、能够获取到的电量信息
      • 3、获取电量信息接口的具体实现
    • 三、磁盘内存信息
      • 1、真机运行效果
      • 2、能够获取到的磁盘内存信息
      • 3、获取电量信息接口的具体实现
    • 四、CPU信息
      • 1、真机运行在控制台输出结果
      • 2、能够获取到的CPU信息
      • 3、获取CPU信息接口的具体实现
    • 五、网络信息
      • 1、概念解释
      • 2、真机运行效果
      • 3、能够获取到的网络信息
      • 4、获取网络信息接口的具体实现
      • 5、判断IPv4和IPv6并获取地址
    • 六、判断iOS设备是否安装了特定的app并跳转
      • 1、应用间相互跳转应用场景
      • 2、应用间相互跳转实现原理
      • 3、应用A跳转到应用B
      • 4、跳转到特定界面
      • 5、从应用B跳转回应用A
      • 6、常见Scheme
      • 7、跳转到APP Store更新APP
    • Demo
    • 参考文献

    一、设备信息

    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、应用间相互跳转应用场景

    • 使用第三方用户登录,跳转到需授权的App,还需要返回到调用的程序,同时返回授权的用户名、密码,如QQ登录,微信登录等。
    • 应用程序推广,跳转到另一个应用程序(本机已经安装),或者跳转到APP Store并显示应用程序下载页面(本机没有安装)。
    • 第三方支付,跳转到第三方支付App,如支付宝支付,微信支付。
    • 内容分享,跳转到分享App的对应页面,如分享给微信好友、分享给微信朋友圈、分享到微博。
    • 显示位置、地图导航,跳转到地图应用。
    • 使用系统内置程序,跳转到打电话、发短信、发邮件、Safari打开网页等内置App中。

    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各种设备信息获取总结

    相关文章

      网友评论

          本文标题:IOS基础:设备信息

          本文链接:https://www.haomeiwen.com/subject/qcdsmktx.html