目录

NSNotificationCenter 使用姿势详解

NSNotificationCenter

对于这个没必要多说,就是一个消息通知机制,类似广播。观察者只需要向消息中心注册感兴趣的东西,当有地方发出这个消息的时候,通知中心会发送给注册这个消息的对象。这样也起到了多个对象之间解耦的作用。苹果给我们封装了这个NSNotificationCenter,让我们可以很方便的进行通知的注册和移除。然而,有些人的姿势还是有点小问题的,下面就看看正确的姿势吧!<br>

正确姿势之remove

只要往NSNotificationCenter注册了,就必须有remove的存在,这点是大家共识的。但是大家在使用的时候发现,在UIViewController 中addObserver后没有移除,好像也没有挂!为什么?? 下面我们就一步步来探究下。

首先,向NSNotificationCenter中addObserver后,并没有对这个对象进行引用计数加1操作,所以它只是保存了地址。为了验证这个操作,我们来做下代码的测试。

一个测试类,用来注册通知:

 
	@implementation MRCObject

- (id)init
{
    if (self = [super init]) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];
    }
    return self;
}

- (void)test
{
    NSLog(@"=================");
}

- (void)dealloc
{
    [super dealloc];
}

@end

 

这个类很简单,就是在初始化的时候,给他注册一个通知。但是在销毁的时候不进行remove操作。我们在VC中创建这个对象后,然后销毁,最后发送这个通知:

 
- (void)viewDidLoad {
    [super viewDidLoad];

    MRCObject *obj = [[MRCObject alloc] init];
    [obj release];

    [[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];
}

在进入这个vc后,我们发现挂了。。而打印出的信息是:

2015-01-19 22:49:06.655 测试[1158:286268] *** -[MRCObject test]: message sent to deallocated instance 0x17000e5b0

我们可以发现,向野指针对象发送了消息,所以挂掉了。从这点来看,苹果实现也基本差不多是这样的,只保存了个对象的地址,并没有在销毁的时候置为nil。

这点就可以证明,addObserver后,必须要有remove操作。

现在我们在UIViewController中注册通知,不移除,看看会不会挂掉。

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];
}

首先用navigationController进入到这个页面,然后pop出去。最后点击发送通知的按钮事件:

- (void)didButtonClicked:(id)sender
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];
}

无论你怎么点击这个按钮,他就是不挂!这下,是不是很郁闷了?我们可以找找看,你代码里面没有remove操作,但是NSNotificationCenter那边已经移除了,不然肯定会出现上面野指针的问题。看来看去,也只能说明是UIViewController自己销毁的时候帮我们暗地里移除了。

那我们如何证明呢?由于我们看不到源码,所以也不知道有没有调用。这个时候,我们可以从这个通知中心下手!!!怎么下手呢?我只要证明UIViewController在销毁的时候调用了remove方法,就可以证明我们的猜想是对的了!这个时候,就需要用到我们强大的类别这个特性了。我们为NSNotificationCenter添加个类别,重写他的- (void)removeObserver:(id)observer方法:

- (void)removeObserver:(id)observer
{
    NSLog(@"====%@ remove===", [observer class]);
}

这样在我们VC中导入这个类别,然后pop出来,看看发生了什么!

2015-01-19 22:59:00.580 测试[1181:288728] ====TestViewController remove===

怎么样?是不是可以证明系统的UIViewController在销毁的时候调用了这个方法。(不建议大家在开发的时候用类别的方式覆盖原有的方法,由于类别方法具有更高的优先权,所以有可能影响到其他地方。这里只是调试用)。

以上也提醒我们,在你不是销毁的时候,千万不要直接调用[[NSNotificationCenter defaultCenter] removeObserver:self]; 这个方法,因为你有可能移除了系统注册的通知。

正确姿势之注意重复addObserver

在我们开发中,我们经常可以看到这样的代码:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"test" object:nil];
}

就是在页面出现的时候注册通知,页面消失时移除通知。你这边可要注意了,一定要成双成对出现,如果你只在viewWillAppear 中 addObserver没有在viewWillDisappear 中 removeObserver那么当消息发生的时候,你的方法会被调用多次,这点必须牢记在心。

正确姿势之多线程通知

NSNotificationCenter消息的接受线程是基于发送消息的线程的。也就是同步的,因此,有时候,你发送的消息可能不在主线程,而大家都知道操作UI必须在主线程,不然会出现不响应的情况。所以,在你收到消息通知的时候,注意选择你要执行的线程。

//接受消息通知的回调
- (void)test
{
    if ([[NSThread currentThread] isMainThread]) {
        NSLog(@"main");
    } else {
        NSLog(@"not main");
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        //do your UI
    });

}

//发送消息的线程
- (void)sendNotification
{
    dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(defaultQueue, ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];
    });
}

枚举的正确使用姿势

状态

typedef NS_ENUM(NSInteger, UIScrollViewKeyboardDismissMode) {
    UIScrollViewKeyboardDismissModeNone,
    UIScrollViewKeyboardDismissModeOnDrag,          
    UIScrollViewKeyboardDismissModeInteractive
};

选项

什么是选项:同时可以出现一个或多个值:

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
        UIViewAutoresizingNone                 = 0,
        UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
        UIViewAutoresizingFlexibleWidth        = 1 << 1,
        UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
        UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
        UIViewAutoresizingFlexibleHeight       = 1 << 4,
        UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

iOS 通过二进制判断图片类型

+ (NSString *)typeForImageData:(NSData *)data {


    uint8_t c;

    [data getBytes:&c length:1];



    switch (c) {

    case 0xFF:

        return @"image/jpeg";

    case 0x89:

        return @"image/png";

    case 0x47:

        return @"image/gif";

    case 0x49:

    case 0x4D:

        return @"image/tiff";

    }

    return nil;

}

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!


hetaodie

Mobile development

简单,深入的研究移动客户端开发技术"