// // Person.h #import <Foundation/Foundation.h> typedef void (^myBlock)(); @interface Person : NSObject //@property (nonatomic, retain) NSString *name; @property (nonatomic, copy) NSString *name; // 注意: 如果是block使用copy并不是拷贝, 将pBlock所指向的代码块从栈转移到堆中。block在堆中,使用外界对象的时候,会对外界对象的计数器加1, @property (nonatomic, copy) myBlock pBlock; //@property (nonatomic, retain) myBlock pBlock; @end
// // Person.m #import "Person.h" @implementation Person - (void)dealloc { // 由于block使用外界对象会对里面的对象加1,因此要在Person释放的时候把里面使用的对象也释放。 // 只要给block发送一条release消息, block中使用到的对象d也会收到该消息。 Block_release(_pBlock); NSLog(@"%s", __func__); [super dealloc]; } @end
// Dog.h #import <Foundation/Foundation.h> @interface Dog : NSObject @end
// Dog.m #import "Dog.h" @implementation Dog - (void)dealloc { NSLog(@"%s", __func__); [super dealloc]; } @end
// main.m // Copy与string,block的结合使用 #import <Foundation/Foundation.h> #import "Person.h" #import "Dog.h" int main(int argc, const char * argv[]) { // 1.copy的第一个用途, 防止外界修改内部的数据 NSMutableString *temp1 = [NSMutableString stringWithFormat:@"lnj"]; Person *p1 = [[Person alloc] init]; p1.name = temp1; //p1.name是一个新的对象, // 问题: 修改了外面的变量, 影响到了对象中的属性,记住: 以后字符串属性都用copy [temp1 appendString:@" cool"]; NSLog(@"name = %@", p1.name);//lnj __block int num = 10;//block的本质是传了指针。才能修改外面的变量。 void (^myBlock1)() = ^{ num = 20; NSLog(@"%i", num); }; myBlock1();// 20 // block默认存储在栈中, 栈中的block访问到了外界的对象, 不会对对象进行retain // block如果在堆中, 如果在block中访问了外界的对象, 会对外界的对象进行一次retain Person *p2 = [[Person alloc] init]; NSLog(@"retainCount = %lu", [p2 retainCount]);//1 void (^myBlock)() = ^{ NSLog(@"%@", p2); NSLog(@"retainCount = %lu", [p2 retainCount]); }; myBlock();// retainCount = 1 Block_copy(myBlock); // 将block转移到堆中 myBlock();// retainCount = 2 // 2.可以使用copy保存block, 这样可以保住block中使用的外界对象的命 // 避免以后调用block的时候, 外界的对象已经释放了 /*__block*/ Dog *d = [[Dog alloc] init]; // 1 NSLog(@"Dog retainCount = %lu", [d retainCount]);// 1 Person *p3 = [[Person alloc] init]; p3.pBlock = ^{ //2,block在堆中,使用外界对象的时候,会对d的计数器加1,Dog前面加了__block,block里面使用dog也不会对dog加1, NSLog(@"%@", d); }; NSLog(@"Dog retainCount = %lu", [d retainCount]); // 2 // 如果狗在调用block之前释放了, 那么程序就会崩溃 [d release]; // 1 p3.pBlock(); [p3 release]; // 3.注意点: copy block之后引发循环引用。 // 如果对象中的block又用到了对象自己, 那么为了避免内存泄露, 应该将对象修饰为__block。 __block Person *p = [[Person alloc] init]; // 1 p.name = @"lnj"; NSLog(@"retainCount = %lu", [p retainCount]); p.pBlock = ^{//由于是copy,所以这段代码在堆中, NSLog(@"name = %@", p.name); // 2,由于内部用到了外部对象p,对p加1,所以p为2,所以p前面要加__block,block里面就不会对外部对象加1, }; NSLog(@"retainCount = %lu", [p retainCount]); p.pBlock(); [p release]; // 1,p始终是1,无法释放。 [p release]; // 2B return 0; }
3的图片内存示意图
pBlock指向的是堆中的代码块地址。