iOS Block实现探究

使用clang的rewrite-objc filename 可以将有block的c代码转换成cpp代码。从中可以看到block的实现。

#include <stdio.h>
int main()
{
  void (^blk)(void) = ^{
    printf("Block\n");
  };
  blk();
  return 0;
}

使用clang rewrite-objc以后会看到block的实现

struct __main_block_impl_0 {
 struct __block_impl impl;
 struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0)
{
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
 }
};

可以看到其实block是一个正常的OC类

来看看block是怎样访问外部变量的

int main()
{
  int dmy = 256;
  int val = 10;
  const char *fmt = "val = %d\n";
  void (^blk)(void) = ^{
    printf(fmt, val);
  };
  return 0;
}

转换之后,可以看到

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  const char *fmt;
  int val;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
  }
};


int main()
{
  int dmy = 256;
  int val = 10;
  const char *fmt = "val = %d\n";
  void (*blk)(void) = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val);
  return 0;
}

block的变量会被复制进block中

如果当block要改变传入的变量值怎么办?首先看一下全局变量和本地静态变量

int global_val = 1;
static int static_global_val = 2;
int main()
{
  static int static_val = 3;
  void(^blk)(void) = ^{
  global_val *= 1;
   static_global_val *= 2;
  static_val *= 3;
  };
  blk();
  return 0;
}



struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int *static_val;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *static_val = __cself->static_val; // bound by copy

global_val *= 1;
static_global_val *= 2;
(*static_val) *= 3;
}

由于全局变量是在Data Section中,所以直接可以访问。局部静态变量是通过将其指针传入到block中,block就可以对其值进行修改。

然后看一下__block修饰符变量

int main()
{
  __block int val = 10;
  void (^blk)(void) = ^{val = 1;};
  blk();
  return 0;
}

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_val_0 *val; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,             __Block_byref_val_0 *_val, int flags=0) : val(_val->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_val_0 *val = __cself->val; // bound by ref
(val->__forwarding->val) = 1;}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src)

block修饰符的变量,会生成一个Block_byref_val_0的struct,然后通过访问其forwarding来访问val值。因为Block有可能是在stack或者heap中,所以用forwarding来访问。之所以会将__block单独生成一个struct是因为可能该变量会被多个block使用。

Block分三种类型

0) NSConcreteStackBlock –stack

1) NSConcreteGlobalBlock –data area

2) NSConcreteMallocBlock –heap

自动copy block

当开启ARC时,在某些情况编译器会自动copy block,从stack到heap。

typedef int (^blk_t)(int);
blk_t func(int rate)
{
  return ^(int count){return rate * count;};
}


blk_t func(int rate)
{
  blk_t tmp = &__func_block_impl_0(
__func_block_func_0, &__func_block_desc_0_DATA, rate);

  tmp = objc_retainBlock(tmp);
  return objc_autoreleaseReturnValue(tmp);
}

有些情况,编译器是无法检测是否应该copy block:

当block作为参数传递到方法或函数中。

但是,如果该方法或函数在内部copy,就不用手动再copy:

0)cocoa framework方法, 有usingBlock

1) GCD API

- (id) getBlockArray
{
  int val = 10;
  return [[NSArray alloc] initWithObjects:
 [^{NSLog(@"blk0:%d", val);} copy],
 [^{NSLog(@"blk1:%d", val);} copy], nil];
}

__forwarding

当block从stack copy到 heap中时,block中用到的block也会copy到heap中,并且copy到heap中的block拥有该block。

__block int val = 0;
void (^blk)(void) = [^{++val;} copy];
++val;
blk();
NSLog(@"%d", val);

在block和外的++val都会变成 ++(val.forwarding->val);
当block copy到heap中后, stack中的
forwarding会指向heap中的block, heap中的forwarding会指向自己的block值,这样保证了forwarding指向的是同一个变量值。

Block什么时候会copy到heap中

0)对block调用copy方法。

1)block作为一个函数的返回值。 编译器自动copy

2)赋值给id或block type class 有__strong 修饰符的成员变量。 编译器自动copy

3)usingBlock, GCD API。 在函数内copy

什么时候用该copy block?

0) block是函数返回值

1) block赋值给id或block type class 有__strong 修饰符的成员变量。

2)3)usingBlock, GCD API。 在函数内copy

UIApplicationDelegate 启动选项介绍

1
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  // Override point for customization after application launch.
  return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application
{
  // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
  // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
  // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
  // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
  // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
  // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

- (void)applicationWillTerminate:(UIApplication *)application
{
  // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

每个iOS app的入口都是

1
- application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

当app启动完成,准备运行时,application会调用自己的这个代理方法。

一个app除了点击图标进行启动,还有一些其他方式调用一个app。区别不同启动方式,就需要用到launchOptions. 与userInfo 字典相似,-application:didFinishLaunchingWithOptions: 可以通过launchOptions获取启动信息的key。

通过URL启动

1
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"app://..."]];

http://URL 会调用Safari, mailto://URL 会打开邮箱, tel://URL 会拨打电话。

这种情况下launchOptionsUIApplicationLaunchOptionsURLKey

app通过URL调用时,还可以有一些系统信息。 当通过UIDocumentInteractionController 或者 AirDrop 调用时,launchedOptions 会被设置为:

UIApplicationLaunchOptionsSourceApplicationKey: key值是一个NSString,表示要调用你app的app bundle ID

UIApplicationLaunchOptionsAnnotationKey: key可以存储property-list 对象。

1
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"Document" withExtension:@"pdf"];
if (fileURL) {
  UIDocumentInteractionController *documentInteractionController =
  [UIDocumentInteractionController interactionControllerWithURL:fileURL];

  documentInteractionController.annotation = @{@"foo": @"bar"};
  [documentInteractionController setDelegate:self];
  [documentInteractionController presentPreviewAnimated:YES];
}

推送消息调用

远程推送

远程推送消息调用时,launch option 会是 UIApplicationLaunchOptionsRemoteNotificationKey

1
- (BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  // ...

    if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
       [self application:application didReceiveRemoteNotification:launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]];
    }
}

本地推送

本地推送launch options 会是 UIApplicationLaunchOptionsLocalNotificationKey

1
@import AVFoundation;
@interface AppDelegate ()
@property (readwrite, nonatomic, assign) SystemSoundID localNotificationSound;
@end

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{     
    if (application.applicationState == UIApplicationStateActive) {         
   UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:notification.alertAction                                                               message:notification.alertBody                                                                 delegate:nil
                                             cancelButtonTitle:NSLocalizedString(@"OK", nil)                                                otherButtonTitles:nil];         
   if (!self.localNotificationSound) {             
        NSURL *soundURL = [[NSBundle mainBundle] URLForResource:@"Sosumi"                                                                        withExtension:@"wav"];             
     AudioServicesCreateSystemSoundID((__bridge CFURLRef)soundURL, &_localNotificationSound);      }         
    AudioServicesPlaySystemSound(self.localNotificationSound);         
    [alertView show];     
  }
}
- (void)applicationWillTerminate:(UIApplication *)application {     
  if (self.localNotificationSound) {         
      AudioServicesDisposeSystemSoundID(self.localNotificationSound);     
  }
}

位置事件调用

当由手机位置变动,调用app时,launch option 会是 UIApplicationLaunchOptionsLocationKey, key 是一个NSNumber 包含一个Boolean。

1
@import CoreLocation;
@interface AppDelegate () <CLLocationManagerDelegate>
@property (readwrite, nonatomic, strong) CLLocationManager *locationManager;
@end

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {     
  // ...     
  if (![CLLocationManager locationServicesEnabled]) {         
  [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Location Services Disabled", nil)                                  message:NSLocalizedString(@"You currently have all location services for this device disabled. If you proceed, you will be asked to confirm whether location services should be reenabled.", nil)                                    
                             delegate:nil                           
                    cancelButtonTitle:NSLocalizedString(@"OK", nil)                                                otherButtonTitles:nil] show];     
   }
   else {         
       self.locationManager = [[CLLocationManager alloc] init];         
       self.locationManager.delegate = self;         
       [self.locationManager startMonitoringSignificantLocationChanges];     
   }     
   if (launchOptions[UIApplicationLaunchOptionsLocationKey]) {         
   [self.locationManager startUpdatingLocation];     
   }
}

Newsstand

launch option 会是 UIApplicationLaunchOptionsNewsstandDownloadsKey, 提示用户有新的Newsstand可以下载

蓝牙调用

iOS 7 中蓝牙分主从关系,所以对应的launch option key有两种

UIApplicationLaunchOptionsBluetoothCentralsKey

UIApplicationLaunchOptionsBluetoothPeripheralsKey

1

@import CoreBluetooth;
@interface AppDelegate () <CBCentralManagerDelegate>
@property (readwrite, nonatomic, strong) CBCentralManager *centralManager;
@end

self.centralManager = [[CBCentralManager alloc] initWithDelegate:self
                                                         queue:nil
                                                         options:@{CBCentralManagerOptionRestoreIdentifierKey:(launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey] ?: [[NSUUID UUID] UUIDString])}];
if (self.centralManager.state == CBCentralManagerStatePoweredOn) {     
  static NSString * const UID = @"7C13BAA0-A5D4-4624-9397-15BF67161B1C"; // generated with `$ uuidgen`     
  NSArray *services = @[[CBUUID UUIDWithString:UID]];     
  NSDictionary *scanOptions = @{CBCentralManagerScanOptionAllowDuplicatesKey:@YES};     
  [self.centralManager scanForPeripheralsWithServices:services options:scanOptions];
}

参考链接

iOS7 nested push animation can result in corrupted

在iOS7中,开启push/pop view controller 动画遇到一个问题,就是在快速切换多次后会在console出现一个问题

nested push animation can result in corrupted navigation bar multiple warning
Unbalanced calls to begin/end appearance transitions for

查了一些资料后,找到了一个方法:

0)找到push/pop view controller 动画结束的回调。

UINavigationControllerDelegate代理中的

-(void)navigationController:(UINavigationController *)navigationController
  didShowViewController:(UIViewController *)viewController
               animated:(BOOL)animated

1)找到是否接受点击方法的方法。

UIGestureRecognizerDelegate中的

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch

应用以上两个代理方法,加上一个BOOL的flag值,可以控制是否处罚pop/push

当动画完成时,将flag设置为允许pop/push, shouldReceiveTouch直接返回flag值。

在点击事件时,要注意更改flag值不再接受其他事件,防止在flag为YES时的多次点击。

参考链接:

http://stackoverflow.com/questions/11813091/nested-push-animation-can-result-in-corrupted-navigation-bar-multiple-warning

http://www.taofengping.com/2013/12/26/ios7_barbuttonitem_navigation_gesture/

http://blog.csdn.net/lengshengren/article/details/12616217

http://stackoverflow.com/questions/10150231/how-to-detect-when-uinavigationcontroller-animation-has-finished

iOS 消息传递过程解析

在iOS中调用一个方法,其实是调用一个方法实现的函数指针并传给他对象实例指针,一个Selector,还有函数用到的参数。在Objective-C 中,每一个方法调用其实都是转换成了调用objc_msgSend方法。

objc_msgSend的调用过程如下:

0)首先检查消息接受对象是否为nil. 如果消息接受对象为nil,则触发nil-handler. 默认nil-handler是什么都不做的。

1)在垃圾回收环境中(Mac OS X)中,检查是否调用了(retain, release, autorelease, retainCount), 如果是,就返回self. 所以,这就意味着在垃圾回收机制中,retainCount 返回self。这时调用retainCount就会产生无限循环。

2)检查class的缓存中是否有该方法的实现,如果有,调用。

3)在类方法中是否有该方法,如果有就调用类方法的实现。

4)查看父类中是否有该方法实现,如果没有再查找父类的父类中有没有实现。如果找到了,调用。

5)调用resolveInstanceMethod:(或者resolveClassMethod:). 如果返回YES, 则调用。 该对象会有该方法的实现,因为它会调用class_addMethod.

6) 调用forwardingTargetForSelector:. 如果返回non-nil, 则对返回对象发消息。此处不会返回self,否则会进入无限循环。

7)调用methodSignatureForSelector:, 如果返回non-nil, 创建一个NSInvocation 并且将这个NSInvocation传递给forwardInvocation:.

8) 调用doesNotRecognizeSelector:. 默认实现就是抛一个异常。

iOS KVO实现方式

KVO 也许是iOS中“最神奇”的部分了,因为你不需要在被观察对象中添加任何代码,就可以实现对被观察对象属性改变的通知。KVO究竟是怎么实现的?

KVO是通过Objective-C的runtime来实现的。当你第一次要对一个对象进行观察时,runtime会为你创建一个被观察对象class的subclass。在这个新创建的subclass中,KVO会复写所要观察属性的setter方法,然后转换被观察对象的isa指针,指向新创建的subclass,所以,你想要观察的对象,变成了KVO在runtime时创建的subclass。因为Apple不想让这种机制暴露,所以还会复写要观察对象的class方法,所以,当你调用class来判断该对象的class时,还会显示原对象的class类型,而不是subclass的类型。

继续探究

// gcc -o kvoexplorer -framework Foundation kvoexplorer.m

#import <Foundation/Foundation.h>
#import <objc/runtime.h>


@interface TestClass : NSObject
{
    int x;
    int y;
    int z;
}
@property int x;
@property int y;
@property int z;
@end
@implementation TestClass
@synthesize x, y, z;
@end

static NSArray *ClassMethodNames(Class c)
{
    NSMutableArray *array = [NSMutableArray array];

    unsigned int methodCount = 0;
    Method *methodList = class_copyMethodList(c, &methodCount);
    unsigned int i;
    for(i = 0; i < methodCount; i++)
        [array addObject: NSStringFromSelector(method_getName(methodList[i]))];
    free(methodList);

    return array;
}

static void PrintDescription(NSString *name, id obj)
{
    NSString *str = [NSString stringWithFormat:
        @"%@: %@\n\tNSObject class %s\n\tlibobjc class %s\n\timplements methods <%@>",
        name,
        obj,
        class_getName([obj class]),
        class_getName(obj->isa),
        [ClassMethodNames(obj->isa) componentsJoinedByString:@", "]];
    printf("%s\n", [str UTF8String]);
}

int main(int argc, char **argv)
{
    [NSAutoreleasePool new];

    TestClass *x = [[TestClass alloc] init];
    TestClass *y = [[TestClass alloc] init];
    TestClass *xy = [[TestClass alloc] init];
    TestClass *control = [[TestClass alloc] init];

    [x addObserver:x forKeyPath:@"x" options:0 context:NULL];
    [xy addObserver:xy forKeyPath:@"x" options:0 context:NULL];
    [y addObserver:y forKeyPath:@"y" options:0 context:NULL];
    [xy addObserver:xy forKeyPath:@"y" options:0 context:NULL];

    PrintDescription(@"control", control);
    PrintDescription(@"x", x);
    PrintDescription(@"y", y);
    PrintDescription(@"xy", xy);

    printf("Using NSObject methods, normal setX: is %p, overridden setX: is %p\n",
          [control methodForSelector:@selector(setX:)],
          [x methodForSelector:@selector(setX:)]);
    printf("Using libobjc functions, normal setX: is %p, overridden setX: is %p\n",
          method_getImplementation(class_getInstanceMethod(object_getClass(control),
                                   @selector(setX:))),
          method_getImplementation(class_getInstanceMethod(object_getClass(x),
                                   @selector(setX:))));

    return 0;
}

首先,定义一个TestClass,有3个属性。
然后定义一些工具函数。ClassMethodNames 通过Objective-C 的runtime函数,来返回当前class实现的方法名。

代码执行结果

control: <TestClass: 0x104b20> NSObject class TestClass libobjc class TestClass implements methods <setX:, x, setY:, y, setZ:, z>


x: <TestClass: 0x103280> NSObject class TestClass libobjc class NSKVONotifying_TestClass implements methods <setY:, setX:, class, dealloc, _isKVOA>

y: <TestClass: 0x104b00> NSObject class TestClass libobjc class NSKVONotifying_TestClass implements methods <setY:, setX:, class, dealloc, _isKVOA>

xy: <TestClass: 0x104b10> NSObject class TestClass libobjc class NSKVONotifying_TestClass implements methods <setY:, setX:, class, dealloc, _isKVOA>

Using NSObject methods, normal setX: is 0x195e, overridden setX: is 0x195e

Using libobjc functions, normal setX: is 0x195e, overridden setX: is 0x96a1a550

可以看出,
0)TestClass 在runtime时变成了NSKVONotifying_TestClass

1)虽然x,y只观察了一个属性,但是NSKVONotifying_TestClass却实现了setY, setX方法。也就是说,一个类,KVO只会subclass一个KVO类,也就是NSKVONotifying_TestClass类。

2)NSKVONotifying_TestClass 覆写了class方法,来掩盖subclass的存在,还覆写了dealloc方法。除此之外,还有一个新的方法_isKVOA, 是Apple提供的一个私有方法,用于判断一个object是否生成动态subclass。

原文链接:http://www.mikeash.com/pyblog/friday-qa-2009-01-23.html

iOS GCD 与 NSOperationQueue对比

  • NSOperationQueue 是在GCD基础上实现的,只不过是GCD更高一层的抽象。
  • GCD 只支持FIFO 的队列, 而NSOperationQueue可以调整队列的执行顺序。(通过调整权重)
  • NSOperationQueue可以在Operation间设置依赖关系,而GCD不可以。 如果一个Operation依赖另一个Operation所产生的数据的化,你可以设置一个Operation依赖于另一个Operation来实现, NSOperationQueue可以根据依赖关系,可以以正确的顺序执行Queue中的Operation。
  • NSOperationQueue支持KVO。 这就意味着你可以观察任务的状态属性。
    以上几点并不是说在任何处理多任务时一定要选择NSOperationQueue, 因为NSOperationQueue在执行速度上会比GCD慢。

iOS 获取本地设备IP地址

1
#import <ifaddrs.h>
#import <arpa/inet.h>
// Get IP Address
- (NSString *)getIPAddress {    
  NSString *address = @"error";
  struct ifaddrs *interfaces = NULL;
  struct ifaddrs *temp_addr = NULL;
  int success = 0;
  // retrieve the current interfaces - returns 0 on success
  success = getifaddrs(&interfaces);
  if (success == 0) {
      // Loop through linked list of interfaces
      temp_addr = interfaces;
      while(temp_addr != NULL) {
          if(temp_addr->ifa_addr->sa_family == AF_INET) {
              // Check if interface is en0 which is the wifi connection on the iPhone
              if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {
                  // Get NSString from C String
                  address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];               
              }
          }
          temp_addr = temp_addr->ifa_next;
      }
  }
  // Free memory
  freeifaddrs(interfaces);
  return address;
  }

iOS Crash类别总结

EXC_BAD_ACCESS

An EXC__BAD_ACCESS occurs whenever you try to access or send a message to a deallocated object. The most common cause of EXC_BAD_ACCESS is when you initialize a variable in one of your initializer methods but use the wrong ownership qualifier, which results in deallocation of your object. For example, you create an NSMutableArray of elements for your UITableViewController in the viewDidLoad method but set the ownership qualifier of the list to unsafe_unretained or assign instead of strong. Now in cellForRowAtIndexPath:, when you try to access the deallocated object, you’ll crash with a EXC_BAD_ACCESS. Debugging EXC_BAD_ACCESS is made easy with the NSZombiesEnabled environment variable.

SIGSEGV

A signal segment fault(SIGSEGV) is a more serious issue that the operating system raises. It occurs when there is a hardware failure or when you try to access a memory address that cannot be read or when you try to write to a protected address.

The first case, a hardware failure, is uncommon. When you try to read data stored in RAM and the RAM hardware at that location is faulty, you get a SIGSEGV. But more often than not, a SIGSEGV occurs for the latter two reasons. By default, code pages are protected from being written into and data pages are protected from being executed. When one of your pointers in your application points to a code page and tries to alter the value pointed to, you get a SIGSEGV. You also get a SIGSEGV when you try to read the value of a pointer that was initialized to a garbage value pointing to an address that is not a valid memory location.

SIGSEGV faults are more tedious to debug, and the most common case reason a SIGSEGV happens is an incorrect typecast. Avoid hacking around with pointers or trying to a read a private data structure by advancing the pointer manually. When you do that and don’t advance the pointer to take care of the memory alignment or padding, you get a SIGSEGV.

SIGBUS

A signal bus(SIGBUS) error is a kind of bad memory access where the memory you tried to access is an invalid memory address. That is, the address pointed to is not a physical memory address at all. Both SIGSEGV and SIGBUS are subtypes of EXC_BAD_ACCESS.

SIGTRAP

SIGTRAP stands for signal trap. This is not really a crash signal. It’s sent when the processor executes a trap instruction. The LLDB debugger usually handles this signal and stops at a specified breakpoint. If you get a SIGTRAP for no apparent reason, a clean build will usually fix it.

EXC_ARITHMETIC

divide zero.

SIGILL

SIGILL stands for SIGNAL ILLEGAL INSTRUCTION. This happens when you try to execute an illegal instruction on the processor. You execute an illegal instruction when you’re trying to pass a function pointer to another function, but for one reason or other, the function pointer is corrupted and is pointing to a deallocated memory or a data segment. Sometimes you get an EXC_BADINSTRUCTION instead of SIGILL, and through both are synonymous, EXC* are machine-independent equivalents of this signal.

SIGABRT

SIGABRT stands for SIGNAL ABORT. This is a more controlled crash where the operating system has detected a condition that is not safe and asks the process to perform cleanup if any is needed. There is no one silver bullet to help you debug the underlying error for this signal. Frameworks like cocos2d or UIKit often call the C function abort when certain preconditions are not met or when something really bad happens. When a SIGABRT occurs, the console usually has a wealth of information about what went wrong. Since it’s controlled crash, you can print the backtrace by typing bt into the LLDB console.

iOS LLDB console debug总结

Xcode’s debugging console window is a full-featured LLDB debugging console. When your app is paused(at a breakpoint), the debugging console shows the LLDB command prompt. You can type any LLDB debugger common into the console to help you with debugging, including loading external python script.

The most frequently used command is po, which stands for print object. When your application is paused in debugger, you can print any variable that is in the current scope. This includes any stack variables, class variables, properties, ivars, and global variables. In short, any variable that is accessible by your application at the breakpoint can be accessed via the debugging console.

Printing scalar variables

when you’re dealing with scalars like integers or structs(CGRect, CGPoint, etc..), instead of using po, you use p, followed by the type of struct.

p (int) self.myAge

p (CGPoint) self.view.center

Printing Registers

Registers in your CPU are used for storing variables that have to be accessed frequently. Compilers optimize frequently used variables like the loop variable, method arguments, and return variables in the registers. When your app crashes for no apparent reason, probing the register for the method name or the selector name that crashed your app will be very useful.

(lldb) register read

General Purpose Registers:

  r0 = 0x37c9cb21  libobjc.A.dylib`objc_msgSend + 1

  r1 = 0x37c9cb21  libobjc.A.dylib`objc_msgSend + 1

  r2 = 0x01b5c214  "idKey"

  r3 = 0x01b5eb28  "checkCurrentContactBean"

  r4 = 0x00000000

  r5 = 0x37c9cb21  libobjc.A.dylib`objc_msgSend + 1

  r6 = 0x27d09bd0

  r7 = 0x27d09bc8

  r8 = 0x01b5bbc4  "view"

  r9 = 0x00000000

 r10 = 0x01b5e3f8  "masterViewController"

 r11 = 0x00000000

 r12 = 0x3a11c1d0  (void *)0x382c3959: _os_lock_handoff_unlock$VARIANT$mp + 1

  sp = 0x27d09358

  lr = 0x37cacabb  libobjc.A.dylib`objc_object::sidetable_release(bool) + 95

  pc = 0x002cce40  iPoS_IOS`-[PersonMainForm viewWillAppear:] + 232 at PersonMainForm.m:59

cpsr = 0x60000030

Your output may vary, butt pay close attention to the wax, dcx, and esi on the simulator or r0-r4 registers when running on a device. These registers store some of the values that you’re interested in. In the Simulator, the dcx register holds the name of the selector that is called when your app crashed. You print an individual register to console by specifying the register name as shown below
register read ecx.

You can also specify multiple registers like

register  read eax ecx.

The dcx register on Intel architecture and the r15 register on ARM architecture hold the program counter. Printing the address of the program counter will show the last executed instruction. Similarly, wax(r0 on ARM) holds the receiver address, ecx (r4 on ARM) and holds the selector that was called last. The arguments to the methods are stored in registers r1-r3. If your selector has more than three arguments, they are stored on stack, accessible via the stack pointer(r13). sp, lr, and pc are actually aliases to the r13,r14 and r15 register, respectively. Hence, register read r13 is equivalent to register read sp.

iOS KVO(Key-Value Observing)总结

KVO 允许观察者对被观察对象的特定属性的值进行观察,如果被观察对象的特定值更改,会触发一个observeValueForKeyPath:(NSString )keyPath ofObject:(id)object change:(NSDictionary )change context:(void *)context的方法。

KVO与NSNotificationCenter有很多相似的地方。首先,KVO需要给被观察者添加观察者,addObserver:forKeyPath:options:context:.取消观察,用removeObserver:forKeyPath:context:.

KVO与NSNotificationCenter不同的是,你不用去手动调用某个方法,如果没有观察者到情况下,广播是会发送到,而KVO则不会执行。KVO只会在有观察者对象到情况下才会执行通知方法。这使得KVO在某些对性能要求很高的地方,是一个很好到选择。

注册观察者

- (void)registerAsObserver
{
  [object addObserver:inspector
      forKeyPath:@"someProperty"
        options:NSKeyValueObservingOptionNew
        comtext:NULL];
}

接受通知

- (void)observeValueForKeyPath:(NSString *)keyPath
                   ofObject:(id)object
                      change:(NSDictionary *)change
                      context:(void *)context
{
    if(someCondition)
    {
       //do something
    }

    /*
      Be sure to call the superclass's implementation
      * if it implements it *
      NSObject does not implement the method.
     */
    [super observeValueForKeyPath:keyPath
                         ofObject:object
                          change:change
                           context:context];
}

取消观察者

- (void)unregisterForChangeNotification
{
   [observedObject removeObserver:inspector forKeyPath:@"someProperty"];
}

关于如何查看一个对象是否有观察者,

在网上查了一下,有一个[object observationInfo]的方法,如果没有观察者返回nil.

对对象删除观察者还可以用

@try
{
  [object removeObserver:inspector forKeyPath:@"someProperty"];
}
@catch (NSException *e)
{
}

来防止对一个没有观察者的对象删除造成程序crash.