SEL(去除警告)、Block(block深入研究、block回调--匿名函数)

注:本文有部分转载自文顶顶博客园。 

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是什么?苹果推荐的类型,效率高,在运行中保存代码。用来封装和保存代码,有点像函数,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类似于C语言的函数指针,这种说法是不正确的。

block中并不是存储的是某一个函数的地址,而是将整个函数的代码块都保存到了block中。更类似于C语言中的结构体。使用typedef定义之后的block,就更像一个C语言中的结构体类型,即此时的block是一个数据类型,可以用来定义变量,这和 int a 一个道理,例如:

       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;};


深入之前,先来来脚本语言中的匿名函数闭包性。如:最常用的javaScript

       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下变量的访问权限:

  1. 相同作用域下,局部变量在block内是只读的
  2. 相同作用域下,带__block修饰的变量,在block内是可读写的
  3. 静态变量和全局变量在block内是可读写的  

注: block(匿名函数)在默认是只可访问上下文中的变量,但不可修改。可以使用关键字__block( 双下划线 )修饰变量,即可在block中对变量进行修
改。

blcok--回调:其实就是利用了闭包性和匿名函数的特性


首先来看一个使用block回调和代理回调的例子:

VC2中定义一个属性,是block。

vc2.h

#import <UIKit/UIKit.h>

@interface AZSecondViewController : UIViewController

@property (nonatomic,strong)void(^blockFun)(NSString *);//声明一个block变量的属性,定义这个变量的时候在rootVC中的定义,这个block的名字是blcokFun


@end

vc2.m
    self.blockFun(@"使用block");//定义了一个快函数,这里只是调用一下块函数,实现在rootVC中。


VC1中:

vc1.h
#import <UIKit/UIKit.h>
#import "AZSecondViewController.h"
@interface AZViewController : UIViewController<AZSecondViewControllerDelegate>

@end


vc1.m
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的目的。




以上是个人观点,欢迎指正!





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值