iOS开发-开发总结

Posted iOS0116

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发-开发总结相关的知识,希望对你有一定的参考价值。

ARC的使用:
ARC并不能避免所有的内存泄露。使用ARC之后,工程中可能还会有内存泄露,不过引起这些内存泄露的主要原因是:block,retain循环,对CoreFoundation对象(通常是C结构)管理不善,以及真的是代码没写好。

 
reuseIdentifier

ios程序开发中一个普遍性的错误就是没有正确的为UITableViewCells、UICollectionViewCells和UITableViewHeaderFooterViews设置reuseIdentifier。
 
为 了获得最佳性能,当在tableView:cellForRowAtIndexPath:方法中返回cell时,table view的数据源一般会重用UITableViewCell对象。table view维护着UITableViewCell对象的一个队列或者列表,这些数据源已经被标记为重用了。
 
如 果没有使用reuseIdentifier会发生什么?如果你在程序中没有使用reuseIdentifier,table view每次显示一个row时,都会配置一个全新的cell。这其实是一个非常消耗资源的操作,并且会影响程序中table view滚动的效率。
 
自iOS 7以来,你可能还希望header和footer views,以及UICollectionView的cell和supplementary views。
 
为了使用reuseIdentifiers,在table view请求一个新的cell时,在数据源中调用下面的方法:
static NSString *CellIdentifier = @"Cell";  
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; 
如 果table view维护的UITableViewCell队列或列表中有可用的cell,则从队列从移除一个已经存在的cell,如果没有的话,就从之前注册的 nib文件或类中创建一个新的cell。如果没有可以重用的cell,并且没有注册nib文件或类,tableview的 dequeueReusableCellWithIdentifier:方法会返回一个nil。
 
 

 
尽量将view设置为Opaque
 
如果view是不透明的,那么应该将其opaque属性设置为YES。为什么要这样做呢?这样设置可以让系统以最优的方式来绘制view。opaque属性可以在Interface Builder或代码中设置。
 
苹果的官方文档对opaque属性有如下解释:
This property provides a hint to the drawing system as to how it should treat the view. If set to YES, the drawing system treats the view as fully opaque, which allows the drawing system to optimize some drawing operations and improve performance. If set to NO, the drawing system composites the view normally with other content. The default value of this property is YES.
(opaque属性提示绘制系统如何处理view。如果opaque设置为YES,绘图系统会将view看为完全不透明,这样绘图系统就可以优化一些绘制操作以提升性能。如果设置为NO,那么绘图系统结合其它内容来处理view。默认情况下,这个属性是YES。)
 
如 果屏幕是静止的,那么这个opaque属性的设置与否不是一个大问题。但是,如果view是嵌入到scroll view中的,或者是复杂动画的一部分,不将设置这个属性的话肯定会影响程序的性能!可以通过模拟器的Debug\\Color Blended Layers选项来查看哪些view没有设置为不透明。为了程序的性能,尽可能的将view设置为不透明!
I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is “messaging” – that is what the kernal[sic] of Smalltalk is all about... The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviors should be.

 

Alan Kay 曾多次强调 Smalltalk 的核心不是面向对象,面向对象只是 the lesser ideas,消息传递才是 the big idea。
 
 
dSYM文件的生成

符号表文件.dSYM实际上是从Mach-O文件中抽取调试信息而得到的文件目录,实际用于保存调试信息的问价是DWARF,其出身可以从苹果员工的文章《Apple’s “Lazy” DWARF Scheme》了解一二。

1、Xcode自动生成
  • Xcode会在编译工程或者归档时自动为我们生成.dSYM文件,当然我们也可以通过更改Xcode的若干项Build Settings来阻止它那么干。

2、手动生成
  • 另一种方式是通过命令行从Mach-O文件中手工提取,比如:

$ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/dsymutil /Users/wangzz/Library/Developer/Xcode/DerivedData/YourApp-cqvijavqbptjyhbwewgpdmzbmwzk/Build/Products/Debug-iphonesimulator/YourApp.app/YourApp -o YourApp.dSYM
该方式通过Xcode提供的工具dsymutil,从项目编译结果.app目录下的Mach-O文件中提取出调试符号表文件。实际上Xcode也是通过这种方式来生成符号表文件。
 
 
计算崩溃符号表地址

以下面的崩溃堆栈为例:
  • Thread 0:
  • 0   libobjc.A.dylib                   0x33f10f60 0x33efe000 + 77664
  • 1   Foundation                        0x273526ac 0x2734a000 + 34476
  • 2   Foundation                        0x27355c3e 0x2734a000 + 48190
  • 3   UIKit                             0x29ef9d1c 0x29bbc000 + 3398940
  • 4   UIKit                             0x29ef9c9a 0x29bbc000 + 3398810
  • 5   UIKit                             0x29ef954c 0x29bbc000 + 3396940
  • 6   UIKit                             0x29c3a16a 0x29bbc000 + 516458
  • 7   UIKit                             0x29e4b8e6 0x29bbc000 + 2685158
  • 8   UIKit                             0x29c3a128 0x29bbc000 + 516392
  • 9   Your                              0x000f0846 0xa2000 + 321606
  • 10  UIKit                             0x29e90fb2 0x29bbc000 + 2969522
  • 11  UIKit                             0x29e91076 0x29bbc000 + 2969718
  • 12  UIKit                             0x29e867cc 0x29bbc000 + 2926540
  • 13  UIKit                             0x29c9e8ea 0x29bbc000 + 927978
  • 14  UIKit                             0x29bc8a6a 0x29bbc000 + 51818
  • 15  QuartzCore                        0x295f0a08 0x295e4000 + 51720
  • 16  QuartzCore                        0x295ec3e0 0x295e4000 + 33760
  • 17  QuartzCore                        0x295ec268 0x295e4000 + 33384
  • 18  QuartzCore                        0x295ebc4c 0x295e4000 + 31820
  • 19  QuartzCore                        0x295eba50 0x295e4000 + 31312
  • 20  QuartzCore                        0x295e5928 0x295e4000 + 6440
  • 21  CoreFoundation                    0x266d0d92 0x26604000 + 839058
  • 22  CoreFoundation                    0x266ce44e 0x26604000 + 828494
  • 23  CoreFoundation                    0x266ce856 0x26604000 + 829526
  • 24  CoreFoundation                    0x2661c3bc 0x26604000 + 99260
  • 25  CoreFoundation                    0x2661c1ce 0x26604000 + 98766
  • 26  GraphicsServices                  0x2da1a0a4 0x2da11000 + 37028
  • 27  UIKit                             0x29c2a7ac 0x29bbc000 + 452524
  • 28  Your                              0x0024643a 0xa2000 + 1721402
  • 29  libdyld.dylib                     0x34484aac 0x34483000 + 6828
1、 符号表堆栈地址计算方式

要 想利用符号表解析出崩溃对应位置,需要计算出符号表中对应的崩溃堆栈地址。而从上述堆栈中第9行可以看到,应用崩溃发生在运行时地址 0x000f0846,该进程的运行时起始地址是0xa2000,崩溃处距离进程起始地址的偏移量为十进制的321606(对应十六进制为 0x4E846)。三者对应关系:

0x000f0846 = 0xa2000 + 0x4E846
对应的公式为:
  • 运行时堆栈地址 = 运行时起始地址 + 偏移量
崩溃堆栈中的起始地址和崩溃地址均为运行时地址,根据虚拟内存偏移量不变原理,只要提供了符号表TEXT段的起始地址,再加上偏移量(这里为0x4E846)就能得到符号表中的堆栈地址,即:
  • 符号表堆栈地址 = 符号表起始地址 + 偏移量
复制代码
 
   self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]
                                         initWithImage:img
                                         style:UIBarButtonItemStylePlain
                                         target:self
                                         action:@selector(onBack:)];
self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
复制代码

 

不新建一个Cell的情况下调整separaLine的位置?
_myTableView.separatorInset = UIEdgeInsetsMake(0, 100, 0, 0);

 

滑动的时候隐藏navigationbar?
navigationController.hidesBarsOnSwipe = Yes
 
导航条返回键带的title消失!
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60)
                                                     forBarMetrics:UIBarMetricsDefault];
 
只用一个pan手势来代替UISwipegesture的各个方向?
复制代码
 1 - (void)pan:(UIPanGestureRecognizer *)sender
 2 {
 3 typedef NS_ENUM(NSUInteger, UIPanGestureRecognizerDirection) {
 4     UIPanGestureRecognizerDirectionUndefined,
 5     UIPanGestureRecognizerDirectionUp,
 6     UIPanGestureRecognizerDirectionDown,
 7     UIPanGestureRecognizerDirectionLeft,
 8     UIPanGestureRecognizerDirectionRight
 9 };
10 static UIPanGestureRecognizerDirection direction = UIPanGestureRecognizerDirectionUndefined;
11 switch (sender.state) {
12     case UIGestureRecognizerStateBegan: {
13         if (direction == UIPanGestureRecognizerDirectionUndefined) {
14             CGPoint velocity = [sender velocityInView:recognizer.view];
15             BOOL isVerticalGesture = fabs(velocity.y) > fabs(velocity.x);
16             if (isVerticalGesture) {
17                 if (velocity.y > 0) {
18                     direction = UIPanGestureRecognizerDirectionDown;
19                 } else {
20                     direction = UIPanGestureRecognizerDirectionUp;
21                 }
22             }
23             else {
24                 if (velocity.x > 0) {
25                     direction = UIPanGestureRecognizerDirectionRight;
26                 } else {
27                     direction = UIPanGestureRecognizerDirectionLeft;
28                 }
29             }
30         }
31         break;
32     }
33     case UIGestureRecognizerStateChanged: {
34         switch (direction) {
35             case UIPanGestureRecognizerDirectionUp: {
36                 [self handleUpwardsGesture:sender];
37                 break;
38             }
39             case UIPanGestureRecognizerDirectionDown: {
40                 [self handleDownwardsGesture:sender];
41                 break;
42             }
43             case UIPanGestureRecognizerDirectionLeft: {
44                 [self handleLeftGesture:sender];
45                 break;
46             }
47             case UIPanGestureRecognizerDirectionRight: {
48                 [self handleRightGesture:sender];
49                 break;
50             }
51             default: {
52                 break;
53             }
54         }
55         break;
56     }
57     case UIGestureRecognizerStateEnded: {
58         direction = UIPanGestureRecognizerDirectionUndefined;   
59         break;
60     }
61     default:
62         break;
63 }
64 }
复制代码
 
改变uitextfield placeholder的颜色和位置

继承uitextfield,重写这个方法

1 - (void) drawPlaceholderInRect:(CGRect)rect {
2     [[UIColor blueColor] setFill];
3     [self.placeholder drawInRect:rect withFont:self.font lineBreakMode:UILineBreakModeTailTruncation alignment:self.textAlignment];
4 }
 
把navigationbar弄成透明的而不是带模糊的效果
1 [self.navigationBar setBackgroundImage:[UIImage new]
2                          forBarMetrics:UIBarMetricsDefault];
3 self.navigationBar.shadowImage = [UIImage new];
4 self.navigationBar.translucent = YES;
 
statusbar是lightcontent的,结果用UIImagePickerController会导致我的statusbar的样式变成黑色

1 - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
2 {
3     [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
4 }

 

把tableview里cell的小对勾的颜色改成别的颜色

_mTableView.tintColor = [UIColor redColor];
 
一句话添加上拉刷新?(还有一个更好的MJRefresh你懂的)

1 https://github.com/samvermette/SVPullToRefresh
2 
3 [tableView addPullToRefreshWithActionHandler:^{
4 // prepend data to dataSource, insert cells at top of table view
5 // call [tableView.pullToRefreshView stopAnimating] when done
6 } position:SVPullToRefreshPositionBottom];
 
关于运行时
复制代码
 1 unsigned int count;
 2     //获取属性列表
 3     objc_property_t *propertyList = class_copyPropertyList([self class], &count);
 4     for (unsigned int i=0; i<count; i++) {         const char *propertyname =" property_getName(propertyList[i]);"         nslog(@"property----="">%@", [NSString stringWithUTF8String:propertyName]);
 5     }
 6     //获取方法列表
 7     Method *methodList = class_copyMethodList([self class], &count);
 8     for (unsigned int i; i<count; i++) {         method method =" methodList[i];"         nslog(@"method----="">%@", NSStringFromSelector(method_getName(method)));
 9     }
10     //获取成员变量列表
11     Ivar *ivarList = class_copyIvarList([self class], &count);
12     for (unsigned int i; i<count; i++) {         ivar myivar =" ivarList[i];"         const char *ivarname =" ivar_getName(myIvar);"         nslog(@"ivar----="">%@", [NSString stringWithUTF8String:ivarName]);
13     }
14     //获取协议列表
15     __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
16     for (unsigned int i; i<count; i++) {         protocol *myprotocal =" protocolList[i];"         const char *protocolname =" protocol_getName(myProtocal);"         nslog(@"protocol----="">%@", [NSString stringWithUTF8String:protocolName]);
17  
复制代码

 

方法调用
如果用实例对象调用实例方法,会到实例的isa指针指向的对象(也就是类对象)操作。

如果调用的是类方法,就会到类对象的isa指针指向的对象(也就是元类对象)中操作。
  • 首先,在相应操作的对象中的缓存方法列表中找调用的方法,如果找到,转向相应实现并执行。
  • 如果没找到,在相应操作的对象中的方法列表中找调用的方法,如果找到,转向相应实现执行
  • 如果没找到,去父类指针所指向的对象中执行1,2.
  • 以此类推,如果一直到根类还没找到,转向拦截调用。
  • 如果没有重写拦截调用的方法,程序报错。
以上的过程给我带来的启发:
    1. 重写父类的方法,并没有覆盖掉父类的方法,只是在当前类对象中找到了这个方法后就不会再去父类中找了。
    2. 如果想调用已经重写过的方法的父类的实现,只需使用super这个编译器标识,它会在运行时跳过在当前的类对象中寻找方法的过程。

以上是关于iOS开发-开发总结的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发CGRectGetMidX. CGRectGetMidY.CGRectGetMinY. CGRectGetMaxY. CGRectGetMinX. CGRectGetMaxX的使用(代码片段

iOS代码片段CodeSnippets

项目开发收尾总结(片段)

iOS 相当于 Android 片段/布局

vue开发快捷键的总结

iOS开发精选知识点讲解 - 视频等 iOSStrongDemo是由@李刚维护,总结一些iOS开发精选知识点。每一个知识点都有相应的测试代码,非常适合iOS初学者。