iOS旅途iOS高级开发面试题整理

iOS存储离线数据到本地待有网后自动发布

2018-11-05  本文已影响2人  朝阳小麦

使用人群:iOS开发(OC语言)。
本文内容:手机断网时存储对象到本地,有网时再自动发布。
难点:自定义对象的本地存储,以及UIImage如何存储本地。

借用第三方:
AFNetworkReachabilityManager(pod AFNetworking)判断是否有网络,以及监听网络状况;
YYModel.h(pod YYModel)来帮助对象和json数据互转;
YYCache.h(pod YYCache)来帮助存储数据到本地。

步骤:

1.判断网络状态;
2.无网络,则存储数据到本地;
3.设置网络监听,当有网络时,检测是否有未发布数据,有则发布;
4.每次启动APP,检测是否有未发布数据,有则发布。

详述:

这里,我封装了一个SMDelaySubmitManager类,提供方法:判断网络状态、保存数据到本地、发布数据等方法。代码如下:

SMDelaySubmitManager.h文件代码:

#import <Foundation/Foundation.h>
@interface SMDelaySubmitManager : NSObject
//获取单例
+ (SMDelaySubmitManager *)sharedDelaySubmitManager;
//发布离线数据
+ (void)submitDelayDatas;

//判断当前网络状态
- (BOOL)hasNetwork;
//保存离线数据到本地
- (void)saveDatasWaitingSubmitWithParams:(NSDictionary *)params photos:(NSArray <SMPhotoModel *>*)photos path:(NSString *)path;
//发布离线数据
- (void)submitDatas;
@end

SMDelaySubmitManager.m文件代码:

#import "SMDelaySubmitManager.h"
#import "AFNetworking.h"
#import <YYCache/YYCache.h>
#import "SMPhotoUploadNetworkManager.h"

@interface SMDelaySubmitManager ()
@property (nonatomic, assign) NSInteger delayIndex;
@property (nonatomic, strong) YYDiskCache *diskCache;
@end

@implementation SMDelaySubmitManager
//创建单例
+ (SMDelaySubmitManager *)sharedDelaySubmitManager {
    static SMDelaySubmitManager *delaySubmitManager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (!delaySubmitManager) {
            delaySubmitManager = [[SMDelaySubmitManager alloc] init];
        }
    });
    return delaySubmitManager;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
//初始化diskCache对象
        NSString *pathString = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
        self.diskCache = [[YYDiskCache alloc] initWithPath:pathString inlineThreshold:YYKVStorageTypeMixed];
        
//存储delayIndex字段,为了拼接缓存数据的keyName,一定要存储本地,确保避免名字重复。
        NSUserDefaults *userDef = [NSUserDefaults standardUserDefaults];
        _delayIndex = [userDef integerForKey:@"delayIndex"];
    }
    return self;
}

//判断是否有网络
- (BOOL)hasNetwork {
    AFNetworkReachabilityManager *net = [AFNetworkReachabilityManager sharedManager];
    return net.isReachable;
}

//保存数据到本地。需要接口参数param、接口名interface(path)、图片数据(根据需求,我这里需要传图片到OSS的)。
- (void)saveDatasWaitingSubmitWithParams:(NSDictionary *)params photos:(NSArray <SMPhotoModel *>*)photos path:(NSString *)path {
//注意每次保存都要增加delayIndex,避免keyName重复。
    _delayIndex ++;
    NSUserDefaults *userDef = [NSUserDefaults standardUserDefaults];
    [userDef setInteger:_delayIndex forKey:@"delayIndex"];
    [userDef synchronize];
    
    NSString *paramStr = [params yy_modelToJSONString];
    
    NSMutableArray *tempArr = [NSMutableArray array];
    for (int i=0; i<photos.count; i++) {
        SMPhotoModel *model = photos[i];
        [model.photo turnImageToEncodeString];//把UIImage图片资源转成字符串。
        [tempArr addObject:[model yy_modelToJSONObject]];
    }
    NSString *photoStr = [tempArr yy_modelToJSONString];
    
//把数据拼接成json数据,存储本地,文件存储。这里记得打个点断,把json字符串复制下来,去网上json校验下格式是否正确。
    NSString *keyStr = [NSString stringWithFormat:@"SMDelaySubmitManager_%ld", _delayIndex];
    NSString *json = [NSString stringWithFormat:@"{\"paramStr\":%@,\"photoData\":%@, \"path\":\"%@\"}", paramStr, photoStr, path];
    [self.diskCache setObject:json forKey:keyStr];
    //Toast:"无网络,会在有网络后自动发布离线数据!";
}

- (void)submitDatas {
    // 获取缓存数据
    for (NSInteger i=1; i<=_delayIndex; i++) {
        NSString *keyStr = [NSString stringWithFormat:@"SMDelaySubmitManager_%ld", i];
        NSString *jsonStr = (NSString *)[self.diskCache objectForKey:keyStr];
        if (jsonStr.length > 0) {
            NSData *jsonData = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
            NSError *err;
            NSDictionary *jsonDic = [NSJSONSerialization JSONObjectWithData:jsonData
                                                                options:NSJSONReadingMutableContainers
                                                                  error:&err];
            
            if (!err && jsonDic) {
                NSDictionary *paramJsonDic = [jsonDic objectForKey:@"paramStr"];
                NSString *path = [jsonDic objectForKey:@"path"];
                
                NSArray *photoArr = [jsonDic objectForKey:@"photoData"];
                NSArray *photos = [NSArray yy_modelArrayWithClass:[SMPhotoModel class] json:photoArr];
                for (SMPhotoModel *m in photos) {
                    if (m.photo.imageDataStr.length > 0) {
                        NSData *decodedImageData = [[NSData alloc]initWithBase64EncodedString:m.photo.imageDataStr options:NSDataBase64DecodingIgnoreUnknownCharacters];
                        UIImage *decodedImage = [UIImage imageWithData:decodedImageData];
                        m.photo.image = decodedImage;
                        m.photo.imageDataStr = @"";//清掉该数据,因为数据太庞大,会卡死。
                    }
                }
                
//请求接口,发布数据。根据自己项目的封装,修改。
                [SMPhotoUploadNetworkManager postDataWithUploadFiles:photos?:@[] params:paramJsonDic?:@{} path:path complete:^(AppBaseModel *response) {
                    if (response && response.status) {
                        //Toast:离线数据发布成功!
                        //把离线数据移除(Warning:一定要移除,不然会重复发送数据)
                        [self.diskCache removeObjectForKey:keyStr];
                    }
                }];
            }
            
        }
    }
    
}

+ (void)submitDelayDatas {
    //发布离线缓存数据
    SMDelaySubmitManager *delaySubmitManager = [SMDelaySubmitManager sharedDelaySubmitManager];
    if ([delaySubmitManager hasNetwork]) {
        [delaySubmitManager submitDatas];
    }
}

封装的代码粘贴完成。

解析:

问题1:如何存储UIImage数据到本地?
存储UIImage到本地肯定是很多方法的。这里我选择把UIImage对象,转成string字符串,进行存储。如果不转的话,发布的时候再转回UIImage对象,可能会数据损坏。
UIImage转NSString参考代码如下:

/**
 手机选择的图片文件.
 */
@property (nonatomic, strong) UIImage *image;
@property (nonatomic, strong) NSString *imageDataStr;
- (void)turnImageToEncodeString;//把image图片资源,转为字符串,方便存储本地文件内做缓存。
//把UIImage转成NSString字符串
- (void)turnImageToEncodeString {
    NSData *data = UIImageJPEGRepresentation(_image, 1.0f);
    if (data) {
        NSString *strimage64 = [data tf_base64EncodedString];
        _imageDataStr = strimage64;
    } else {
        _imageDataStr = @"";
    }
}

那么怎么检测数据是否损坏呢?如下:

//如果image对象能正常转成NSData数据,则该图片未损坏。
NSData *imageData = UIImageJPEGRepresentation(model.image, 1.0);
if (imageData) {
    //未损坏
} else {
    //已损坏
}

问题2:对象如何存储?
自定义对象Model需要实现<NSCoding>协议。实现它的两个方法,示例代码如下:

//注意,BOOL和NSInteger等非对象数据,是存储在栈中的,不需要写。

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        self.photo = [_photo initWithCoder:aDecoder];//photo是个其他Model对象
        self.markedPhoto = [_markedPhoto initWithCoder:aDecoder];
        self.imageTime = [aDecoder decodeObjectForKey:@"imageTime"];
        self.userName = [aDecoder decodeObjectForKey:@"userName"];
        self.userId = [aDecoder decodeObjectForKey:@"userId"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [_photo encodeWithCoder:aCoder];
    [_markedPhoto encodeWithCoder:aCoder];
    [aCoder encodeObject:self.imageTime forKey:@"imageTime"];
    [aCoder encodeObject:self.userName forKey:@"userName"];
    [aCoder encodeObject:self.userId forKey:@"userId"];
}

问题3:网络的监听?

#import <AFNetworking/AFNetworking.h>
/**
 检测网络状态
 */
+ (void)startMonitoringNetworkStatus {
    //创建网络监测者
    AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
    /*枚举里面四个状态  分别对应 未知 无网络 数据 WiFi
     typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
     AFNetworkReachabilityStatusUnknown          = -1,      未知
     AFNetworkReachabilityStatusNotReachable     = 0,       无网络
     AFNetworkReachabilityStatusReachableViaWWAN = 1,       蜂窝数据网络
     AFNetworkReachabilityStatusReachableViaWiFi = 2,       WiFi
     };
     */
    [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        switch (status) {
            case AFNetworkReachabilityStatusUnknown:
//                NSLog(@"未知网络状态");
                break;
            case AFNetworkReachabilityStatusNotReachable:
//                NSLog(@"无网络");
                break;
                
            case AFNetworkReachabilityStatusReachableViaWWAN:
//                NSLog(@"蜂窝数据网");
                [SMDelaySubmitManager submitDelayDatas];//发布离线数据
                break;
                
            case AFNetworkReachabilityStatusReachableViaWiFi:
//                NSLog(@"WiFi网络");
                [SMDelaySubmitManager submitDelayDatas];//发布离线数据
                break;
            default:
                break;
        } 
    }] ;
    [manager startMonitoring];
}

有其他问题,欢迎留言。

上一篇 下一篇

猜你喜欢

热点阅读