iOS 开发经验总结http://www.cocoachina.com/ios/20170216/18699.html
1.cocoa pods 常用的framework
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
platform :ios,
'7.0'
target
'store'
do
pod
'AFNetworking'
,
'~> 3.1.0'
pod
'JSONKit'
,
'~> 1.5pre'
pod
'MBProgressHUD'
,
'~> 0.9.2'
pod
'SDWebImage'
,
'~> 3.7.6'
pod
'SVPullToRefresh'
,
'~> 0.4.1'
pod
'JTObjectMapping'
,
'~> 1.1.2'
pod
'MagicalRecord'
,
'~> 2.3.2'
pod
'GetuiSDK'
,
'~> 1.4.2'
pod
'XTSafeCollection'
,
'~> 1.0.4'
pod
'libWeChatSDK'
,
'~> 1.6.2'
pod
'XMLDictionary'
,
'~> 1.4'
pod
'Masonry'
,
'~> 1.0.0'
pod
'OpenUDID'
,
'~> 1.0.0'
pod
'SAMKeychain'
,
'~> 1.5.0'
pod
'iVersion'
,
'~> 1.11.4'
pod
'iCloudDocumentSync'
,
'~> 7.4.1'
end
|
2.NSInteger打印以及字符串的转换
使用%zd打印NSInteger, %tu打印NSUInteger。
1
2
3
4
5
|
NSInteger integer = 1;
NSLog(@
"first number: %zd"
, integer);
NSUInteger uinteger = 1;
NSLog(@
"second number: %tu"
, uinteger);
|
3.UIScrollView 在iOS7中使用了Autolayout 导致不能滚动
1
2
3
|
- (void)viewDidLayoutSubviews
{
[_mainScrollView setContentSize:CGSizeMake(SCREEN_WIDTH, SCREEN_HEIGHT + 10)];
//增加10个像素让scrollview可上下滑动}
|
添加scrollView的content高度.
4.APP包瘦身
1)删除项目中没有使用的图片。 查找XCode工程中没被使用的图片资源
2)删除项目中没有用到的class 和 xib 及storyboard。分析工程中没有用到class
安装 fui 工具
sudo gem install fui -n /usr/local/bin
fui usage: https://github.com/dblock/fui
到工程目录下,执行 fui find 命令,可以找出所有的没有用到的class文件。
3)分析ipa包,找出大于500K的文件或内容 使用
1
|
find . -type f -size +500000c | xargs ls -lh
|
图形搜索法
5.直接iOS7的连续跳转
1
2
3
4
5
6
7
|
- (void)back
{
[self dismissViewControllerAnimated:YES
completion:^{
if
(self.loginCanceled) { self.loginCanceled(); self.loginCanceled = nil;
}
}];
}
|
其中loginCanceled的实现
1
|
[weakSelf.navigationController popViewControllerAnimated:YES];
|
6.去除Preferred Max 的警告
警告的原因:
-
xib或storyboard中添加了UILabel的控件
-
UILabel的number lines 大于1
-
UILabel的preferred max没有设置值( @property(nonatomic) CGFloat preferredMaxLayoutWidth NS_AVAILABLE_IOS(6_0);)
解决方法:
1)如果是Storyboard的,右键点击警告
警告
2)查看警告的详情
详情
3)复制ID的值在搜索框中进行搜索
搜索
4)点击搜索到的Label,可以找到label
找到Label
5)设置preferred width为0,并选中explicit
设置
6)警告解决
如果警告的是Xib,直接点击警告
xib
就会跳转到对应的UILabel控件,设置preferred width为0,并选中explicit。 警告解决。
7.Cocoa pods更新到1.0 不能用了
更新完cocoa pods后,执行pod install后提示
Errno::ENOTEMPTY - Directory not empty @ dir_s_rmdir - /Users/arthurwang/SVN///*/Pods
[!] Oh no, an error occurred.Search for existing GitHub issues similar to yours:https://github.com/CocoaPods/CocoaPods/search?q=Directory+not+empty+%40+dir_s_rmdir+-+%2FUsers%2Farthurwang%2FSVN%2F59Store%2Fstoreapp-ios%2Fstore%2FPods&type=IssuesIf none exists, create a ticket, with the template displayed above, on:https://github.com/CocoaPods/CocoaPods/issues/newBe sure to first read the contributing guide for details on how to properly submit a ticket:https://github.com/CocoaPods/CocoaPods/blob/master/CONTRIBUTING.md
Don't forget to anonymize any private data!
解决方法:
删除项目中的Pods文件夹,再进行pod install的安装
8.interactivepopgesturerecognizer 使用
设置left bar button后,会导致右滑返回的效果失效,查看完美的设置方案。
同时为了获取到右滑返回的事件,可以执行
[self.navigationController.interactivePopGestureRecognizer addTarget:self action:@selector(back)];
在ViewController中viewDidAppare中添加,在viewWillDisappear中remove。
9.更新Cocoapods上的代码
1)采用私有的Cocoapods管理代码,需要添加
pod repo add HXSpec git@code.59store.com:ios/HXSpecs.git
将私有的Spec管理Git添加到pod的specs中。为了方便更新私有库。
2)在使用Cocoapods管理代码的时候,如果在GitHut上更新了代码,希望在project中执行pod install 来刷新代码,那么需要删除Pods和Podfile.lock文件
然后在执行pod install 来刷新代码
使用pod update进行repo的更新(更新私有库)
3)多次安装pod install 会出现Build Phase里多个Check Pods Manifest.lock的选项,这些选项可以删除。
采用直接将specs文件放到HXSpecs的管理库中,那么需要执行pod update 进行
Updating local specs repositories
不然会报错
4)删除这个目录下的缓存文件,进行第三方库文件的重新获取
/Users/arthurwang/Library/Caches/CocoaPods/Pods
5)进行两个组件相互依赖的时候,会导致报错:
[!] There is a circular dependency between StoreLocation and StoreBase
10. UICollectionView的数据不够,无法滑动——解决方案
tableView的数据无论多少,它的界面默认都是可以滑动的。
和tableView相比,当collectionView的数据较少不够一个屏幕时,它无法滑动。
解决方案:
1
|
_collectionView.alwaysBounceVertical = YES;
|
设置为总能垂直滑动就OK了。
11.MLeaksFinder 检查内存泄露
MLeaksFinder 直接用Pod导入就可以。根据DEBUG这个宏,开启内存泄露的检查,如果发现内存泄露,将断言。 So Good
12.删除String的中文
思路: 采用删除不用需要保留的字符,来实现删除中文的目的
1
2
3
4
5
6
7
|
//数字和字母
#define ALPHANUM @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
//数字
#define NUM @"0123456789"
NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:ALPHANUM] invertedSet];
NSString *filteredStr = [[textStr componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@
""
];
|
另外:UITextField 只能输入数字和字母
1
2
3
4
|
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(textFieldChanged:)
name:UITextFieldTextDidChangeNotification
object:nil];
|
监听name:UITextFieldTextDidChangeNotification 和 - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
一起使用,来实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
- (BOOL)validatePasswordString:(NSString *)resultMStr
{
BOOL result = YES;
switch
(self.mode) {
case
HXSChangePasswordLogin: {
NSString *regex = @
"^[a-zA-Z0-9]+$"
;
NSPredicate *pred = [NSPredicate predicateWithFormat:@
"SELF MATCHES %@"
, regex];
result = [pred evaluateWithObject:resultMStr];
break
;
}
case
HXSChangePasswordPay: {
NSString *regex = @
"^[0-9]+$"
;
NSPredicate *pred = [NSPredicate predicateWithFormat:@
"SELF MATCHES %@"
, regex];
result = [pred evaluateWithObject:resultMStr];
break
;
}
}
return
result;
}
|
13.iOS8 以后用WKWebView替代UIWebView
iOS8 之前使用UIWebView进行html的展示,使用UIWebView存在内存占用过大并不释放的问题。
WKWebView解决了内存占用过大的问题。
14.Xib中UILabel换行
在Xib中输入很长的文字,需要根据编号进行换行时,按“Enter”键无效。
方法:将文字在Text编辑工具编写好,copy到UILabel上。换行就有了
15.workspace — project — targets 讲解
一个工作空间可以包含多个项目,一个项目可以包含多个目标(生成物)。
一个项目中根据运行的targets不同,可以进行不同的编译设置,project是基础父类,targets是子类,targets的设置会覆盖project的设置。
16.绘画虚线, drawRect绘画frame未定的情况
1
2
3
|
[yourView.layer setBorderWidth:5.0];
[yourView.layer setBorderColor:
[[UIColor colorWithPatternImage:[UIImage imageNamed:@
"DotedImage.png"
]] CGColor]];
//just add image name and create image with dashed or doted drawing and add here
|
这里只需要添加QuartzCore/QuartzCore.h框架,像下面一样导入.m文件:
注意:
当使用Autolayout后,绘画虚线是根据frame进行的,那么在
1
2
3
4
|
- (void)drawRect:(CGRect)rect
{
[self drawBorderLayer];
}
|
drawRect方法中,进行绘画。
17.Array 的深复制
指针的复制
dataArray3=[dataArray2 mutableCopy];
知识对dataArray2 进行了深复制,数组中的内容仅仅是指针的复制。
单层深复制
dataArray3=[[NSMutableArray alloc]initWithArray:dataArray2 copyItems:YES];
dataArray2进行本身和内容的深复制。单仅仅是内容的第一层
完全深复制
dataArray3 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:dataArray2]];
不管多少层都可以进行了深复制
18.企业证书下载版本可在APP中直接打开
在APP中有H5的页面,可以直接点击进行其他应用的下载
19.URL中对“#”的处理
1
2
3
4
|
NSURL *url = [NSURL URLWithString:[str stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@
"`%^{}\"[]|\\<> "
].invertedSet]];
NSLog(@
"url is %@"
, url);
|
输出: http://test.fd.com/#/dte%7B%7D%7C%7C%7C?lsfj=12&sdlfj=34&hao=%E4%BD%A0%E5%A5%BD
# 没有被转义
iOS9版本中需要使用
stringByAddingPercentEncodingWithAllowedCharacters替代之前stringByAddingPercentEscapesUsingEncoding。
1
2
3
4
5
6
7
8
9
10
11
12
|
NSURL *url = [NSURL URLWithString:[str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(@
"[url scheme] is %@."
, [url scheme]);
NSLog(@
"[url host] is %@."
, [url host]);
NSLog(@
"[url password] is %@."
, [url password]);
NSLog(@
"[url user] is %@."
, [url user]);
NSLog(@
"[url path] is %@."
, [url path]);
NSLog(@
"[url query] is %@."
, [url query]);
NSLog(@
"[url fragment] is %@."
, [url fragment]);
NSLog(@
"[url resourceSpecifier] is %@."
, [url resourceSpecifier]);
NSLog(@
"[url parameterString] is %@."
, [url parameterString]);
NSLog(@
"[url relativePath] is %@."
, [url relativePath]);
|
输出:
2016-08-09 18:57:52.913 store[20150:672790] [url scheme] is http.
2016-08-09 18:57:52.913 store[20150:672790] [url host] is test.fd.com.
2016-08-09 18:57:52.914 store[20150:672790] [url password] is (null).
2016-08-09 18:57:52.914 store[20150:672790] [url user] is (null).
2016-08-09 18:57:52.914 store[20150:672790] [url path] is /#/dte{}|||.
2016-08-09 18:57:52.914 store[20150:672790] [url query] is lsfj=12&sdlfj=34&hao=%E4%BD%A0%E5%A5%BD.
2016-08-09 18:57:52.914 store[20150:672790] [url fragment] is (null).
2016-08-09 18:57:52.915 store[20150:672790] [url resourceSpecifier] is //test.fd.com/%23/dte%7B%7D%7C%7C%7C?lsfj=12&sdlfj=34&hao=%E4%BD%A0%E5%A5%BD.
2016-08-09 18:57:52.915 store[20150:672790] [url parameterString] is (null).
2016-08-09 18:57:52.915 store[20150:672790] [url relativePath] is /#/dte{}|||.
20.APP 启动页不更新
修改了LaunchScreen.xib 后,模拟器上显示正常,但是真机上没有更新。
原因: 应用缓存
解决:真机上删除APP,重启手机,重新安装APP。
21. httpsdns 使用
申请 account id 下载sdk
查看SDK 文档 初始化,替换域名为IP
https://help.aliyun.com/document_detail/30116.html?spm=5176.product30100.4.28.bLXrJ4
在阿里云 里配置域名
22.打印不出变量的值 都显示nil
当对某个变量进行调试的并打印值时,一直返回nil。但是界面又能显示内容。
原因:说明这个变量是在某个线程中,而调试的不在这个线程里。
23.判断是否为gif/png图片的正确姿势
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
//通过图片Data数据第一个字节 来获取图片扩展名
- (NSString *)contentTypeForImageData:(NSData *)data
{
uint8_t c;
[data getBytes:&c length:1];
switch
(c)
{
case
0xFF:
return
@
"jpeg"
;
case
0x89:
return
@
"png"
;
case
0x47:
return
@
"gif"
;
case
0x49:
case
0x4D:
return
@
"tiff"
;
case
0x52:
if
([data length] < 12) {
return
nil;
}
NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
if
([testString hasPrefix:@
"RIFF"
]
&& [testString hasSuffix:@
"WEBP"
])
{
return
@
"webp"
;
}
return
nil;
}
return
nil;
}
|
其实图片数据的第一个字节是固定的,一种类型的图片第一个字节就是它的标识, 我们来调用一下这个方法:
1
2
3
4
5
6
7
|
//假设这是一个网络获取的URL
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:path]];
//调用获取图片扩展名
NSString *string = [self contentTypeForImageData:data];
//输出结果为 png
NSLog(@
"%@"
,string);
|
24.JSPatch 的convertor 工具使用
左边输入
1
2
3
4
5
6
7
8
|
@implementation HXSAdTableViewCell
+ (CGFloat)getCellHeightWithObject:(HXSStoreAppEntryEntity *)storeAppEntryEntity
{
CGFloat scale = [storeAppEntryEntity.imageHeightIntNum floatValue]/[storeAppEntryEntity.imageWidthIntNum floatValue];
return
([UIScreen mainScreen].bounds.size.width) / 3 * scale + 30;
}
@end
|
右边输出
1
2
3
4
5
6
7
8
|
require(
'UIScreen'
);
defineClass(
'HXSAdTableViewCell'
, {} {
getCellHeightWithObject:
function
(storeAppEntryEntity) {
var
scale = storeAppEntryEntity.imageHeightIntNum().floatValue() / storeAppEntryEntity.imageWidthIntNum().floatValue();
return
(UIScreen.mainScreen().bounds().size().width()) / 3 * scale + 30;
},
});
|
有一个BUG: defineClass('HXSAdTableViewCell', {} { 在{}后面少一个“,”。
正确:
1
2
3
4
5
6
7
8
|
require(
'UIScreen'
);
defineClass(
'HXSAdTableViewCell'
, {}, {
getCellHeightWithObject:
function
(storeAppEntryEntity) {
var
scale = storeAppEntryEntity.imageHeightIntNum() / storeAppEntryEntity.imageWidthIntNum();
return
(UIScreen.mainScreen().bounds().width) / 3 * scale + 30;
},
});
|
25.Navigation bar的left bar button 跳动
1
2
3
4
5
6
7
|
- (void)jumpToViewController:(UIViewController *)vc
{
// show the navigation bar in other vcs
[self.navigationController setNavigationBarHidden:NO];
[self.navigationController pushViewController:vc animated:YES];
}
|
当设置了 self.navigationItem.leftBarButtonItem.imageInsets = self.navigationController.viewControllers.count == 1 ? UIEdgeInsetsZero : UIEdgeInsetsMake(0, -5, 0, 5);
那么进行显示navigation bar的时候,就引起left bar button的跳动。
可以设置navigation bar hidden在push之前来解决。
26.Xcode8 出现ubsystem: com.apple.UIKit, category: HIDEventFiltered。。。的日志
解决办法:【product】-【scheme】-【Edit Scheme】-【Run】-【Argument】-【Environment Variable】添加keyValue【OS_ACTIVITY_MODE disable】可以停止输出打印此日志
27.intrinsic size 同一行有2个可变长度的的UILabel
Content Hugging Priority代表控件拒绝拉伸的优先级。优先级越高,控件会越不容易被拉伸。
而下面的Content Compression Resistance Priority代表控件拒绝压缩内置空间的优先级。优先级越高,控件的内置空间会越不容易被压缩
设置Content Hugging Priority 和 Content Compression Resistance Priority, 可实现当2个UILabel的内容都很长的时候,哪个label进行压缩,哪个Label进行显示完整。
28.pusher 推送测试
https://github.com/noodlewerk/NWPusher/releases/tag/0.7.0
直接进行推送的测试。
29.float的精度解决
将NSNumber转化为CGFloat时,精度失真。
1
2
3
4
5
6
7
8
9
|
NSString *decimalNumberMutiplyWithString(NSString *multiplierValue,NSString *multiplicandValue)
{
NSDecimalNumber *multiplierNumber = [NSDecimalNumber decimalNumberWithString:multiplierValue];
NSDecimalNumber *multiplicandNumber = [NSDecimalNumber decimalNumberWithString:multiplicandValue];
NSDecimalNumber *product = [multiplicandNumber decimalNumberByMultiplyingBy:multiplierNumber];
return
[product stringValue];
}
NSLog(@
"%@"
,decimalNumberMutiplyWithString([NSString stringWithFormat:@
"%f"
,a], [NSString stringWithFormat:@
"%d"
,b]));
//输出结果 999999.99
|
通过计算每一位的数字,再进行string展示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
- (NSString *)convertStringFromFloatNum:(NSNumber *)floatNum
{
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setPositiveFormat:@
"0.00"
];
NSString *tempFloatStr = [numberFormatter stringFromNumber:[NSNumber numberWithDouble:([floatNum floatValue] * 100)]];
// yuan to fen
NSInteger tempInt = [tempFloatStr integerValue];
NSInteger result = tempInt % 100;
if
(0 == result) {
NSString *str = [NSString stringWithFormat:@
"%zd"
, tempInt/100];
return
str;
}
result = tempInt % 10;
if
(0 == result) {
NSString *str = [NSString stringWithFormat:@
"%zd.%zd"
, tempInt/100, (tempInt % 100)/10];
return
str;
}
NSString *str = [NSString stringWithFormat:@
"%zd.%zd%zd"
, tempInt/100, (tempInt % 100)/10, (tempInt % 100)];
return
str;
}
|
30.个推iOS10改动
iOS 10 中 “loc-key”和“body”(我们官网就是“message”)这二个字段的优先级发生了变化,在iOS 10中“body”的优先级大于“loc-key”,在iOS 10以下“loc-key”大于“body”,不推荐使用“body”传递数据
将Message(对应body)中的数据放到payload中,将loc-key的数据放到message(对应body)中,在iOS 10中就不会出现显示代码了
31.iOS10 默认的frame
32.腾讯手机管家 骚扰拦截 数据库更新失败
骚扰拦截功能,进行数据库更新时,如果是英文环境,一直会出现数据库更新失败,切换到中文环境,一次性就成功了。