之前公司的项目知识点记录

1.Http访问,创建CXHttpManager继承AFHttpSessionManager

- (void)sendHTTPGetRequest:(NSString *)URLString params:(NSDictionary *)params resultBlock:(CXHTTPManagerResultBlock)resultBlock;

- (void)sendHTTPPostRequest:(NSString *)URLString params:(NSDictionary *)params resultBlock:(CXHTTPManagerResultBlock)resultBlock;

- (void)sendHTTPPutRequest:(NSString *)URLString params:(NSDictionary *)params resultBlock:(CXHTTPManagerResultBlock)resultBlock;

- (void)sendHTTPDeleteRequest:(NSString *)URLString params:(NSDictionary *)params resultBlock:(CXHTTPManagerResultBlock)resultBlock;

然后创建CXNetworkRequestManager,持有CXHttpManager作为属性,对应不同的模块就是创建CXNetworkRequestManager的category,不同模块类似订单Order,资源Resource,一个模块一个category,在其中加对应的接口,调用CXHttpManger来实现请求,其中CXNetworkRequestManager中还持有Reachability对象监听网络情况,以及NetworkStatus获取不同状态码

最后创建NSObject的分类CXNetworkRequest,其中持有CXNetworkRequestManager(这里要用objc_setAssociatedObject来关联CXNetworkRequestManager对象),就可以在ViewModel中,直接调用对应的CXNetworkRequestManager对象来调用各种接口了。

2.圆角加载

dispatch_async(dispatch_get_global_queue(0, 0), ^{
        UIImage *resizedImage = [image cx_imageScaleAspectToSize:inRect.size];
        
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(inRect.size.width, inRect.size.height), NO, 0);
        UIBezierPath *circlePath = [self circlePathWithRect:inRect];
        [circlePath addClip];
        
        [resizedImage drawInRect:inRect];
        
        if (borderWidth != 0 && borderColor) {
            [borderColor setStroke];
            circlePath.lineWidth = borderWidth;
            [circlePath stroke];
        }
        
        UIImage *circleImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.image = circleImage;
        });
    });

3.音频下载缓存
1)使用CXRecordDownloadManager管理所有下载文件,以CXRecordDownloadModel方式展示
将一个个音频文件先利用对应的CXRecordDownloadModel来存储信息,currentFileSize,totalFileSize,对应的资源信息,然后本地保存三个数组,分别是保存本地的录音文件数组,下载中的录音文件数组和下载后的录音文件数组,然后使用一个本地文件 沙盒路径/recordDownloadManagerConfigFileName 来保存本地的所有录音数组,再根据文件的currentFileSize和totalFileSize来判断是否下载完成,放置不同的数组。
当用户需要添加下载的时候,先请求对应链接,通过服务器反馈获取文件大小,从而生成RecordDownloadModel对象,然后添加到下载中数组中,同时更新将每个数组的数据(每个数据从model转成NSDictionary)然后写入本地文件(沙盒路径/recordDownloadManagerConfigFileName中),更新本地数据。
删除下载录音的时候,一样,a)先删除对应的文件。b)同时清理对应的数组,c)更新本地文件

2)使用CXRecordDownloadModel实现下载操作

通过

- (void)getFilesizeWithBlock:(FileSizeCompeletion)compeletion{
    //开启新线程获取对应下载URL的response,然后从response的参数中获取文件大小
    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.recordModel.recordLink]];
    request.HTTPMethod = @"HEAD";
    [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
            if(compeletion) {
//                if(connectionError || data == nil || data.length == 0) {
                if(connectionError || data == nil) {
                    CXLog(@"data :%@",data);
                    compeletion(nil);
                }
                compeletion(response);
            }
        }];
    });
}

获取文件大小

持有NSURLConnection来实现断点下载
持有NSFileHandle来写入文件
使用UInt64 downloadFileEnd来保存文件的尾部

断点下载的代码
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.recordModel.recordLink]];
    NSString *value = [NSString stringWithFormat:@"bytes=%lld-%lld", self.currentFileSize, self.downloadFileEnd];
    [request setValue:value forHTTPHeaderField:@"Range"];
    [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
    
    //开始下载(在上面已经配置好了断点下载)
    //切换到其他线程
    _downloadProgramURLConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
    [_downloadProgramURLConnection setDelegateQueue:[[NSOperationQueue alloc] init]];
    [_downloadProgramURLConnection start];
    //        _downloadProgramURLConnection = [NSURLConnection connectionWithRequest:request delegate:self];
    if(completion) {
        completion();
    }
在对应的delegate中,写入文件,更新状态等
#pragma mark - NSURLConnectionDataDelegate

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    //    NSLog(@"开始下载");
    self.isDownloading = true;
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    
    if(self.currentFileSize >= self.totalFileSize) {
        CXLog(@"self.currentFileSize >= self.totalFileSize");
        return;
    }
    self.isDownloading = true;
    
    //移动到文件的尾部
    [self.writeHandle seekToFileOffset:self.currentFileSize];
    //从当前移动的位置(文件尾部)开始写入数据
    [self.writeHandle writeData:data];
    
    //累加长度
    self.currentFileSize += data.length;
    
    
    
    //更新文件大小
    [self.writeHandle truncateFileAtOffset:self.currentFileSize];
    
    CXLog(@"self.currentFileSize = %lld , self.totalSize = %lld",self.currentFileSize,self.totalFileSize);
    if(self.currentFileSize == self.totalFileSize) {
        //下载完成
        CXLog(@"download finished");
        [[NSNotificationCenter defaultCenter] postNotificationName:kZBYRecordDownloadFinishedNotification
                                                            object:nil];
    }
    
    [[CXRecordDownloadManager defaultManager] updateDownloadProgramModel:self];
    
    //回调更新UI
    if([self.delegate respondsToSelector:@selector(downloadSizeDidChange:)]) {
        [self.delegate downloadSizeDidChange:self];
    }
    
    
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    //关闭连接(不再输入数据到文件中)
    [self.writeHandle closeFile];
    self.writeHandle = nil;
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
}

4.启动页动画
UIView 动画,CABasicAnimation,CGAffineTransform,CAKeyframeAnimation,CAAnimationGroup都尝试过,类似网易云音乐那个

5.内存泄漏
用Command+I instrument里面的leak泄露,然后录制,查看leak处,查看相关代码,寻找原因

6.类似MJExtension的CXBaseModel

- (instancetype)initWithResponseObject:(NSDictionary *)responseObject error:(NSError **)error;
- (instancetype)initWithDictionary:(NSDictionary *)dictionary error:(NSError **)error;

+ (void)load {
    //这里筛选出所有NSInteger的属性,并用runtime为CXBaseModel类添加validate:error:的方法
    unsigned int propertiesCount = 0;
    objc_property_t *properties = class_copyPropertyList([self class], &propertiesCount);
    for (NSInteger i = 0; i < propertiesCount; i++) {
        objc_property_t property = properties[i];
        //属性类型
        NSString *propertyType = @(property_copyAttributeValue(property, "T"));
        
        BOOL isNSIntegerType = (strcmp([propertyType UTF8String], @encode(NSInteger)) == 0);
        BOOL isNSArrayType = (strcmp([propertyType UTF8String], @encode(NSArray)) == 0);
        
        if (!isNSIntegerType || !isNSArrayType) {
            continue;
        }
        //获取属性名字,生成validate:error:的SEL
        NSString *propertyName = @(property_getName(property));
        //capitalizedString
        NSString *validateMethodName = [NSString stringWithFormat:@"validate%@:error:", propertyName.capitalizedString];
        
        if (isNSIntegerType) {
            class_addMethod([self class], NSSelectorFromString(validateMethodName), (IMP)ZBYValidateNSInteger, "B^@^@");
            continue;
        }
        if (isNSArrayType) {
            class_addMethod([self class], NSSelectorFromString(validateMethodName), (IMP)ZBYValidateNSArray, "B^@^@");
        }
    }
}
在load的时候,使用方法添加,添加对应的方法

- (instancetype)initWithResponseObject:(NSDictionary *)responseObject error:(NSError *__autoreleasing *)error {
    _JSONAdapter = [[CXJSONAdapter alloc] initWithModelClass:[self class]];
    NSDictionary *modelDictionary = [_JSONAdapter modelDictionaryWithResponseDictionary:responseObject];
    self = [self initWithDictionary:modelDictionary error:error];
    return self;
}

- (instancetype)initWithDictionary:(NSDictionary *)dictionary error:(NSError **)error {
    self = [self init];
    if (!self) {
#if DEBUG
        NSLog(@"%@ model is nil", _JSONAdapter.modelClass);
#endif
        return nil;
    }
    for (NSString *key in dictionary) {
        // Mark this as being autoreleased, because validateValue may return
        // a new object to be stored in this variable (and we don't want ARC to
        // double-free or leak the old or new values).
        id value = [dictionary objectForKey:key];
        
        if ([value isEqual:[NSNull null]]) {
            value = nil;
        }
        BOOL success = ZBYValidateAndSetValue(self, key, value, YES, error);
        if (!success) {
#if DEBUG
            NSLog(@"%@ model is nil", _JSONAdapter.modelClass);
#endif
            return nil;
        }
    }
    return self;
}

这里有将model专成dictionary
- (NSDictionary *)cx_dictionaryTransformation {
    
    NSMutableDictionary *modelMutableDictionary = [NSMutableDictionary dictionary];
    
    unsigned int propertiesCount = 0;
    objc_property_t *properties = class_copyPropertyList([self class], &propertiesCount);
    for (NSInteger i = 0; i < propertiesCount; i++) {
        objc_property_t property = properties[i];
        NSString *propertyName = @(property_getName(property));
        if([propertyName isEqualToString:@"superclass"] || [propertyName isEqualToString:@"hash"]
           ||[propertyName isEqualToString:@"debugDescription"]
           ||[propertyName isEqualToString:@"description"]) {
            continue;
        }
        id propertyValue = [self valueForKey:propertyName];
        if (!propertyValue) {
            continue;
        }
        //如果是model类则继续转换
        if ([[propertyValue class] isSubclassOfClass:[CXBaseModel class]]) {
            propertyValue = [((CXBaseModel *)propertyValue) cx_dictionaryTransformation];
        }
        //如果是数组类则判断数组元素类型
        if ([propertyValue isKindOfClass:[NSArray class]]) {
            propertyValue = [self arrayTransformWithModelsArray:propertyValue];
        }
        modelMutableDictionary[propertyName] = propertyValue;
    }
    return [modelMutableDictionary copy];
}

实际就是用runtime获取model的一个个属性,然后便利出来获得key和value放进去dictionary,如果对应的value是一个model那么就继续递归,直到全部属性便利完,返回对应的字典即可

7.LimitInputNumberView
原理:在- (void)textViewDidChange:(UITextView *)textView中,获取对应的字符,这里做了一个温馨处理,如果是中文汉字(通过检测键盘输入模式 如果是中文输入),那么不会限制能否继续输入,直到你输入完成,我们再判断中文汉字是否超过字符长度,用一个持有的Bool值来判断,更新这个bool值,然后在

  • (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text 这个接口中来判断用户可否继续敲入。

同时在textViewDidChange可以做剩余字数的UI更新

8.CXTextView
1)直接用一个UILabel 加在UITextView上,通过textViewDidChange来判断UILabel是否要显示
每个属性设置调用setNeedsLayout
2)在drawRect的时候,绘制界面
每个属性设置调用setNeedsDisplay

//- (void)drawRect:(CGRect)rect {
//    if(self.hasText) return;
//    
//    NSMutableDictionary * attrs = [NSMutableDictionary dictionary];
//    attrs[NSFontAttributeName] = self.font;
//    attrs[NSForegroundColorAttributeName] = self.placeholderColor ? self.placeholderColor : [UIColor grayColor];
//    
//    CGFloat x = self.drawRectX;
//    CGFloat w = rect.size.width;
//    CGFloat y = self.drawRectY;
//    CGFloat h = rect.size.height;
//    CGRect placeholderRect = CGRectMake(x, y, w, h);
//    [self.placeholder drawInRect:placeholderRect withAttributes:attrs];
//}

9.CXPhotoBrowser
在MWPhotoBrowser的前提下,封装好,对应的图片浏览器

10.音频播放器
使用AVPlayer来播放音频,使用AVPlayerItem来初始化播放器
2)首先初始化AVPlayer,然后监听AVPlayerItem 的status,如果是可以播放,就获取总时间长度,更新UI,同时通过持有id 类型的currentTimeObserver来更新滑动条的状态,同时设置音频为后台依旧可以播放,就类似于播放器那样,退回首页,还是可以听到音频
3)同时通过kvo监听AVPlayerItem的loadedTimeRanges属性,来更新缓冲条的UI,获取当前缓冲时长,计算缓冲的比例
4)下载成功后会更新Model的下载状态,通过kvo监听,判断如果下载完更新UI,提示用户下载音频完成。

最后就是在离开的时候,关闭timerObserver,avplayer=nil,avplayerItem=nil
实际测试 释放完全,没有大问题

11.视频播放器
其实是在音频的基础上
1)播放器的切换屏幕是
通过transform动画来实现旋转,同时更新约束来实现放大缩小效果。

也是通过kvo监听status来播放视频,loadedTimeRanges来更新缓冲进度,playbackBufferEmpty来判断缓冲的状态,playbackLikelyToKeepUp来判断当前是否在播放视频中
2)视频并没做下载缓存,不过应该和音频缓存一个道理

你可能感兴趣的:(之前公司的项目知识点记录)