1.iosä¸allocånewçåºå«
2.xcode调试心得
3.如何使iOS地图加Annotation有从空中掉下来的源码效果
iosä¸allocånewçåºå«
1.å¨å®é å¼åä¸å¾å°ä¼ç¨å°newï¼ä¸è¬å建对象å±ä»¬çå°çå ¨æ¯[[className alloc] init]
ä½æ¯å¹¶ä¸æå³çä½ ä¸ä¼æ¥è§¦å°newï¼å¨ä¸äºä»£ç ä¸è¿æ¯ä¼çå°[className new]ï¼
è¿æå»é¢è¯çæ¶åï¼ä¹å¾å¯è½è¢«é®å°è¿ä¸ªé®é¢ã
2.é£ä¹ï¼ä»ä»¬ä¸¤è ä¹é´å°åºæä»ä¹åºå«å¢
æ们çæºç ï¼
+ new { id newObject = (*_alloc)((Class)self, 0); Class metaClass = self->isa; if (class_getVersion(metaClass) > 1) return [newObject init]; else return newObject; } //è alloc/init åè¿æ ·ï¼ + alloc { return (*_zoneAlloc)((Class)self, 0, malloc_default_zone()); } - init { return self; }
éè¿æºç ä¸æ们åç°ï¼[className new]åºæ¬çåäº[[className alloc] init]ï¼
åºå«åªå¨äºallocåé å åçæ¶å使ç¨äºzone.
è¿ä¸ªzoneæ¯ä¸ªä»ä¹ä¸ä¸å¢ï¼
å®æ¯ç»å¯¹è±¡åé å åçæ¶åï¼æå ³èç对象åé å°ä¸ä¸ªç¸é»çå ååºåå ï¼ä»¥ä¾¿äºè°ç¨æ¶æ¶èå¾å°ç代价ï¼æåäºç¨åºå¤çé度ï¼
3.è为ä»ä¹ä¸æ¨è使ç¨newï¼
ä¸ç¥å¤§å®¶åç°äºæ²¡æï¼å¦æ使ç¨newçè¯ï¼åå§åæ¹æ³è¢«åºå®æ»åªè½è°ç¨init.
èä½ æ³è°ç¨initXXXæä¹åï¼æ²¡é¨å¿ï¼æ®è¯´æåç设计æ¯å®å ¨åé´Smalltalkè¯æ³æ¥çã
ä¼ è¯´é£ä¸ªæ¶åå·²ç»æallocFromZone:è¿ä¸ªæ¹æ³ï¼
ä½æ¯è¿ä¸ªæ¹æ³éè¦ä¼ 个åæ°id myCompanion = [[TheClass allocFromZone:[self zone]] init];
è¿ä¸ªæ¹æ³åä¸é¢è¿æ ·ï¼
+ allocFromZone:(void *) z { return (*_zoneAlloc)((Class)self, 0, z); } //åæ¥ç®å为ä¸é¢è¿ä¸ªï¼ + alloc { return (*_zoneAlloc)((Class)self, 0, malloc_default_zone()); }
ä½æ¯ï¼åºç°ä¸ªé®é¢ï¼è¿ä¸ªæ¹æ³åªæ¯ç»å¯¹è±¡åé äºå åï¼å¹¶æ²¡æåå§åå®ä¾åéã
æ¯ä¸æ¯ååå°newé£æ ·çå¤çæ¹å¼ï¼å¨æ¹æ³å é¨éå¼è°ç¨initæ¹æ³å¢ï¼
åæ¥åç°âæ¾ç¤ºè°ç¨æ»æ¯éå¼è°ç¨è¦å¥½âï¼æ以åæ¥å°±æ两个æ¹æ³åå¼äºã
æ¦æ¬æ¥è¯´ï¼newåalloc/initå¨åè½ä¸å ä¹æ¯ä¸è´çï¼åé å å并å®æåå§åã
å·®å«å¨äºï¼éç¨newçæ¹å¼åªè½éç¨é»è®¤çinitæ¹æ³å®æåå§åï¼
éç¨allocçæ¹å¼å¯ä»¥ç¨å ¶ä»å®å¶çåå§åæ¹æ³ã
xcode调试心得
没有系统的学习和总结过Xcode调试相关的知识,这里参考/里面的源码教程,总结一下调试相关的源码知识,算半拉翻译,源码半拉总结吧崩溃的源码表现一般来说:
SIGABRT(好处理)
EXC_BAD_ACCESS(一般内存问题)
SIGBUS
SIGSEGV
左面工具栏会按照线程分出bug所在,thread1一般主线程,源码老师评价系统源码其他线程的源码问题会在自己的位置显示。点开里面的源码方法都是看不懂的汇编(其实以前学过)。
对于Xcode下方有提示的源码bug一般很好解决,但是源码有时候只是简单的EXC_BAD_ACCESS,无从下手,左面工具栏中的源码方法也看不出所以然,这时要把顶部工具栏的源码breakpoint打开,也许左面就会显示出更多出问题的源码方法,如图打开brekpoints以后多出了所选的源码方法提示,找到了是源码数组的问题。当然也可以在左面下方滑块调节来显示出更多提示的lua 5.1源码方法。
总结的来说,就是在左面栏里找到出问题的地方
App启动的流程
上面的图的调用关系说明了App是怎么启动的,除了main方法,其他方法都是看不到的,默认封装到系统的framework里,没法看源码
方法引用错误一般来说:
[UINavigationController setList:]: unrecognized selector sent to instance 0x6d4ed
1 [UINavigationController setList:]: unrecognized selector sent to instance 0x6d4ed这种要不就是这个类没这个方法,或者调用方法的对象错误,或者拼错,比较简单
看打印信息
没有打印信息的时候,可以点这个,有时候需要多点几次,可以有更详细的错误打印信息,lldb调试输入c功能是一样的,都是让程序继续运行
This class is not key value coding-compliant
Problems[:f] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is notkey value coding-compliant for the key button.'
12
3
4
5 Problems[:f] *** Terminating app due to uncaught exception 'NSUnknownKeyException',
reason: '[ setValue:forUndefinedKey:]: this class is not
key value coding-compliant for the key button.'
1.有时会碰到这种错误,印象里是批量php源码请求的网络列表返回为空,出现了个这么诡异的现象,这是一种情况。
2.NSUnknownKeyException指示了未知的key,而这个未知的key出现在MainViewController里,这个key的名字是button
先看nib,在这个例子里有一个button,和MainViewController的属性button连接了IBOutlet,但是@property对应的@synthesize没有写,出现了这个问题,虽然在iOS6可以不用写@synthesize了,但是在老版本可能还会出现这个问题
3.总结一下,“This class is not key value coding-compliant”这个问题出现在NIB相关的地方,一般是iboutlet已经连接,但是这个属性却不存在,常常发生在ib连着呢,军事头条源码属性给删了。
使用DEBUGGER
[self performSegueWithIdentifier:@"ModalSegue" sender:sender];
1 [self performSegueWithIdentifier:@"ModalSegue" sender:sender];这句话出问题了,不知道怎么处理,可以在左面选中抛出的异常expection_throw
用LLDB的debugger po $eax会调用description方法,打印全部信息
po是point object,$eax是当前cpu注册者之一,如果选中了异常错误输入这个命令,这个注册者就是NSException对象,注意$eax是用于模拟器的,真机要用$r0
可以看大原因了,是segue问题,storyboard中的问题这里就定位了
类似的,还有这些debugger方法
po [$eax class] //可以看到 (id) $2 = 0xe NSException,数字不重要NSException是问题的名字po [$eax name]//得到exception的名字po[$eax reason]//得到错误原因(unsigned int) $4 = Receiver () has no segue with identifier 'ModalSegue'
12
3
4
5 po [$eax class] //可以看到 (id) $2 = 0xe NSException,数字不重要NSException是问题的名字
po [$eax name]//得到exception的名字
po[$eax reason]//得到错误原因(unsigned int) $4 = Receiver () has no segue with identifier 'ModalSegue'
NSAssert用法
- (void)doSomethingWithAString:(NSString *)theString{ NSAssert(theString != nil, @"String cannot be nil");NSAssert([theString length] = 3, @"String is too short");. . .}
12
3
4
5
6
7
8
9
- (void)doSomethingWithAString:(NSString *)theString
{
NSAssert(theString != nil, @"String cannot be nil");
NSAssert([theString length] = 3, @"String is too short");
. . .
}
NSAssert最为一种防御型的代码,目的就是一有错,程序就伴随着异常崩溃,或者说停止运行,thinkphp 源码解析不往下进行。上面的代码当传入空数组的时候就会打印这个:
-- ::. Problems[:c] *** Assertion failure in -[MainViewController doSomethingWithAString:], /Users/lipengxuan/Downloads/Problems/Problems/MainViewController.m:
1 -- ::. Problems[:c] *** Assertion failure in -[MainViewController doSomethingWithAString:], /Users/lipengxuan/Downloads/Problems/Problems/MainViewController.m:有的时候程序崩溃打印信息就会出现Assertion,那么就是这句话起作用了,这个时候可以继续运行(lldb c),可以看到更详细的打印信息。
总结一下,遇到Assertion failure,就可以下一步运行看更详细的信息
BreakPoint使用breakpoint 分Exception breakPoint和breakPoint
Exception breakPoint:程序崩溃异常了会立刻暂停到断点,点加号第一个就是添加Exception断点
breakPoint:随意放
Finally!终于到了传说中的打断点,这个很基本很经典的调试方法,事实上,断点和NSLog用法差不多,只不过不用你去写了
一个简单的例子,现在有个这么个方法
- (id)initWithStyle:(UITableViewStyle)style{ NSLog(@"init with style");if (self == [super initWithStyle:style]){ list = [NSMutableArray arrayWithCapacity:];[list addObject:@"One"];[list addObject:@"Two"];[list addObject:@"Three"];[list addObject:@"Four"];[list addObject:@"Five"];}return self;}
12
3
4
5
6
7
8
9
- (id)initWithStyle:(UITableViewStyle)style
{
NSLog(@"init with style");
if (self == [super initWithStyle:style])
{
list = [NSMutableArray arrayWithCapacity:];
[list addObject:@"One"];
[list addObject:@"Two"];
[list addObject:@"Three"];
[list addObject:@"Four"];
[list addObject:@"Five"];
}
return self;
}
程序运行后我发现貌似这个方法没有执行,这是一种猜测,通常我会在方法里加入打印信息,也可以打断点,在方法定义的地方加断点,如果调用这个方法了,就会停止在这里,起到了一样的作用。
接着是单步调试
打断点,然后点击跳跃的箭头,就可以一步步的执行了,更精细的步骤可以F6,期间可以随时打印想看的变量,比如在tableview的cellForRowAtIndexPath函数中用po indexPath打印出indexPath的值
(NSIndexPath *) $3 = 0x 2 indexes [0, 1]
1 (NSIndexPath *) $3 = 0x 2 indexes [0, 1]意思就是section 0 row 1
这样进行一步打印一次,可以看出indexes也在变化,知道出问题的敌方
Problems[:f] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 5 beyond bounds [0 .. 4]'*** First throw call stack:(NSIndexPath *) $ = 0xa8a6c0 2 indexes [0, 5]
12
3
4
5
6
7 Problems[:f] *** Terminating app due to uncaught exception 'NSRangeException',
reason: '*** -[__NSArrayM objectAtIndex:]: index 5 beyond bounds [0 .. 4]'
*** First throw call stack:
(NSIndexPath *) $ = 0xa8a6c0 2 indexes [0, 5]
单步调试找出来了index row=5超出了数组的范围
总结一下,po命令非常实用,只要找到正确的调用方法,就可以打印对象信息
ZOMBIES问题EXC_BAD_ACCESS问题一般是内存问题,比如下面这种情况
程序停在了这里,EXC_BAD_ACCESS问题,但是也不知道具体问题是什么,这时候可以用zombie 工具检测
Xcode点击左上角项目名字-Edit Scheme-Diagnostics-Enable Zombie Objects,OK再次运行
会多出这段话
-- ::. Problems[:c] *** -[__NSArrayM objectAtIndex:]: message sent to deallocated instance 0xcbe
1 -- ::. Problems[:c] *** -[__NSArrayM objectAtIndex:]: message sent to deallocated instance 0xcbe这段话什么意思呢?
创建一个对象,alloc一个对象会分配给这个对象一块内存,党这个对象release了,引用计数归零了,这块内存就会dealloc掉,之后其他对象就可以用这块内存。
但是可能有这样一种情况,正在有对象使用的内存被另一个对象企图指向使用,或者已经被释放的内存企图再次被释放。
僵尸工具的作用是让对象被released的时候,内存不dealloc,这块内存被标记为“undead”,如果其他对象想再用这块内存,app可以识别出错误并显示“message sent to deallocated instance”,就像上面的那段话一样。结合上面的代码
cell.textLabel.text = [list objectAtIndex:indexPath.row];
1 cell.textLabel.text = [list objectAtIndex:indexPath.row];这段话就是zombie对象出现的地方,一般来说textLabel 还有indexPath.row应该没问题,问题应该出现在list这个数组,而且zombie tool也说了是[__NSArrayM objectAtIndex:]的问题,利用NSLog打印list,可以看到内存地址和错误的地址相同,那么错误就定义在list了
-- ::. Problems[:c] list is 0xb0ed-- ::. Problems[:c] *** -[__NSArrayM objectAtIndex:]: message sent to deallocated instance 0xb0ed0
12
3 -- ::. Problems[:c] list is 0xb0ed0
-- ::. Problems[:c] *** -[__NSArrayM objectAtIndex:]: message sent to deallocated instance 0xb0ed0
当然用dubugger 命令 p list也可以打印出这个结果
这就是zombie 工具起的作用,解决内存问题
总结的来说,如果出现了EXC_BAD_ACCESS错误,打开僵尸工具,再试一遍。但是没问题的时候不要打开这个工具,因为这个工具从不dealloc内存,只是标记内存为”undead”,会导致内存泄露,所以用的时候再打开,诊断后再关掉。
习惯问题应该看bug一样的视线看warning,能修复就修复。
- (void)buttonTapped:(id)sender{ NSLog(@"You tapped on: %s", sender);}
12
3
4
5
6
7 - (void)buttonTapped:(id)sender
{
NSLog(@"You tapped on: %s", sender);
}
比如这个按钮点击事件,%s是打印C-String,就是末尾是NUL的字符串,但是这里sender是按钮对象,所以会崩溃,不能忽略警告
如何使iOS地图加Annotation有从空中掉下来的效果
第一种是实现MKMapViewDelegate的一个方法,然后自已实现下落的动画效果,代码如下:源码打印?- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views { MKAnnotationView *aV; for (aV in views) { CGRect endFrame = aV.frame; aV.frame = CGRectMake(aV.frame.origin.x, aV.frame.origin.y - .0, aV.frame.size.width, aV.frame.size.height); [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.]; [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [aV setFrame:endFrame]; [UIView commitAnimations]; } } 第二种方法很简单,只需要设置一个annotationview的属性值,也是在MKMapViewDelegate的一个方法中实现,代码如下:源码打印?- (MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id )annotation { if (annotation == mV.userLocation) { return nil; } MKPinAnnotationView *pinView = nil; static NSString *defaultPinID = @"custom pin"; pinView = (MKPinAnnotationView *)[mV dequeueReusableAnnotationViewWithIdentifier:defaultPinID]; if ( pinView == nil ) { pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID] autorelease]; [pinView setDraggable:YES]; } pinView.pinColor = MKPinAnnotationColorRed; pinView.canShowCallout = YES; pinView.animatesDrop = YES; return pinView; }如何使iOS地图加Annotation有从空中掉下来的效果