iOS 项目问题

IOS安装CocoaPods详情过程https://www.tuicool.com/articles/3yAJR3i

iOS Git的使用: https://www.jianshu.com/p/1134abfb40c1

pod 'JXCategoryView'

iOS开发iOS14适配https://www.tuicool.com/articles/BRJBvuR

如何申请接入微信APP支付:https://www.jianshu.com/p/20d33602877c
iOS接入微信支付:https://www.jianshu.com/p/49c7515e96e4

myGitHub:2016llf

Aspect Ratio

Aspect Ratio:设置视图的宽高比
视图宽度随着屏幕宽度变化拉伸时,让其高度自动进行等比例拉伸.保持该视图宽高比不变.
一般先确定宽度(6的尺寸),再设置宽高比利,

Device RGB

iOS色彩空间——xib和代码设置颜色的偏差问题在使用可视化编程时,
在XIB或SB中直接设置颜色,选择RGB Slider设置颜色时,Xcode 中默认勾选的是Generic RGB。
解决方式:选择Device RGB,
//强与弱
__weak typeof(self) weakSelf = self;
[self doSomeBlockJob:^{
    __strong typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf) {
        ...
    }
}];

按钮细节问题

[_cancelBtn setTitleColor:UIColorHex(00ACEB) forState:(UIControlStateNormal)];
[_cancelBtn setTitleColor:UIColorHexAndAlpha(00ACEB, 0.5) forState:(UIControlStateHighlighted)];
[_cancelBtn setBackgroundImage:[BYDImageManager imageWithColor:kColorHighlight] forState:UIControlStateHighlighted];

UIViewController

//UIViewController细节:
self.navigationItem.title = @"XXXX"

等比例缩放图片

//img:为传入的图片;   size:为放置图片区域的大小;  scaledImage:为返回压缩后的图片
+ (UIImage *)scaleToSize:(UIImage *)img size:(CGSize)size{
    // 创建一个bitmap的context
    // 并把它设置成为当前正在使用的context
    UIGraphicsBeginImageContext(size);
    // 绘制改变大小的图片
    [img drawInRect:CGRectMake(0,0, size.width, size.height)];
    // 从当前context中创建一个改变大小后的图片
    UIImage* scaledImage =UIGraphicsGetImageFromCurrentImageContext();
    // 使当前的context出堆栈
    UIGraphicsEndImageContext();
    //返回新的改变大小后的图片
    return scaledImage;
}

内存优化

1,图片的读取采用正确的方式
imageNamed: 会缓存图片,用它读取频繁用到的小图片,最恶心的是不知道它到底什么时候释放。
imageWithContentsOfFile: 不缓存图片,适合一次性读取的大图片。

NSString *path = [[NSBuddle mainBuddle] pathForResource:@"resourceName" oftype@"resourceType"];
UIImage *image = [[UIImage imageWithContentsOfFile:path];

2,imageWithContentsOfFile 、 Assets.xcassets
对于大的图片且偶尔需要显示的应放到工程目录下,不要放到 Assets.xcassets 中;
并使用 imageWithContentsOfFile 加载不让系统缓存
对于经常需要展示的小图片放到 Assets.xcassets 中让系统缓存,使用 imageNamed 加载

一张图片 7kb,在5个地方会用到;resource会占用35kb内存,imageAssets占用7kb
一张图片300kb,在一个地方会用到;resource会在使用后会清除300kb内存,imageAssets会一直占用300kb内存

///
这里顺便提一下 imageNamed: 和 imageWithContentsOfFile: 的区别,这两个 API 都需要解码,并且工作流程都是一致的。
不过imageNamed:会做缓存处理,在下一次用到相同的资源时,就会从缓存里面读取。而 imageWithContentsOfFile: 则不会。

其实,大多数情况下,都是直接使用[UIImage imageNamed:]这种加载本地图片,既然是使用这种方法,
那么图片放在Images.xcassets里面和放在单独文件夹里面区别不是很大,
最大的区别就是Images.xcassets在打包的时候会减小包的体积。

数据库问题

1 数据库频繁操作打开关闭,手动打开关闭锁, 使用一个单例类操作数据库

2 避免同时操作一个资源使用队列串行方式

Appearance方法

我们通过UIAppearance设置一些UI的全局效果,这样就可以很方便的实现UI的自定义效果
又能最简单的实现统一界面风格,但是需要注意使用细则:
**
1、控件遵守了UIAppearance协议,才能对控件进行appearance设置
2、只有被UI_APPEARANCE_SELECTOR这个宏修饰的属性才能使用appearance进行设置,其他属性则不具备该功能
3、appearance设置需要在该控制显示之前设置完成,否则可能无效
**

   if (@available(iOS 11.0, *)){
        [[UIScrollView appearance] setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever];
    }
    
    if (@available(iOS 15.0, *)) {
        [UITableView appearance].sectionHeaderTopPadding = 0;
    }

   // [[UITableView appearance] setEstimatedRowHeight:0];
    [[UITableView appearance] setEstimatedSectionFooterHeight:0];
    [[UITableView appearance] setEstimatedSectionHeaderHeight:0];

NSUserDefaults standardUserDefaults中在最后使用synchronize的作用

系统会保存到该应用下的/Library/Preferences/gongcheng.plist文件中。需要注意的是如果程序意外退出,
NSUserDefaultsstandardUserDefaults数据不会被系统写入到该文件,所以,
要使用[[NSUserDefaultsstandardUserDefaults] synchronize]命令直接同步到文件里,来避免数据的丢失。

///小插曲
- (void)test {
    NSDictionary *dict = @{@1: @"甲",
                           @2: @"乙",
                           @3: @"丙",
                           @4: @"丁"};
    
    [[NSUserDefaults standardUserDefaults] setObject:dict forKey:@"testKey"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}
///报错
2018-04-16 10:55:04.573393+0800 text[1344:58370] [User Defaults] Attempt to set a non-property-list object {
    3 = "\U4e19";
    2 = "\U4e59";
    1 = "\U7532";
    4 = "\U4e01";
} as an NSUserDefaults/CFPreferences value for key testKey

///why
And although NSDictionary and CFDictionary objects allow their keys to be objects of any type,
 if the keys are not string objects, the collections are not property-list objects.
虽然 NSDictionary 和 CFDictionary 对象的 Key 可以为任何类型(只要遵循 NSCopying 协议即可),
 但是如果当 Key 不为字符串 string 对象时,此时这个字典对象就不能算是 property list objects 了,

捕捉异常

    @try
    {
        ///关键代码
        NSString *str = @"abc";
        [str substringFromIndex:111]; // 程序到这里会崩
    }
    
    @catch (NSException *exception)
    {
        ///捕捉异常(弹框提示报错了)
        NSLog(@"%s\n%@", __FUNCTION__, exception);
        NSLog(@"%@", exception.name);
        NSLog(@"%@", exception.reason);
    }
    
    @finally
    {
        ///资源回收完毕!
        NSLog(@"我一定会执行");
    }

多用字面量语法,少用等价方法

// 字面量字符串
NSString *str = @"QiShare";

// 字面量数值
NSNumber *num = @(1);
NSNumber *floatNum = @(1.0);
int x = 5;
float y = 3.14;
NSNumber *num = @(x * y);

// 字面量数组
NSArray *animals = @[@"cat", @"dog", @"tiger", @"monkey"];
NSString *cat = animals[0];

// 字面量字典
NSDictionary *qiShareDic = @{@"englishName": @"QiShare",
                             @"chineseName": @"奇分享"}];
NSString *englishName = qiShareDic[@"englishName"];
NSString *chineseName = qiShareDic[@"chineseName"];

//@property()定义一个成员变量时:1:生成一个带有下滑线的成员变量,2:生成成员变量的set和get方法
trong常使用在可变类型:NSMutableArray、NSMutableDictionary、NSMutableString
copy常使用在不可以变类型:NSArray、NSDictionary、NSString
assign常使用在基本类型:NSInteger、NSUInteger,CGFloat,NSTimeInterval
weak常使用在delegate等防止循环引用的场景

NSString应该使用copy与strong关键字

NSMutableString 不要用 copy https://www.jianshu.com/p/5d138efee024

这里当我们用copy修饰可变类型(NSMutableString、NSMutableArray、NSMutableSet)时,竟然崩溃了。因为当用copy修饰后,NSMutaleArray由可变类型变成了不可变类型,而NSArray并没有增、删元素的方法,所以崩溃。因此,可变类型(NSMutableString、NSMutableArray、NSMutableSet),要用strong修饰。

@property (nonatomic, strong) NSString   *str1;

@property (nonatomic, copy)   NSString   *str2;

    NSMutableString *mtStr = [NSMutableString stringWithFormat:@"hello"];
    
    self.str1 = mtStr;
    
    self.str2 = mtStr;
    
    [mtStr appendString:@"world"];
    
    NSLog(@"mtStr:%p,, str1:%p,, str2:%p",mtStr, self.str1, self.str2);
    // mtStr:0x281641710,, str1:0x281641710,, str2:0xb5284f85e15a82cb
    // mtStr,str1指向同一个对象
    // 而mtStr,str2对象地址不同,是两个不同的对象,所以copy是深复制,创建了一个新的对象。
    
    NSLog(@"str1-------%@",self.str1);
    // str1-------helloworld
    // 经过strong修饰过的str1却随mtStr的改变而改变。
    NSLog(@"str2-------%@",self.str2);
    // str2-------hello
    // 经过copy关键字修饰过的str2并没有因为mtStr的变化而变化,
    

    // 如果不希望字串的值跟着赋值的字串的值变化,所以我们一般用copy.
    // 如果希望字串的值跟着赋值的字串的值变化,可以使用strong。
    
    //上面的情况是针对于当把NSMutableString赋值给NSString的时候,才会有不同,如果是赋值是NSString对象,那么使用copy还是strong,结果都是一样的,因为NSString对象根本就不能改变自身的值,他是不可变的。

    //把一个对象赋值给一个属性变量,当这个对象变化了,如果希望属性变量变化就使用strong属性,如果希望属性变量不跟着变化,就是用copy属性。

static:声明局部变量时,则改变变量的存储方式(生命期),使变量成为静态的局部变量,即编译时就为变量分配内存,直到程序退出才释放存储单元。

//两个概念:生命周期、作用域
//static修饰全局变量,拥有局部变量修饰时候的属性,不同的是该变量只能被本文件访问使用。
static int age = 20;
//
- (void)staticTest{
    // static修饰局部变量,它的生命周期、作用域都是在这个代码块内。
    //1,使得局部变量只初始化一次
    //2,局部变量在程序中只有一份内存,使这个变量拥有记忆功能(可以被赋值).
    //3,局部变量的作用域不变,但是生命周期变了(直到程序结束才销毁)
    static int age = 0;
    age++;
    //所以此处输入的是局部变量的值,在其他部分调用全局age还是20。
    NSLog(@"局部变量的age:%d",age);
}

const:修饰变量,const修饰的变量值是不可变的,(主要强调变量是不可修改的)
const修饰的是其右边的值,也就是const右边的这个整体的值不能改变。

//staic和const常用使用场景,是用来代替宏,把一个经常使用的字符串常量,定义成静态全局只读变量。
static  NSString * const key = @"name";

iOS - Xcode Slicing图片拉伸的图文详解

这里主要讲下Slicing里三条线条的意义和作用


1165585-1df6637f37475749.jpg

也就是当图片拉伸时,用区域A的图片元素去不断复制填充区域B的空间,区域B是被白遮罩层盖的,这区域的图片部分是不可见的,是用于区域A填充的空间。

线1和线2的区间为区域A
线2和线3之间的白遮罩区间为区域B

524F9A1A-894A-4436-8A66-7614766CB6A5.png
WechatIMG2.jpeg
63BB8C58-A176-4289-99C5-14B7CE72FB31.png
A0BB6D64-0639-4BEB-AD18-65974B970F8E.png
1,先设置 slicing 图片拉伸
2,设置UIImageView的contentMode属性为Redraw或者scale To Fill

字符串问题

//减少硬编码
static NSString * const newPushGotoMessageTips = @"前往消息中心";
#define heightItem kAutoSize(64)

//字符串转数组
    NSString *str1 = @"张三, 李四, 王五, 祥子";
    NSArray *array1 = [str1 componentsSeparatedByString:@","];
    NSLog(@"%@", array1);
    (张三, 李四, 王五,祥子,)
//数组转字符串
    NSArray *array2= @[@"哈哈", @"呵呵", @"你好", @"简书"];
    NSString *str2 = [array2 componentsJoinedByString:@","];
    NSLog(@"%@", str2);
    哈哈,呵呵,你好,简书

//检查字符串是否为空 或 NSNumber  并替换
+ (NSString *)checkEmpty:(id)value defaultString:(NSString *)defaultString {
    
    if ([value isKindOfClass:[NSString class]] && ((NSString *)value).length) {
        return value;
    }
    
    if ([value isKindOfClass:[NSNumber class]]) {
        return [value stringValue];
    }
    
    if ([defaultString isKindOfClass:[NSString class]]) {
        return defaultString;
    }
    return @"";
}
//检查字符串是否为空
+ (BOOL)checkEmptyString:(NSString *)string {
    if ([string isKindOfClass:[NSString class]] && string.length) {
        return NO;
    }
    return YES;
}
//检查数组是否为空
+ (BOOL)checkEmptyArray:(NSArray *)array {
    if ([array isKindOfClass:[NSArray class]] && array.count) {
        return NO;
    }
    return YES;
}

//检查字典是否为空
+ (BOOL)checkEmptyDic:(NSDictionary *)dic {
    if ([dic isKindOfClass:[NSDictionary class]] && dic.allValues.count) {
        return NO;
    }
    return YES;
}

字符串根据 , 转数组

if ([model.attr_name length] > 2) {
    NSArray *arrayStr = [model.attr_name componentsSeparatedByString:@","];
    NSMutableString *attriStr = [NSMutableString string];
    for (NSString * str in arrayStr) {
        [attriStr appendString:NSStringFromFormat(@"%@\n", str)];
    }
    [attriStr deleteCharactersInRange:NSMakeRange([attriStr length] - 1, 1)];
    self.subTitleLabel.text = attriStr;
}

UITextField

//限制长度
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textfieldEditChange) name:UITextFieldTextDidChangeNotification object:nil];

- (void)textfieldEditChange{
    if (self.phoneTextFiled.text.length > 11) {
        self.phoneTextFiled.text = [self.phoneTextFiled.text substringToIndex:11];
    }
}

///去掉前后空格
  NSString *searchText = textField.text;
  searchText = [searchText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

UIKeyboard

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];

#pragma mark - keyboardWillShow

- (void)keyboardWillShow:(NSNotification *)notification {
    CGFloat kbHeight = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
    CGFloat offset = kbHeight;
    double duration = [[notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    //将视图上移计算好的偏移
    if(offset > 0) {
        [UIView animateWithDuration:duration animations:^{
            _contenView.top = kScreenHeight - kAutoSize(323) - offset/2;
        }];
    }
}

#pragma mark - keyboardWillHide

- (void)keyboardWillHide:(NSNotification *)notify {
    double duration = [[notify.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    [UIView animateWithDuration:duration animations:^{
        _contenView.top = kScreenHeight - kAutoSize(323);
    }];
}

Masonry

//优先级约束
[self.numberLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.priceLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];

//大于等于10
make.bottom.lessThanOrEqualTo(self).mas_offset(@-10);

//宽高比利1:1
make.width.mas_equalTo(self.leftImageView.mas_height).multipliedBy(1);

//底部安全距离
if (@available(ios 11.0,*)) {
    make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom);
}else{
    make.bottom.equalTo(@0);
}

///
mas_makeConstraints:
一个视图,刚创建出来,没有任何约束。后期也不会重置相关的约束用这个方法。
已有的约束,再添加约束,不修改原有约束
添加新的约束, 则视图会添加这个约束
不会删除已有约束
mas_updateConstraints:
一个视图,刚创建出来,没有任何约束或者已经有约束需要去更新相关约束或者加入约束,但后面不回去重制约束
已有的约束,再添加约束,则更新原有约束
添加新的约束, 则视图会添加这个约束
不会删除已有约束
mas_remakeConstraints
一个视图虽然已经有了约束,但是之前的约束都不要了,需要重新去更新约束。
已有的约束,再添加约束,则更新原有约束
添加新的约束, 则视图会添加这个约束
会删除已有的所有约束

Block

- (void)pushViewControllerSuccess:(UIViewController *)viewController
                         animated:(BOOL)animated
                    successHandle:(void (^)(void))success
                     faliedHandle:(void (^)(void))falied;

数组

//项目开发过程中可能会有这种需求,某个可变数组不断地增加元素,同时
//我们需要判断新的元素是否已经在数组里,如果不在才添加该元素,否则丢弃
//containsObject:是在比较内存地址,即使两个对象内容完全一样,地址不同,那也是不同的。
//我个人认为这个方法应该叫是否存在同一个对象

NSMutableArray *defaultArray = [NSMutableArray array];
if (![defaultArray containsObject:searchText]) {
    [defaultArray insertObject:searchText atIndex:0];
}

//取前面10个
if (defaultArray.count > 10) {
    [defaultArray subarrayWithRange:NSMakeRange(0, 10)]
} 

网络数据处理

//pageSize == 10
if (isFirst) {
    //show转圈圈
}

//成功
if (listArray.count < pageSize) {
    dispatch_async(dispatch_get_main_queue(), ^(){
        [weakSelf.collectionView.mj_footer endRefreshingWithNoMoreData];
    });
}
if (weakSelf.dataPage == 1)
{
    [weakSelf.qualityGoodsModeArray removeAllObjects];
    (listArray.count == 0) ? (notDataView.hidden = NO) : (notDataView.hidden = YES);
}
else if (weakSelf.dataPage > 1 && listArray.count == 0)
{
    weakSelf.dataPage--;
}
[weakSelf.dataArray addObjectsFromArray:listArray];
[weakSelf.collectionView.mj_footer endRefreshing];
weakSelf.collectionView.mj_footer.hidden = NO;
[weakSelf.collectionView reloadData];
    
//失败
weakSelf.dataPage--;
[weakSelf.collectionView.mj_footer endRefreshing];
if (weakSelf.dataArray.count > 0) {
    weakSelf.collectionView.mj_footer.hidden = NO;
    //show吐司(提示无网络,报错)
    return;
}else{
    weakSelf.collectionView.mj_footer.hidden = YES;
}
if (isFirst) {
    if (error.isNetwork) {
        [weakSelf.collectionView statusViewLoadFailed:^{
            [weakSelf requestDatafirst:YES];
        }];
    } else {
        [weakSelf.collectionView statusViewLoadError:^{
            [weakSelf requestDatafirst:YES];
        }];
    }
}

多个请求网络无序
dispatch_group_enter :通知 group,下个任务要放入 group 中执行了
dispatch_group_leave: 通知 group,任务成功完成,要移除,与 enter成对出现
dispatch_group_wait: 在任务组完成时调用,或者任务组超时是调用
dispatch_group_notify: 只要任务全部完成了,就会在最后调用

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
    
    //成功
    dispatch_group_leave(group);
    //失败
    dispatch_group_leave(group);
    
});

//
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
    
    //成功
    dispatch_group_leave(group);
    //失败
    dispatch_group_leave(group);
    
});

//
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
    
    //成功
    dispatch_group_leave(group);
    //失败
    dispatch_group_leave(group);
    
});

//
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    
    //TODO:
    
});

多个网络请求有序
用信号量机制使异步线程完成同步操作

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

dispatch_group_async(group, queue, ^{
    //1
    //成功1(发送一个信号)
    dispatch_semaphore_signal(semaphore);
    //失败1(发送一个信号)
    dispatch_semaphore_signal(semaphore);
    //等待信号
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});

dispatch_group_async(group, queue, ^{
    //2
    //成功2(发送一个信号)
    dispatch_semaphore_signal(semaphore);
    //失败2(发送一个信号)
    dispatch_semaphore_signal(semaphore);
    //等待信号
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});

dispatch_group_async(group, queue, ^{
    //3
    //成功3(发送一个信号)
    dispatch_semaphore_signal(semaphore);
    //失败3(发送一个信号)
    dispatch_semaphore_signal(semaphore);
    //等待信号
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});

//
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    
    //TODO:
    
});

VC返回指定的VC

for (UIViewController *subVC in self.navigationController.viewControllers) {
    if ([subVC isKindOfClass:[BYDAfterSalesGoodsVC class]]) {
        [self.navigationController popToViewController:subVC animated:YES];
        break;
    }
}

URL跳支付宝支付

///白名单
<key>LSApplicationQueriesSchemes</key>
    <array>
        <string>alipay</string>
        <string>alipayshare</string>
    </array>
<key>LSApplicationQueriesSchemes</key>

///
BOOL isSucc = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:resultModel.payinfo]];
if (!isSucc) {
    [UIFactory showToast:@"用户未安装支付宝客户端"];
}else{
    [weakSelf paySuccessEvent];
}

WKWebView加载html字符串

    WKWebView * webView = [[WKWebView alloc]init];
    webView.navigationDelegate = self;
    webView.frame = self.view.bounds;
    [webView loadHTMLString:self.htmlStr baseURL:nil];
    [self.view addSubview:webView];

- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"网页导航加载完毕");
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    //修改字体大小(方法一)
    NSString *fontSize = [NSString stringWithFormat:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%f%%'",kAutoSize(250)];
    [webView evaluateJavaScript:fontSize completionHandler:nil];
}

导航栏按钮问题:大小24*24,距离20

    //
    UIButton * searchBtn = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, kAutoSize(24), kAutoSize(24))];
    [searchBtn setImage:UIImageWithName(@"Top_search_icon") forState:(UIControlStateNormal)];
    [searchBtn addTarget:self action:@selector(touchSearchClick) forControlEvents:(UIControlEventTouchUpInside)];
    UIBarButtonItem * searchBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:searchBtn];
    //
    UIBarButtonItem *fixedSpaceBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
    fixedSpaceBarButtonItem.width = kAutoSize(20);
    //
    UIButton * scanBarBtn = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, kAutoSize(24), kAutoSize(24))];
    [scanBarBtn setImage:UIImageWithName(@"shop_scan_icon") forState:(UIControlStateNormal)];
    [scanBarBtn addTarget:self action:@selector(touchScanVinClick) forControlEvents:(UIControlEventTouchUpInside)];
    UIBarButtonItem * scanBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:scanBarBtn];
    //
    self.navigationItem.rightBarButtonItems = @[scanBarButtonItem,fixedSpaceBarButtonItem,searchBarButtonItem];

getset

@property (nonatomic,strong) BYDSentryVideoInfo *model;

-(void)setModel:(BYDSentryVideoInfo *)model
{
    _model = model;
}

进入后台做耗时操作

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    //阻塞主线程
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for(int i=0;i<10000;i++)
        {
            NSLog(@"i->> %d",i);
        }
        
        //在这里你处理一些事情
    });
    
}

不允许在 dealloc 的时候取 weak self.

- (void)dealloc
{
    [_segmentView removeFromSuperview];
}

项目规范问题:

1,屏幕适配:kAutoSize()
2,字体适配:UIFontSize()
3,按钮高亮状态

[secondBtn setBackgroundImage:[ImageManager imageWithColor:kColorHighlight] forState:UIControlStateHighlighted];

4,tableview:先隐藏tableView,拿到数据,再显示tableView
5,多个网络请求:顺序执行有依赖关系(用信号量一个一个执行有序),并发执行( 加到group里执行玩再处理)
6,无网络状态,加载失败状态,无数据状态。(占位图+文字+按钮)(正在加载...)(加载失败)(上传中...)(网络异常,请检查网络设置)
7,吐司规范:宏定义
8,接口返回错误码规范
9,订单列表:列表没数据转圈圈,有数据不转圈圈,上拉下拉也不转圈圈,
底部(endRefreshingWithNoMoreData)
10,判断是否越狱手机,越狱手机直接提示不支持使用,点击确定退出应用。
11,数据模式跟UI分开,模型里面不能写UI。
12,Model的网络请求用减方法(实例方法,必须通过实例化的对象调用)。
13,第三方的方法不能直接调用,都要二次封装使用。

项目风险安全问题:

1,梆梆安全
2,微信支付
3,支付宝支付
4,上架审核
5,软件著作权
6,埋点

第三方

SwipeTableView 一款好用的,既能上下滚动又能左右滑动的控件SwipeTableView

keyChain是ios中唯一可以存储安全可靠敏感数据的地方。而且应用被卸载,数据也不会被删除,所以非常可靠。苹果官方的操作keychain,比较繁琐和隐晦,这里使用开源的第三方库SAMKeychain

Bugly

优化

1,图片压缩:删除肿的元数据。通过压缩图像节省磁盘空间和带宽,而不会降低质量:https://imageoptim.com/mac
2,后台给业务部门⼀个图⽚上传的标准,业务部门上传的图⽚处理成标准范围内再上传(20-100kb)
3,列表页显示缩略图,详情页使用原始图
4,项目管理从svn(管理方便,适合开发人数不多的项目开发;但服务器压力太大,数据库容量暴增。 如果不能连接到服务器上,基本上不可以工作。)转成git(适合分布式开发,强调个体)
5,iOS路由(MGJRouter) 跳转

CMMI认证

CMMI全称是Capability Maturity Model Integration, 即软件能力成熟度模型集成,是由美国国防部与卡内基-梅隆大学和美国国防工业协会共同开发和研制的,其目的是帮助软件企业对软件工程过程进行管理和改进,增强开发与改进能力,从而能按时地、不超预算地开发出高质量的软件。其所依据的想法是:只要集中精力持续努力去建立有效的软件工程过程的基础结构,不断进行管理的实践和过程的改进,就可以克服软件开发中的困难。CMMI为改进一个组织的各种过程提供了一个单一的集成化框架,新的集成模型框架消除了各个模型的不一致性,减少了模型间的重复,增加透明度和理解,建立了一个自动的、可扩展的框架。因而能够从总体上改进组织的质量和效率。CMMI主要关注点就是成本效益、明确重点、过程集中和灵活性四个方面。

自测的问题

1,iOS,Android的实现,两端同事多沟通,保证逻辑,交互,界面统一。
2,中文文字带上中文的标点符号。
3,每周二代码评审
4,手机号判断:只弹出数字键盘,判断(长度11位)&&(首位是1)
5,下单按钮防重点
6,手机app性能指标记录
7,收集手机app有关隐私权限,以及提示语

项目中遇到的崩溃

//奔溃提示:

**[__NSArrayM info]: unrecognized selector sent to instance 0x280e2bb10**

//原因:解析后台返回的数组,数组里面包含有两个模型。第一个为空,调用第一个模型发生崩溃           

XXXModel *model = listArray[0];

model.info

//解决:判断对象是否是某个类型或某个类型子类的对象

XXXModel *model = listArray[0];

if ([XXXModel isKindOfClass:[XXXModel class]]) {

model.info

}

//总结:造成unrecognized selector sent to instance XXX,
//大部分情况下是因为对象被提前release了,在你心里不希望他release的情况下,指针还在,对象已经不在了。

后期项目管理

1,每次发版本前都会走查bugly,解决崩溃问题。
2,每次新功能开发完都会代码走查。
3,每次发版本前都会进行发布评审:

  • bug修复与验证
  • 强关功能
  • SDK更新
  • 机型适配
  • 注意事项,加固,代码混淆,关闭Log日志,关闭新特性,版本备份,项目配置
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • 作为一个前端程序猿,下面这些站会让你眼前一亮。 amazeui框架组建丰富 http://amazeui.org...
    欧巴冰冰阅读 8,805评论 18 303
  • 在自己折腾ionic2项目时,总会遇到下载不顺畅,卡在某个地方半天的情况。有人说要翻墙,这个的确没说错,但是有时翻...
    wan__wan阅读 1,660评论 0 7
  • 独离故土三月余,怆然神伤思往昔。风吹三径渐荒芜,泪添游子愁葱葱。华夏纵横 百万里,羁客只可旅他屋?愿化此身为浮土,...
    这些年无处安放的青春阅读 215评论 0 0
  • 同学聚会上,x是最后一个到的,推开门时,同学们都兴致正高,突然的推开门使得房间里出现片刻的寂静,时候重新变得喧嚣。...
    袋鼠带分数阅读 213评论 0 0
  • 飞翔是自由的 让心如鸟儿一样 自由飞翔
    随缘153阅读 217评论 0 0