注:本文有部分转载自文顶顶博客园。
SEL:
SEL:全称Selector 表示方法的存储位置。SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去寻找对应的方法地址,找到方法地址后就可以调用方法。SEL这种类型的数据,保存的是方法在内存中的地址。(和C语言中的函数指针类似)。
方法在内存中是怎么存储的?
Person *p=[[Person alloc] init];
[p test];
寻找方法的过程:
(1)首先把test这个方法名包装成sel类型的数据;
(2)根据SEL数据找到对应的方法地址;
(3)根据方法地址调用相应的方法。
(4)注意:在这个操作过程中有缓存,第一次找的时候是一个一个的找,非常耗性能,之后再用到的时候就直接使用。
关于_cmd:每个方法的内部都有一个-cmd,代表着当前方法。
注意:SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去寻找对应的方法地址,找到方法地址后就可以调用方法。这些都是运行时特性,发消息就是发送SEL,然后根据SEL找到地址,调用方法。
SEL sel = @selector(crash);
[man performSelector:sel];
[man performSelector:@selector(setName:) withObject:@"Tom"];
【注】performSelector:最多支持两个参数
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[vc1 performSelector:NSSelectorFromString(@"cellMoveLeft")];
#pragma clang diagnostic pop
//在ARC(自动内存管理)的条件下,使用选择器,很可能会报警
//可照如上方式去除报警
SEL sel = @selector(run);
//这个方法用于编译时就能确定调用的方法
SEL sel = NSSelectorFromString(@"run");
//这个方法用于运行时确定调用的方法
(一)简介
BLOCK是什么?苹果推荐的类型,效率高,在运行中保存代码。用来封装和保存代码,有点像函数,BLOCK可以在任何时候执行。
BOLCK和函数的相似性:(1)可以保存代码(2)有返回值(3)有形参(4)调用方式一样。
标识符 ^
一般C语言函数:(int) add (int a,int b)
block声明:int(^block名)( int, int) 格式:返回值类型( ^ block的名字 )(参数类型1,参数类型2 ,……)
block的定义(类似于函数的实现): ^ ( int a,int b){
return 返回值类型;
}
(二)基本使用
(1)定义BLOCK变量
Int (^SumBlock)(int,int);//有参数,返回值类型为int
Void (^MyBlock)();//无参数,返回值类型为空
(2)利用block封装代码
(3)Block访问外部变量
1)Block内部可以访问外部变量;
2)默认情况下,Block内部不能修改外部的局部变量
3)给局部变量加上__block关键字,则这个局部变量可以在block内部进行修改。
(4)利用typedef定义block类型(和指向函数的指针很像)
Typedef int(^MyBlock)(int ,int);
以后就可以利用这种类型来定义block变量了。
MyBlock a,b;
a=^(int a,int b){return a-b;};
MyBlock b2=^(int n1,int n2){return n1*n2;};
————————————————————————————————————————————
————————————————————————————————————————————
以下是block 的深入研究:
以后就可以利用这种类型来定义block变量了。
MyBlock a,b;
a=^(int a,int b){return a-b;};
MyBlock b2=^(int n1,int n2){return n1*n2;};
javaScript是一直弱变量语言。即声明变量的时候,不用指明变量的类型,只用关键字var即可。
匿名函数:
实际是就是一个没有名字的函数,它只有内容(也叫做body),我们可以它存在一个变量之中,以便以后使用,或者将其当做一个参数,传递给另外一个函数使用。
javaScript:
function eat ( food )
{
food();
}
<pre name="code" class="javascript">//<span style="color:#ff0000;">创建一个变量,保存匿名函数</span>
var fish=<span style="font-family: Arial, Helvetica, sans-serif;"> function()</span><pre name="code" class="javascript"> {
alert("fish");
};
eat(fish);
</pre><pre name="code" class="javascript"><span style="color: rgb(204, 0, 0);">//创建一个匿名函数,将其当做参数传递到eat中</span>
//那么我们可以直接这样使用eat//普通调用 eat(12);//匿名函数传参调用:eat{ function() { alert("fish"); }};
</pre><pre name="code" class="javascript">//当我们调用eat这个函数时,就会弹出fish;
闭包性:允许一个函数( 这个函数是指匿名函数)访问其所声明的上下文中的变量,甚至实在不同的运行上下文中。
javascript:
function eat( food )
{
alert ( food() );
}
function fish()
{
var str='fish';
//创建一个匿名函数,并将其作为参数传递到eat中
eat(
function()
{
return str;//注意,此时我们的str并没在这个匿名函数中声明,而是在它的上文中(既是fish中声明的),但是我们依然可以在匿名函数中访问到str
}
);
}
<pre name="code" class="javascript">//调用fish
fish();
//弹出fish
OC 为了能够使用 匿名函数,(匿名函数拥有) 闭包性 ,引入了一个叫block的概念,但其原理和脚本语言中的一样,(换句话说block就是脚本语言中的匿名函数)。因为OC是强类型编译语言,所以block使用起来,和脚本语言中有些不一样。因此OC为block制定了一些语法。[注:在纯C和C++中block也是可以使用的]
既然OC中的Block就是脚本语言中的匿名函数,那么它应该也就和脚本语言中的匿名函数的功能一样(至少是大致上相同)。
01 匿名函数可以将其保存到一个变量中,这个在OC中就是如下这种方式体现:
在标准C函数中,定义block(匿名函数)之前需要先声明block原型。
标示符 ^
block (匿名函数) 的原型:
一般格式:返回值类型 ( ^ block函数名) ( 参数类型1,参数类型2,…… );
如:void (^blcok1)(); //无返回值,无参数的block
NSString *( ^myBlock)( int );
这个block的名字叫做myblock,有一个参数是int类型,它有一个返回值,是NSString*类型
block (匿名函数) 的定义:
一般形式:block名=^返回值类型(参数类型1 参数值,参数类型2 参数值){ ……代码块…… ,return 返回值类型};//注意最后有分号
如:来实现myblock的定义:
myblock=^(int a) { return [NSString stringWtihFormat:@"hello world %d",a]; };
使用myblock: myblock(5); //就会返回"hello word 5";
02 匿名函数可以作为一个参数,传递到其他函数,在OC中如下方式体现:
对象方法:
- ( void) logBlock( NSString *( ^myBlock)( int ) );
实现方法
-(void) logBlock (NSString *( ^myBlock)( int ) )
{
NSLog(@" the block is %@",myBlock);
}
调用方法
logBlock( myBlock(5) );//打印出 the block is hello world 5
//03 匿名函数有闭包性,在匿名函数中可以访问其所声明的上下文中的变量,甚至实在不同的运行上下文中。 在OC中的体现方式如下:
<span style="font-size:14px;">
void logBlock (int (^numBlock)() )
{
NSLog(@"%d",numBlock);
}
int main()
{
int x;
//声明一个block变量
int ^(numBlock)();
x=10;
//定义一个block
numBlock=^()
{
return x;
};
//调用logBlock
logBlock(numBlock); //打印出10;
}</span>
总结下,block下变量的访问权限:
- 相同作用域下,局部变量在block内是只读的
- 相同作用域下,带__block修饰的变量,在block内是可读写的
- 静态变量和全局变量在block内是可读写的
注: block(匿名函数)在默认是只可访问上下文中的变量,但不可修改。可以使用关键字__block( 双下划线 )修饰变量,即可在block中对变量进行修
首先来看一个使用block回调和代理回调的例子:
VC2中定义一个属性,是block。
vc2.h
#import <UIKit/UIKit.h>
@interface AZSecondViewController : UIViewController
@property (nonatomic,strong)void(^blockFun)(NSString *);//声明一个block变量的属性,定义这个变量的时候在rootVC中的定义,这个block的名字是blcokFun
@end
self.blockFun(@"使用block");//定义了一个快函数,这里只是调用一下块函数,实现在rootVC中。
vc1.h
#import <UIKit/UIKit.h>
#import "AZSecondViewController.h"
@interface AZViewController : UIViewController<AZSecondViewControllerDelegate>
@end
AZSecondViewController *secondVC=[[AZSecondViewController alloc] init];
//使用BLOCK回调
//实现secondVC中的块函数
secondVC.blockFun=^(NSString *title)
{
self.navigationItem.title=title;
};
在VC1中 对VC2中的block变量 进行定义(赋值给一个代码块到VC2中的block),然后再VC2中调用blcokFun。
self.blockFun(@"使用block");
那么就可以做到给VC1中的self.navigateionItem.title进行赋值。这个过程叫做block回调。
它的原理:因为block可以访问上下文中的变量,全局变量在block中是可以被读写的,那么self.navigateionItem.title就可以被blcok进行读写,但是要清楚一点,虽然我们在VC1中对VC2的blick属性进行了定义,但是我们是在VC2中才调用的block,那么这段block里的代码是在VC2中才被实现的。
从内存管理上看Block:
从C语言上来看,Block其实就是一个结构体,结构体存储了不同类型的变量(代码块),既然是一个结构体类型,那么它创建出的变量就可以被拷贝和销毁。
在OC中,block可以接受retain、release、copy等,这和普通对象一样。而OC中的block使用,大都是定义了一个block变 量,然后再这个变量中存放代码块,既然是变量,所以block大都是存在栈区上,而栈区是不能被有效的控制它什么时候被弹栈 的(销毁)。所以当我们使用retain的时候,是先调用了copy在堆区上创建一个block,将栈区上得block变量深拷贝到堆区上。 用于达到长期保留block的目的。
以上是个人观点,欢迎指正!