1 浅谈iOS-Block

对于 block 的语法,只放一张图即可。



ios 开发 block作为返回值 ios __block原理_ios 开发 block作为返回值



**Xcode** 的默认编译器 **clang** ,在学习 **Objective-C** 中的 **block** ,会经常使用的 **clang** 的 **-rewrite-objc** 命令来将 block 的语法转换成**C**语言的 **struct** 结构,从而供我们学习参考。

1.1   Block本质

  • block本质上也是一个OC对象,它内部也有个isa指针
  • block封装了函数调用以及函数调用环境
  • block封装函数及其上下文


ios 开发 block作为返回值 ios __block原理_强引用_02


block底层结构图



struct __block_impl {
     void *isa;
     int Flags;
     int Reserved;    //保留
     void *FuncPtr;    //指向调用函数的地址
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;  // block描述信息
  // 构造函数(类似于OC的init方法),返回结构体对象
  __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执行逻辑的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_c60393_mi_0);
        }
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
        // 定义block变量
        void (*block)(void) = &__main_block_impl_0(
                                                   __main_block_func_0,
                                                   &__main_block_desc_0_DATA
                                                   );	
        // 执行block内部的代码
        block->FuncPtr(block);
    }
    return 0;
}



ios 开发 block作为返回值 ios __block原理_强引用_03



1.2   Block 捕获变量

Q:下述代码输出值为多少?

int age=10;
void (^Block)(void) = ^{
    NSLog(@"age:%d",age);
};
age = 20;
Block();

输出值为 age:10 原因:创建block的时候,已经把age的值存储在里面了。


Q:下列代码输出值分别为多少?

auto int age = 10;
static int num = 25;
void (^Block)(void) = ^{
    NSLog(@"age:%d,num:%d",age,num);
};
age = 20;
num = 11;
Block();

输出结果为:age:10,num:11
原因:auto变量block访问方式是值传递,auto被定义为自动推断变量的类型。static变量block访问方式是指针传递
源码证明:

int age = __cself->age; // bound by copy
int *num = __cself->num; // bound by copy

NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_d2875b_mi_0, age, (*num));

int age = 10;
static int num = 25;

block = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, age, &num));

age = 20;
num = 11;

上述代码可查看 static修饰的变量,是根据指针访问的


Q:为什么block对auto和static变量捕获有差异?
auto自动变量可能会销毁的,内存可能会消失,不采用指针访问;static变量一直保存在内存中,指针访问即可
成员变量存储在对象的堆内存中,静态变量存储在方法区(共享数据区)的静态区. 成员变量随着类对象的建立而建立,随着对象的回收而释放.静态变量随着类的加载而存在,随着类的消失而消失


Q:block对全局变量的捕获方式是?
block不需要对全局变量捕获,都是直接采用取值的


Q:为什么局部变量需要捕获?
考虑作用域的问题,需要跨函数访问,就需要捕获


Q:block的变量捕获(capture)
为了保证block内部能够正常访问外部的变量,block有个变量捕获机制


Q:block里访问self是否会捕获?
会,self是当调用block函数的参数,参数是局部变量,self指向调用者


Q:block里访问成员变量是否会捕获?

会,成员变量的访问其实是self->xx,先捕获self,再通过self访问里面的成员变量

ios 开发 block作为返回值 ios __block原理_API_04

1.3 Block类型

Q:block有哪几种类型?
block的类型,取决于isa指针,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型

Block的类型

Block对象的存储区域

判断Block的类型

Block调用copy操作

NSGlobalBlock

程序的数据区域(.data)

没有访问auto变量

什么也不做

NSStackBlock


访问了auto变量

从栈复制到堆

NSMallocBlock


NSStackBlock调用了copy

引用计数增加

Malloc(memory allocation)

1.4 ARC环境下自动为Block进行copy操作的情况

Q:在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上的几种情况?
1.block作为函数返回值时
2.将block赋值给__strong指针时
3.block作为Cocoa API中方法名含有usingBlock的方法参数时
4.block作为GCD API的方法参数时
1.block作为函数返回值时

typedef void(^Block)(void);
Block test() {
    int age = 10;
    return ^{
        NSLog(@"age=%d", age);
    };
}
int main(int argc, char * argv[]) {
    @autoreleasepool {
       Block block = test();
       block();
       NSLog(@"%@",[block1 class]);    // __NSMallocBlock__
       return 0;
    }
}

2.将block赋值给__strong指针时

void(^block)(void);
void test() {
    int age = 10;
    block = ^{
        NSLog(@"age=%d", age);
    };
    NSLog(@"%@",[block1 class]);     //__NSMallocBlock__
}
int main(int argc, char * argv[]) {
    @autoreleasepool {
        test();
        block();
        return 0;
    }
}

3.block作为Cocoa API中方法名含有usingBlock的方法参数时

NSArray *array = [[NSArray alloc] init];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
    NSLog(@”%d”,idx);
}]

4.block作为GCD API的方法参数时

Dispatch_async(dispatch_get_main_queue(),^{
    NSLog(@”1111”);
};

代码示例

void (^block1)(void) = ^{
    NSLog(@"block1");
};
NSLog(@"%@",[block1 class]);
NSLog(@"%@",[[block1 class] superclass]);
NSLog(@"%@",[[[block1 class] superclass] superclass]);
NSLog(@"%@",[[[[block1 class] superclass] superclass] superclass]);

输出结果:
NSGlobalBlock
NSBlock
NSObject
(null)

上述代码输出了block1的类型,也证实了block是对象,最终继承NSObject 代码展示block的三种类型:

int age = 1;
void (^block1)(void) = ^{
    NSLog(@"block1");
};

void (^block2)(void) = ^{
    NSLog(@"block2:%d",age);
};

NSLog(@"%@/%@/%@",[block1 class],[block2 class],[^{
    NSLog(@"block3:%d",age);
} class]);

输出结果:

__NSGlobalBlock __/__NSMallocBlock __/__NSStackBlock __

Q:各类型的block在内存中如何分配的?
__NSGlobalBlock __ 在数据区
__NSMallocBlock __ 在堆区
__NSStackBlock __ 在栈区
堆:动态分配内存,需要程序员自己申请,程序员自己管理
栈:自动分配内存,自动销毁,先入后出,栈上的内容存在自动销毁的情况



ios 开发 block作为返回值 ios __block原理_API_05



Q:如何判断block是哪种类型?
没有访问auto变量的block是__NSGlobalBlock __ ,放在数据段
访问了auto变量的block是__NSStackBlock __
[__NSStackBlock __ copy]操作就变成了__NSMallocBlock __


Q:对每种类型block调用copy操作后是什么结果?
__NSGlobalBlock __ 调用copy操作后,什么也不做
__NSStackBlock __ 调用copy操作后,复制效果是:从栈复制到堆;副本存储位置是堆
__NSMallocBlock __ 调用copy操作后,复制效果是:引用计数增加;副本存储位置是堆

MRC下block属性的建议写法

@property (copy, nonatomic) void (^block)(void);

ARC下block属性的建议写法

@property (copy, nonatomic) void (^block)(void);

1.4.1:对象类型的auto变量

Q:ARC下述代码中Person对象是否会释放?
示例代码:

typedef void(^XBTBlock)(void);
XBTBlock block;
{
    Person *p = [[Person alloc] init];
    p.age = 10;
    
    block = ^{
        NSLog(@"======= %d",p.age);
    };
}

Person.m
- (void)dealloc{
    NSLog(@"Person - dealloc");
}

输出结果:不会打印Person - dealloc
转化C++代码后:

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

上述
block为堆block,block里面有一个Person指针,Person指针指向Person对象。只要block还在,Person就还在。block强引用了Person对象。


Q:上述代码改换成MRC,Person对象会释放么?
会的!
堆空间的block会对Person对象retain操作,拥有一次Person对象。


Q:下列代码中Person是否会被释放?

@autoreleasepool {
        XBTBlock block;
        
        {
            Person *p = [[Person alloc] init];
            p.age = 10;
            __weak Person *weakPersn = p;
            block = ^{
                NSLog(@"======= %d",weakPersn.age);
            };
        }
        NSLog(@"--------------");
}

答案:会释放
小结
无论MRC还是ARC,栈空间上的block,不会持有对象;堆空间的block,会持有对象。


Q:当block内部访问了对象类型的auto变量时,是否会强引用?
答案:分情况讨论,分为栈block和堆block
栈block
a) 如果block是在栈上,将不会对auto变量产生强引用
b) 栈上的block随时会被销毁,也没必要去强引用其他对象


堆block
1.如果block被拷贝到堆上:
a) 会调用block内部的copy函数
b) copy函数内部会调用_Block_object_assign函数
c) _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用

2.如果block从堆上移除
a) 会调用block内部的dispose函数
b) dispose函数内部会调用_Block_object_dispose函数
c) _Block_object_dispose函数会自动释放引用的auto变量(release)
正确答案:
如果block在栈空间,不管外部变量是强引用还是弱引用,block都会弱引用访问对象
如果block在堆空间,如果外部强引用,block内部也是强引用;如果外部弱引用,block内部也是弱引用


Q:__weak 在使用clang转换OC为C++代码时,可能会遇到以下问题

cannot create __weak reference in file using manual reference
解决方案:支持ARC、指定运行时系统版本,比如
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m


Q:gcd的block中引用 Person对象什么时候销毁?(GCD它是基于C语言的API,开发者只需要将任务放在block内,并指定好追加的队列,就可以完成多线程开发。

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    Person *person = [[Person alloc] init];
    person.age = 10;
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"age:%d",person.age);
    });
    
    NSLog(@"touchesBegan");
}

输出结果:

14:36:03.395120+0800 test[1032:330314] touchesBegan
14:36:05.395237+0800 test[1032:330314] age:10 14:36:05.395487+0800
test[1032:330314] Person-dealloc

原因:gcd的block默认会做copy操作,即dispatch_after的block是堆block,block会对Person强引用,block销毁时候Person才会被释放。
Q:上述代码如果换成__weak,Person什么时候释放?

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    Person *person = [[Person alloc] init];
    person.age = 10;
    
    __weak Person *weakPerson = person;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"age:%p",weakPerson);
    });

    NSLog(@"touchesBegan");
}

输出结果:

14:38:42.996990+0800 test[1104:347260] touchesBegan
14:38:42.997481+0800 test[1104:347260] Person-dealloc
14:38:44.997136+0800 test[1104:347260] age:0x0

原因:使用__weak修饰过后的对象,堆block会采用弱引用,无法延时Person的寿命,所以在touchesBegan函数结束后,Person就会被释放,gcd就无法捕捉到Person。
Q3:如果gcd内包含gcd,Person会什么时候释放?

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    Person *person = [[Person alloc] init];
    person.age = 10;
    
    __weak Person *weakPerson = person;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)),
                   dispatch_get_main_queue(), ^{
                       
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"2-----age:%p",person);
        });
        NSLog(@"1-----age:%p",weakPerson);
    });

    NSLog(@"touchesBegan");
}

输出结果:

14:48:01.293818+0800 test[1199:403589] touchesBegan
14:48:05.294127+0800 test[1199:403589] 1-----age:0x604000015eb0
14:48:08.582807+0800 test[1199:403589] 2-----age:0x604000015eb0
14:48:08.583129+0800 test[1199:403589] Person-dealloc

原因:gcd内部只要有强引用Person,Person就会等待执行完再销毁!所以Person销毁时间为7秒。
Q4:如果gcd内部先强引用后弱引用,Person什么时候释放?

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    Person *person = [[Person alloc] init];
    person.age = 10;
    
    __weak Person *weakPerson = person;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)),
                   dispatch_get_main_queue(), ^{
                       
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"2-----age:%p",weakPerson);
        });
        NSLog(@"1-----age:%p",person);
    });

    NSLog(@"touchesBegan");
}

输出结果

14:52:29.036878+0800 test[1249:431302] touchesBegan
14:52:33.417862+0800 test[1249:431302] 1-----age:0x6000000178d0
14:52:33.418178+0800 test[1249:431302] Person-dealloc
14:52:36.418204+0800 test[1249:431302] 2-----age:0x0

原因:Person会等待强引用执行完毕后释放,只要强引用执行完,就不会等待后执行的弱引用,会直接释放的,所以Person释放时间为4秒。

1.5 __block修饰符

Q:block在修改NSMutableArray,需不需要添加__block?
不需要。

NSMutableArray *arr = [NSMutableArray array];

Block block = ^{
    [arr addObject:@"123"];
    [arr addObject:@"2345"];
};

答案:可以,因为是addObject是使用NSMutableArray变量,而不是通过指针改变NSMutableArray,如果是arr = nil,这就是改变了NSMutableArray变量,会报错。


Q:block能否修改变量值?
auto修饰变量,block无法修改,因为block使用的时候是内部创建了变量来保存外部的变量的值,block只有修改内部自己变量的权限,无法修改外部变量的权限。
static修饰变量,block可以修改,因为block把外部static修饰变量的指针存入,block直接修改指针指向变量值,即可修改外部变量值。
全局变量值,全局变量无论哪里都可以修改,当然block内部也可以修改。


Q:__block int age = 10,系统做了哪些?
答案:编译器会将__block变量包装成一个对象
查看c++源码:

struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;//age的地址
 int __flags;
 int __size;
 int age;//age 的值
};

Q:__block 修饰符作用?
__block可以用于解决block内部无法修改auto变量值的问题
__block不能修饰全局变量、静态变量(static)
编译器会将__block变量包装成一个对象
__block修改变量:age->__forwarding->age
__Block_byref_age_0结构体内部地址和外部变量age是同一地址



ios 开发 block作为返回值 ios __block原理_API_06


__forwarding指针指向



1.5.1 __block的内存管理

当block在栈上时,并不会对__block变量产生强引用
Q:block的属性修饰词为什么是copy?
block一旦没有进行copy操作,就不会在堆上
block在堆上,程序员就可以对block做内存管理等操作,可以控制block的生命周期


Q:当block被copy到堆时,对__block修饰的变量做了什么?
会调用block内部的copy函数
copy函数内部会调用_Block_object_assign函数
_Block_object_assign函数会对__block变量形成强引用(retain)


Q:当block从堆中移除时,对__block修饰的变量做了什么?
会调用block内部的dispose函数
dispose函数内部会调用_Block_object_dispose函数
_Block_object_dispose函数会自动释放引用的__block变量(release)

1.6 block循环引用

Q:ARC下如何解决block循环引用的问题?
三种方式:__weak、__unsafe_unretained、__block
1.第一种方式:__weak

Person *person = [[Person alloc] init];
//        __weak Person *weakPerson = person;
__weak typeof(person) weakPerson = person;

person.block = ^{
    NSLog(@"age is %d", weakPerson.age);
};

2.第二种方式:__unsafe_unretained

__unsafe_unretained Person *person = [[Person alloc] init];
person.block = ^{
    NSLog(@"age is %d", weakPerson.age);
};

3.第三种方式:__block

__block Person *person = [[Person alloc] init];
person.block = ^{
    NSLog(@"age is %d", person.age);
    person = nil;
};
person.block();

4.三种方法比较
__weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil
__unsafe_unretained:不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变
__block:必须把引用对象置为nil,并且要调用该block

Q:MRC下如何解决block循环引用的问题?
两种方式:__unsafe_unretained、__block
1.第一种方式:__unsafe_unretained

__unsafe_unretained Person *person = [[Person alloc] init];
person.block = ^{
    NSLog(@"age is %d", weakPerson.age);
};

2.第二种方式:__block

__block Person *person = [[Person alloc] init];
person.block = ^{
    NSLog(@"age is %d", person.age);
};

在OC中习惯用block来传值,而swift中,block被重新定义了一下,叫闭包;
使用的技巧:谁定义谁传值;

1-定义

格式: typealias 闭包名称 = (参数名称: 参数类型) -> 返回值类型

typealias block = (str: String) -> void
2- 声明
var callBackBlock = block?()
3- 赋值

需要定义一个方法,参数是和block类型一致得闭包,并赋值给block

func callBackFunction (block: (str: String) -> Void){
    callBackBlock = block
}
4- 传值
func buttonClick () {
if callBackBlock != nil{
    callBackBlock!("传这个值给 A")    //注意,这里是使用属性传值,不是方法
}
}
5- A控制器中
B.callBackFunction  { (str) in 
print("这里使用传过来的值") 
}

2 Swift中Block的使用

在OC中用惯了Block,在Swift中,发现没有。所以查找了一下,发现在Swift中,可以用闭包代替OC中的Block。

2.1 闭包的介绍

        闭包是功能性自包含模块,可以在代码中被传递和使用。 Swift 中的闭包与 C 和 Objective-C中的 blocks 以及其他一些编程语言中的 lambdas 比较相似。
        闭包可以捕获和存储其所在上下文中任意常量和变量的引用。 这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。Swift会为您管理在捕获过程中涉及到的内存操作。
OC中的block是匿名的函数
Swift中的闭包是一个特殊的函数
block和闭包都经常用于回调

2.2 .闭包的用法

<1>. 闭包写法总结:
类型:(形参列表)->(返回值)
技巧:初学者定义闭包类型,直接写()->().再填充参数和返回值
值:

{
 (形参) -> 返回值类型 in
//执行代码
};

let b = { (param : Int) -> (Int) in
print(param)
} 
//调用
b(100)

<2>.闭包的简写
如果闭包没有参数,没有返回值,in和in之前的内容可以省略

httpTool.loadRequest({
    print("回到主线程",NSThread.currentThread());
})

尾随闭包写法:
如果闭包是函数的最后一个参数,则可以将闭包写在()后面
如果函数只有一个参数,并且这个参数是闭包,那么()可以不写
// 开发中建议该写法

httpTool.loadRequest{
    print("回到主线程",NSThread.currentThread());
}

<3>.使用闭包代替block,闭包作为参数进行延时回调
定义网络请求的类

// 开发中建议该写法
class HttpTool : NSObject {
    func loadRequest(callBack : () -> () {
        dispatch_async(dispatch_get_global_queue(0,0)){() -> Void in
            print("加载数据",[NSThread.currentThread()])
            dispatch_async(dispatch_get_main_queue(),{() -> Void in
                callBack()
            })
        }
    }
}

进行网络请求,请求到数据后利用闭包进行回调

Override func touchesBegan(touches : Set<UITouch>,withEvent event: UIEvent?){
    //网络请求
    httpTool.loadRequest({() -> () in
        print("回到主线程",NSThread.currentThread())
    })
}

<3>.实例二,闭包的回调传值

//[weak self]:解决循环引用导致的内存泄露
override func touchesBegan(touches : Set<UITouch>,with event: UIEvent?){
    delayMethod {[weak self] (r,e) -> () in
        print("$$$:\(r)\(e)%%%\(String(describing: self))")
    }   //$$$:qwertyuiasdf%%%Optional(<suanfa3.ViewController: 0x7ffaba708060>)
    delayMethod(comletion: {[weak self](r,e) -> () in
        print("******:\(r)\(e) ***********\(String(describing: self))")
    })  //******:qwertyuiasdf ************Optional(<suanfa3.ViewController: 0x7ffaba708060>)
}

//@escaping:逃逸闭包。它的定义非常简单而且易于理解。如果一个闭包被作为一个参数传递给一个函数,并且在函数return之后才被唤起执行,那么这个闭包是逃逸闭包。
func delayMethod(completion: @escaping(_ results: String,_ results: String) -> ()) -> (){
    //开启一个全局异步子线程
    DispatchQueue.global().async{
        Thread.sleep(forTimeInterval: 2.0)
        //回调到主线程
        DispatchQueue.main.async(execute: {
            print(" 主线程更新 UI\(Thread.current)")
            completion("qwertyui","asdf")
        })      
    }
}

<4>.闭包进行两个界面的传值
我们要实现点击第二个界面后,关掉第二个界面,并且传值给第一个界面

1>.首先在第二个界面声明闭包进行操作

class NewViewController : UIViewController {
//声明闭包
typealias lewisCloser = (_ paramOne : String?) -> ()
//定义个变量 类型就是上面声明的闭包
var customCloser : lewisCloser?
    override func touchesBegan(_ touches:Set<UITouch>, with event: UIEvent?){
        if(customCloser != nil) {
              customCloser!("要发给第一个界面的值")
}
self.dismiss(animated: true,completion: nil)
} 
override func viewDidLoad() {
        super.viewDidLoad()
        //Do any additional setup after loading the view.
    }
}

2>.在第一个界面实现闭包,取得要传的值

override func viewDidLoad() {
    let vc = NewViewController()
    //实现闭包
    vc.customCloser = {(cusValue) -> () in
    //cusValue 就是传过来的值
    print("^^^^^^^^^^:\(cusValue)")
}
self.present(vc,animated: true, completion: nil)

以上就是swift中闭包和OC中block的用法比较!

2.2.1 什么是block,用java来解释oc中的block

1.Java代码说明block
就是java中匿名内部类,而匿名内部类的好处是代码更清晰
例如

button btn = new Button().setListener(newListener(){
//  这里写上监听的代码,这样就可以很直观的观察到这个btn 要干啥了
//  例如这里写个发送邮件的代码,这样人们就知道你这按钮是点击后是发送邮件的功能
});

而如果不写这种方法你就需要这么写

class activity implements OnClickListener{
    onCreate(){
        button btn = new Button();
        btn.setListen(this);
    }
    // 这里有很多其他代码如让你感觉很乱,例如其他方法
    …  
    onClick(){
    }
}

2.oc代码说明block

-(IBAction)buttonTapped:(id) sender {
     UIAlertVeiw *alert = [[UIAlert alloc]initWithTitle:@”send email” Message :@“ send it ?”
     Delegate:self
     cancelbutonTitle:@”cancel”
     otherButtonTitle:@”send”,nil] 
    [alert show]
    [alert release]
}
- (void) alertView:(UIAlertView*) alertViewdidiDismissWithButtonIndex:(NSInteger) buttonIndex {
    if(buttonIndex != [alertView cancelButtonIndex]) {
        [self sendTheMail];
    }
}
- (void) sendTheMail{
    //发送邮件代码
}

使用block 的匿名内部方法写

[UIAlertView showAlertViewWithTitle:@"send email"
                      message:@"send it?"
                      cancelButtonTIle:@"cancel"
                      otherButtonTitles:[NSArray arrayWithObjects:@"send",nil]
                      onCompletion:^{
                         //发送邮件代码
                         // ^{} 这个就是block 的实现,由于没有参数输入也就省了,要不就是这样 ^(int i, int j){}
                       }
                       onCancel:^{
                           //退出代码
                       }
]

这样就清楚多了
不需要实现委托(实现接口) delegate:selft
不需要分配和释放对象 [alert release]
不需要明确的显示警告对话框 [alertshow]

2.3 block作用域

和java不同在java匿名内部类里面是不可以访问它外面的代码的,除非你把代码定义为 final
block 不需要定义final,直接用就ok

main(){
    int c = 25;
    ^(int i, int j ) {
        NsLog(@”%i”,c);// 这里直接能打印c变量的值,但不能改变也就是c = 19这里不行
    }
}

如果非要想改也可以用__block 关键字

main(){
  __Block int c = 25;
  ^(int i, int j ) {
     c = 19;
    NSLog(@”%i”,c);// 这里就可以改变c = 19的值了
    }
}
  • 4.block和函数区别

函数不能定义在其他函数体内,block却可以,他就是一段代码块,而不是函数,但是跟函数是很像的