赞
踩
块类似于匿名函数或闭包,在许多其他编程语言中也存在类似的概念。
可以访问上下文,运行效率高
以下是块的一些基本知识:
{}
包围的代码片段,可以包含一段可执行的代码。块的定义使用 ^
符号,并可以带有参数列表和返回类型。例如:^{
// 代码块的内容
}
typedef
来定义块的类型。例如:typedef returnType (^BlockTypeName)(parameterTypes);
其中 returnType
是块的返回类型,BlockTypeName
是块的类型名称,parameterTypes
是块的参数类型。
3. 块的赋值和调用:块可以赋值给变量,并且可以像函数一样进行调用。可以使用 =
运算符将块赋值给变量,然后使用该变量调用块。例如:
ReturnType (^blockName)(ParameterTypes) = ^ReturnType (Parameters) {
// 块的内容
};
blockName(argumentValues); // 调用块
NSInteger outsideVariable = 10;
void (^block)(void) = ^{
NSLog(@"Outside variable: %ld", (long)outsideVariable);
};
block(); // 输出:Outside variable: 10
在这个例子中,块捕获了外部的 outsideVariable
变量,并在块内部访问它。
默认情况下,为块所捕获的变量是不可以在块里面修改的,如果修改了outsideVariale的值,就会报错,声明变量的时候可以加上__block修饰符,这样就可以在块内修改了
外部变量如果是数组那种的话是可以使用方法来给数组添加内容的,因为此举没有改变对象原本的地址
- (void)performOperationWithCompletion:(void (^)(void))completionBlock {
// 执行操作
// 操作完成后调用块
completionBlock();
}
// 调用方法,传递块作为参数
[self performOperationWithCompletion:^{
NSLog(@"Operation completed!");
}];
在这个例子中,performOperationWithCompletion:
方法接受一个块作为参数,并在操作完成后调用该块。
#pragma mark **--修改为块所捕获的变量** NSArray *array = @[@0, @1, @2, @3, @4, @5]; **__block** NSInteger count = 0; //块作为方法的参数 [array enumerateObjectsUsingBlock:^(NSNumber *number, NSUInteger idx, **BOOL** *stop) { **if** ([number compare:@2] == NSOrderedAscending) { count++; } NSLog(@"%ld====%@",(**unsigned** **long**)idx, number); }]; NSLog(@"%ld", (**long**)count); //内联块的用法,传给“numberateObjectsUsingBlock:"方法的块并未先赋给局部变量,而是直接在内联函数中调用了,如果块所捕获的变量类型是对象类型的话,那么就会自动保留它,系统在释放这个块的时候,也会将其一并释放。这就引出了一个与块有关的重要问题,块本身可以视为对象,在其他oc对象能响应的选择子中,很多块也可以响应,最重要的是,块本身也会像其他对象一样,有引用计数,为0时,块就回收了,同时也会释放块所捕获的变量,以便平衡捕获时所执行的保留操作 //如果块定义在oc类的实例方法中,那么除了可以访问类的所有实例变量之外,还可以使用self变量,块总能修改实例变量,那么除了声明时无需添加__block。不过,如果通过读取或写入操作捕获了实例变量,那么也会自动把self给捕获了,因为实例变量是与self所指代的实例关联在一起的。 // 需要注意的是(self也是一个对象,也会被保留),如果在块内部使用了实例变量,块会自动对self进行保留操作,以确保在块执行期间保持对象的有效性。但是,如果在块内部直接使用了self,并对其进行读取或写入操作,那么self也会被捕获,从而导致循环引用的问题。为了避免循环引用,可以在块内部使用__weak修饰符来避免对self进行保留操作。
如果某个实例在执行anInstanceMethod放法,那么self变量就会指向此实例。由于块里没有明确使用self变量,所以很容易就会忘记self变量其实也为块所捕获了。直接访问实例遍历和通过self来访问时等效的:
self->_anInstanceVariable = @“someThing”;
typedef void(^SomeBlock) (void);
@property (nonatomic, copy) BlockName someBlock;
`- (void)anInstanceMethod {
self.someBlock = ^ {
_anInstanceVariable = @"someThing";
}
}
self也是个对象,因而块在捕获它时也会将其保留。如果self所指代的那个对象同时也保留了块,那么这种情况就会导致“保留环”
修改为以下代码即可:
typedef void(^SomeBlock) (void);
@property (nonatomic, copy) BlockName someBlock;
__weak typeof(self)weakSelf = self;
`- (void)anInstanceMethod {
self.someBlock = ^ {
weakSelf.anInstanceVariable = @"someThing";
}
}
带有自动变量(局部变量)的匿名函数。
isa
指针为了保证block内部能够正常访问外部的变量,block有个变量捕获机制
block变量与c语言变量完全相同,可以作为以下用途:
block的类型,取决于isa指针,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
__ NSGlobalBlock __ ( _ NSConcreteGlobalBlock ) 对象存储在数据区
__ NSStackBlock __ ( _ NSConcreteStackBlock ) 对象存储在栈区
__ NSMallocBlock __ ( _ NSConcreteMallocBlock )对象存储在堆区
捕获了自动变量block就是栈类型
没有捕获就是数据区
不存在一创建就在堆区的,堆区的意义可以理解为和autorelease
一样:延长作用域 Stack
类型的Block进行了copy
操作之后变成了堆区
如果一个block没有访问外部局部变量,或者访问的是全局变量,或者 静态局部变量,此时的blcok就是一个全局block,并且储存在全局区
- (**void**)NSGlobalBlock { //block1没有引用到局部变量 **int** a = 10; **void** (^block)(**void**) = ^{ NSLog(@"hello world"); }; NSLog(@"block:%@", block); // block2中引入的是静态变量 **static** **int** a1 = 20; **void** (^block1)(**void**) = ^{ NSLog(@"hello - %d",a1); }; NSLog(@"block:%@", block); } - (**void**)NSStackBlock { **__block** **int** a = 10; **static** **int** a1 = 20; **void** (^**__weak** block)(**void**) = ^{ NSLog(@"hello - %d",a); NSLog(@"hello - %d",a1); }; NSLog(@"block:%@", block); } - (**void**)NSMallocBlock { **int** a = 10; **void** (^block1)(**void**) = ^{ NSLog(@"%d",a); }; NSLog(@"block1:%@", block1); **__block** **int** b = 10; **void** (^block2)(**void**) = ^{ NSLog(@"%d",b); }; NSLog(@"block2:%@", block2); } //block继承与nsobject - (**void**)blockFromNSObject { **void** (^block1)(**void**) = ^{ NSLog(@"block1"); }; NSLog(@"%@",[block1 class]); NSLog(@"%@",[[block1 class] superclass]); NSLog(@"%@",[[[block1 class] superclass] superclass]); NSLog(@"%@",[[[[block1 class] superclass] superclass] superclass]); NSLog(@"%@",[[[[[block1 class] superclass] superclass] superclass] superclass]); }
block1
的类型,也证实了block
是对象,最终继承NSObject
- (**void**)diffStackAndHip { **__block** **int** a = 10; **__block** **int** b = 20; NSLog(@"a:%p---b:%p", &a, &b); **void** (^**__weak** block)(**void**) = ^{ NSLog(@"hello - %d---%p",a, &a); a++; }; **void** (^block1)(**void**) = ^{ NSLog(@"hello - %d---%p",b, &b); b++; }; block(); block1(); NSLog(@"block:%@---block1:%@", block, block1); NSLog(@"a:%d---b:%d", a, b); NSLog(@"a:%p---b:%p", &a, &b); }
block
的地址是在栈区,而block1
的地址是在堆区,而栈block
引用的变量a
的地址并没有变化,而堆block1
引用的变量b
的地址也相应变成了堆区`0x6,并且后面使用的b的地址都是堆区上的。- (**void**)diffStackAndHip2 { NSObject *objc = [NSObject new]; NSLog(@"%@---%ld",objc, CFGetRetainCount((**__bridge** CFTypeRef)(objc)));// 1 // block 底层源码 // 捕获 + 1 // 堆区block // 栈 - 内存 -> 堆 + 1 **void**(^strongBlock)(**void**) = ^{ // 1 - block -> objc 捕获 + 1 = 2 NSLog(@"%@---%ld",objc, CFGetRetainCount((**__bridge** CFTypeRef)(objc))); }; strongBlock(); **void**(^**__weak** weakBlock)(**void**) = ^{ // + 1 NSLog(@"%@---%ld",objc, CFGetRetainCount((**__bridge** CFTypeRef)(objc))); }; weakBlock(); **void**(^mallocBlock)(**void**) = [weakBlock copy]; mallocBlock(); }
奇怪为什么堆区block
里面的对象引用计数加2呢?而后面的mallocBlock
只加1呢?
首先objc在strongBlock里面必然会拷贝一份到堆区,所以会加1,但是他是从当前函数的栈区拷贝吗?并不是,对于堆区Block一开始编译时是栈block这时候objc对象地址拷贝了一份引用计数加1,后面从栈block变成堆block,又拷贝了一份引用计数又加1,所以这时候是3,weakBlock是栈block仅拷贝了一份,所以引用计数加1,这时候是4,mallocBlock从weakblock拷贝了一份,所以引用计数再加1,这时候是5,相当于strongBlock = weakblock + void(^mallocBlock)(void) = [weakBlock copy];
- (**void**)diffStackAndHip3 { NSObject *a = [NSObject alloc]; NSLog(@"1---%@--%p", a, &a); **void**(^**__weak** weakBlock)(**void**) = **nil**; { // 栈区 **void**(^**__weak** strongBlock)(**void**) = ^{ NSLog(@"2---%@--%p", a, &a); }; weakBlock = strongBlock; strongBlock(); NSLog(@"3 - %@ - %@",weakBlock,strongBlock); } weakBlock(); NSLog(@"4---%@--%p", a, &a); }
strongBlock
的赋值给外面的栈区weakBlock
,因为都是存放在栈空间的,只有当前函数结束才会被销毁,随意这边weakBlock
调用并不会有什么问题。如果换成堆区block
就不一样了。- (**void**)diffStackAndHip4 { NSObject *a = [NSObject alloc]; NSLog(@"1---%@--%p", a, &a); **void**(^**__weak** weakBlock)(**void**) = **nil**; { // 栈区 **void**(^**__strong** strongBlock)(**void**) = ^{ NSLog(@"2---%@--%p", a, &a); }; weakBlock = strongBlock; strongBlock(); NSLog(@"3 - %@ - %@",weakBlock,strongBlock); } // weakBlock();//测试的时候取消注释 NSLog(@"4---%@--%p", a, &a); }
strongBlock
出了大括号就会被销毁,此时你去调用这个block
就会崩溃weakBlock
为什么也是__NSMallocBlock__
,其实weakBlock
相当于是指针,此时指向的是一个堆上的内存所以是__NSMallocBlock__
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。