iOS中几种通信方式

Block

1.block是带有局部变量的匿名函数。

 ^(int event){
 
        DLog(@"event is %d",event);
    };

Block语法的BN范式:

^ 返回值类型 参数列表 表达式 (返回值类型和参数列表都是可以省略的)

省略返回值类型

^ (int count) {return conut +1;}

省略参数列表

^ void {printf("Blocks\n");}

2.声明Block类型的变量:

int (^blk) (int);

Block类型的变量与一般C语言变量作用相同:

  • 赋值

  • 传参

  • 返回值

使用Block语法将Block赋值给Block类型的变量(Block也指由Block语法生成的值)。

int (^blk) (int) = ^ (int count) {return count +1;};

也可以将Block类型的变量赋值给Block类型的变量。

int (^blk1) (int) = blk;
int (^blk2) (int);
blk2 = blk1;

因为Block类型的变量的记述方式比较复杂,所以我们一般使用typedef来重命名一下:

typedef int (^blk_t) (int);

Block是OC对象

捕获变量

block可以捕获在block之前声明的变量,但是仅仅是copy了一份,在block中修改变量,并不会影响原始变量的值。也就是说block不能修改变量的值,除非使用__block修饰符。

捕获变量,就是将变量保存到Block的结构体实例中(copy了一份)。

C语言中,一个变量是可以直接被block捕获并修改的:

  • 静态变量

赋值给捕获变量的话,编译器会报错,使用变量的话不会报错

使用C语言的数组时要使用其指针,因为block捕获变量的方法并不能直接捕获C语言的数组

const char *text = @"hellow";
void (blk){
   printf("%c\n,text[2]");
};

Block的存储位置

Block的类:

  • _NSConcreteStackBlock (栈)
  • _NSConcreteGlobalBlock (数据区)
  • _NSConcreteMallocBlock (堆)

声明在数据区的block,不能捕获变量。只要block不捕获变量,就可以将其声明在数据区。

声明在栈上的block,如果超出了作用域,那么block会废弃。所以使用的时候,将其copy到堆上面,即使超出了作用域,block还可以继续存在。如

  • block作为函数返回值返回时
  • 作为传递的参数时
  • Cocoa框架中的某些方法,比如数组的块枚举遍历
  • GCD
  • 调用copy方法
  • 作为属性或者赋值给 _strong id类型

dispose函数负责废弃堆上的block。

将block从栈上复制到堆上是相当消耗CPU的

不论block声明在哪里,都可以copy,所以在不确定时调用copy来确保block被复制到堆上

__block变量

__block变量一开始也是被声明到栈上的,会随着block被copy到堆上。并且符合内存管理原则,如果有block持有它,引用计数就会增加,反之减少。

因为__block变了在被copy到堆上之后,之前的变量会改变指针,也指向堆上的变量地址,所以不论是block里,还是block外的,修改的都是同一个变量。这也就是为什么我们要用__block修饰符,因为我们希望在block中修改这个变量。

只有copy到堆上的block才会将捕获的变量变为_stong类型,这样会强引用变量,使其不会过早地被释放。如果没有调用copy方法的话,是没有效果的。

循环引用

参考另一篇文章中讲到的block:ARC中的Block


block使用前要赋值,不然调用的话会产生野指针,程序崩溃。

Delegate

实现代理主要有3个部分:

(1)协议:指定代理的内容,即要做什么

(2)委托者:根据协议,让代理者实现功能

(3)代理者:根据协议,实现功能

协议:协议一般是方法列表,不过也可以定义属性

协议也是可以被继承的,A类的子类也可以实现协议内容。

协议可以多继承(对象不可以),但协议只声明方法,不实现方法,方法由代理者去实现。

协议有两个修饰符@optional(可选)和@required(必须,不实现会崩溃)。一般都会判断代理者是否现实这个方法(能否相应这个方法)。同理,block使用前也需要判断block是否存在,已经被赋值,不然的话也会崩溃。

if ([self.delegate respondsToSelector:@selector(userLoginWithUsername:password:)]) {
    [self.delegate userLoginWithUsername:self.username.text password:self.password.text];
}
代理的原理

实际上委托方是用一个id类型的指针弱引用了代理方,其实是向id这个指针指向对象发送消息。

设置delegate属性就是设置id指针指向代理者。

像上面的self.delegate就是代理者,如果它没有实现userLoginWithUsername方法,会崩溃(因为userLoginWithUsername就是它自己的方法)。

代理的内存管理

delegate属性使用weak,如果使用strong的话,会出现循环引用(委托者通过delegate强引用代理者,代理者又强引用创建了委托者)。

一个对象被释放后,weak会自动将指针指向nil,而assign则不会。在iOS中,向nil发送消息时不会导致崩溃的,所以assign就会导致野指针的错误unrecognized selector sent to instance。

所以修饰delegate的话,使用weak,不要使用assign。

非正式协议

非正式协议一般都是以NSObject的Category的方式存在的。由于是对NSObject进行的Category,所以所有基于NSObject的子类,都接受了所定义的非正式协议。对于@Protocol来说编译器会在编译期检查语法错误,而非正式协议则不会检查是否实现。

单例对象最好不要用delegate。单例对象由于始终都只是同一个对象,如果使用delegate,就会造成我们上面说的delegate属性被重新赋值的问题,最终只能有一个对象可以正常响应代理方法。

NSNotification

我感觉适用于跨层和跨页面专递消息。

使用了观察着模式:

(1)在NSNotificationCenter中注册观察者,关注某个事件

[[NSNotificationCenter defaultCenter] addObserver:self
                                          selector:@selector(execute:)
                                             name:@"NOTIFICATION_NAME"
                                            object:nil];

(2)发送通知

[[NSNotificationCenter defaultCenter] postNotificationName:@"NOTIFICATION_NAME" object:nil];

(3)收到通知后执行相应的方法

- (void)execute:(NSNotification *)notification {
     //do something when received notification
     //notification.name is @"NOTIFICATION_NAME"
     if(notification.object && [notification.object isKindOfClass:[Test class]]){
         //do something
     }
}

(4)如果不需要通知了,要移除

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"NOTIFICATION_NAME" object:nil];

KVC、KVO

KVC

key-value-code: 间接访问对象的属性,而不是通过调用存取方法,非对象类型的变量将被自动封装或者解封成对象,很多情况下会简化程序代码;

直接修改属性的值:

[laughingSir setValue:@"laughing 哥" forKey:@"name"];

缺点:编译器无法检测出错误;效率低;必须先解析字符串,然后再设置或者访问对象的实例变量。

使用场景:

(1)apple 官网的一个例子,当我们需要统计很多People的时候,每一行是一个人的实例,并且有2列属性,name, age, 这时候我们可以会这样做,

- (id)tableView:(NSTableView *)tableview 
      objectValueForTableColumn:(id)column row:(NSInteger)row { 
  
    People *people = [peoleArray objectAtIndex:row]; 
    if ([[column identifier] isEqualToString:@"name"]) { 
        return [people name]; 
    } 
    if ([[column identifier] isEqualToString:@"age"]) { 
        return [people age]; 
    } 
    // And so on. 
} 

同样我们也可以用KVC,帮助我们化简这些if, 因为name, age其实都是property, 我们可以直接通过key来访问,所以整理过后是

People *people = [peopleArray objectAtIndex:row]; 
return [people valueForKey:[column identifier]]; 

(2)解析服务端返回的json字符串的时候,会用到KVC

[self setValuesForKeysWithDictionary:jsonObject];

不过现在解析json都用MJ的库了。

(3)修改数组中元素的属性,如把一个数组里的People的名字的首字母大写,并且把新的名字存入新的数组。

  NSArray *newName = [self.people valueForKeyPath:@"name.capitalizedString"];

    Person *one = self.people[0];

    Person *two = self.people[1];

    one.name = newName[0];

    two.name = newName[1];

为什么用valueForKeyPath, 不用valueForKey, 因为valueForKeyPath可以传递关系,例如这里是每个People的name property的String的capitalizedString property, 而valueForKey不能传递这样的关系,所以对于dict里面的dict, 我们也只能用valueForKeyPath。

KVC原理

当通过KVC调用对象时,比如:[self valueForKey:@”someKey”]时,程序会自动试图通过下面几种不同的方式解析这个调用

  • 首先查找对象是否带有 someKey 这个方法

  • 会继续查找对象是否带有someKey这个实例变量(iVar)

  • 程序会继续试图调用 -(id) valueForUndefinedKey:这个方法

  • 程序会抛出一个NSUndefinedKeyException异常错误。

补充:KVC查找方法的时候,不仅仅会查找someKey这个方法,还会查找getsomeKey这个方法,前面加一个get,或者_someKey以_getsomeKey这几种形式。同时,查找实例变量的时候也会不仅仅查找someKey这个变量,也会查找_someKey这个变量是否存在。设计valueForUndefinedKey:方法的主要目的是当你使用-(id)valueForKey方法从对象中请求值时,对象能够在错误发生前,有最后的机会响应这个请求。

KVO

key-value-observer :监听property的变化。

(1)添加观察者:

[self.yiTian addObserver:self forKeyPath:@"narcotics" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:nil];

(2)截获变化:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
    
    if([keyPath isEqualToString:@"narcotics"]){
        
        NSNumber *narcoticsN = [change objectForKey:@"new"];//修改之后的最新值
        NSInteger narcotics = [narcoticsN integerValue];
        if (narcotics>0) {
            if (self.delegate!=nil&&[self.delegate respondsToSelector:@selector(reportYitian:)]) {
                [self.delegate reportYitian:narcotics];
            }
        }
    }
}

(3)移除观察者:

- (void)dealloc{
    //移除观察者
    [self.yiTian removeObserver:self forKeyPath:@"narcotics"];
}

--

NSNotification、Block、Delegate和KVO的区别。

代理是一种回调机制,且是一对一的关系,通知是一对多的关系,一个对向所有的观察者提供变更通知;

效率:Delegate比NSNOtification高;

Delegate和Block一般是一对一的通信;

Delegate需要定义协议方法,代理对象实现协议方法,并且需要建立代理关系才可以实现通信;

Block:Block更加简洁,不需要定义繁琐的协议方法,但通信事件比较多的话,建议使用Delegate;

引用文章:
iOS面试必看,最全梳理
iOS KVC & KVO

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,527评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,314评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,535评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,006评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,961评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,220评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,664评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,351评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,481评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,397评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,443评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,123评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,713评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,801评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,010评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,494评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,075评论 2 341

推荐阅读更多精彩内容