OC加强(二)之ARC/分类/延展

1.

autorelease 的用法:

2.ARC快速入门:

ARC机制下注意事项:

1) 不允许调用对象的 release方法 

 

2)不允许调用 autorelease方法 

 

3)再重写父类的dealloc方法时,不能再调用 [super dealloc]; 

 

ARC分为了强弱指针:strong和weak(默认是强指针)

只要有强指针指向,对象就不会被释放,所以不存在内存泄漏的问题

3.分类:category 

 首先什么是分类?分类是干吗用的?

其实很简单:分类的定义就是在不改变原有类的基础上增加新的方法

注意事项:

1)分类中只能增加方法不能增加成员变量

2)分类中可以访问原有类的成员变量

3)如果分类中出现和原有类中同名的方法,优先调用分类中的方法,原有类中的方法会被屏蔽

4)如果有多个分类,每个分类都有同名的方法,此时应该怎么调用?

        // 调用的是最后一个参与编译的分类文件的同名方法,跟引用头文件的顺序无关,跟创建顺序也无关

4.延展(extendsion)

 所谓的扩展,其实就是为一个类添加额外的原来没有 的变量、方法或者合成属性(@property)。 

类别与类扩展的区别:

1)类别中只能增加方法;

2)是的,你没看错,类扩展不仅可以增加方法,还可以增加实例变量(或者合成属性),只是该实例变量默认是私有类型的(作用范围只能在自身类,而不是子类或其他地方);

3)类扩展中声明的方法没被实现,编译器会报警,但是类别中的方法没被实现编译器是不会有任何警告的。这是因为类扩展是在编译阶段被添加到类中,而类别是在运行时添加到类中。

4)类扩展不能像类别那样拥有独立的实现部分(@implementation部分),也就是说,类扩展所声明的方法必须依托对应类的实现部分来实现。

5)定义在 .m 文件中的类扩展方法为私有的,定义在 .h 文件(头文件)中的类扩展方法为公有 的。类扩展是在 .m 文件中声明私有方法的非常好的方式。 

 5.block

 1)block最简单形式

/** 定义时,把block当成数据类型

特点:
1. 类型比函数定义多了一个 ^
2. 设置数值,有一个 ^,内容是 {} 括起的一段代码

最简单的定义方式

void (^myBlock)() = ^ { // 代码实现; }

*/

void (^myBlock)() = ^ {

  NSLog(@"hello");

};

// 执行时,把block当成函数

myBlock(); 

2) block带有参数的block的定义和使用

格式:

void (^block名称)(参数列表)
= ^ (参数列表) { // 代码实现; }

//定义有参数的block

void (^sumBlock)(int, int) = ^ (int x, int y) {

  NSLog(@"%d", x + y);

};

//调用block

sumBlock(10, 20); 

 

3) 带有参数和返回值的block

格式:
返回类型 (^block名称)(参数列表)

= ^ 返回类型 (参数列表) { // 代码实现; }

//定义有返回值和参数的block
int (^sumBlock2)(int, int) = ^ int (int a, int b) {

  return a + b;

};

//调用有返回值的block

NSLog(@"%d", sumBlock2(4, 8)); 

 4)利用typedef定义block类型

其实就是给block类型重命名用来替换这个类型更简化,起个别名

第一种是给无参无返回值的block起别名

 第二种就是给有参无返回值block起别名

 第三种是给有参有返回值的block起别名

6.block访问外部变量

1)在block内部是可以访问外部变量的

具体原理是:我们知道block是一个数据类型

变量 显示 存储空间 生命周期 特点
成员变量(实例变量、属性、成员属性) 在类的声明中定义在@interface  @end之间或者@property包含的 堆区   1.定义的时候不能初始化2.只能通过对象访问,不能离开类单独存在
局部变量 函数体内或代码块中 栈区 函数体内  
全局变量 函数外部 静态区 整个文件 程序一启动就会自动分配内存空间,直到程序释放才结束

看下图:问题:如果我要打印num的值,结果是什么?答案是距离NSLog最近的定义的值,就是300,如果把300注释掉会依次往上打印结果

这里注意一下就是block内部可以访问block外部的值,也可以改变block外部的全局变量的值但不可以改变block外部局部变量的值

特别要记住的就是:当block内部访问外部局部变量的时候是把存储在栈区的局部变量以const的形式copy到堆区中,仅仅是值的拷贝,通俗的来说可以理解为浅拷贝

所以我们只能访问到这个值却不能改变这个值.

而剩下的静态全局变量,全局变量,函数参数,也是可以在直接在Block中改变变量值的,但是他们并没有变成Block结构体__main_block_impl_0的成员变量,因为他们的作用域大,所以可以直接更改他们的值。

值得注意的是,静态全局变量,全局变量,函数参数他们并不会被Block持有,也就是说不会增加retainCount值。

2)给局部变量加上__block关键字,则这个局部变量可以在block内部进行修改。 

 带__block的自动变量 和 静态变量 就是直接地址访问。所以在Block里面可以直接改变变量的值。

注意:

一、静态变量 和 全局变量 在加和不加 __block 都会直接引用变量地址。也就意味着 可以修改变量的 值。在没有加__block 参数的情况下。

二, 常量变量(NSString *a = @"hello";a 为变量,@“hello”为常量。)-----不加__block类型 block 会引用常量的地址(浅拷贝)。加__block类型 block会去引用常量变量(如:a变量,a = @"abc".可以任意修改a 指向的内容。)的地址。

如果不加__block 直接在block 内部修改变量 ,会编译报错。block 内部改变量是 只读的。 

7.block作为函数返回值 

 

/*
 
   函数的返回值,一般只能返回值1个值
   想返回多个值,必须使用 指针完成
 
   int sum(int a,int b){
 
 
      return a+b;
   }
 
 
   //如何实现返回值是一个代码块类型
 
 //定义别名
  typedef void (^BlockType)();
 
  BlockType test(){
 
      void (^b1)() = ^{
         
         int a = 10;
         int s = a+100;
         NSLog(@"s = %d",s);
 
     }
 
      //b1();

     reurn b1;
 
  }
 
 //接收
 BlockType bb = test(); //bb是一个BlockType类型的,也是无参无返回值的block变量
 bb();
 */


#import <Foundation/Foundation.h>
//定义一个别名 BlockType
typedef void (^BlockType)();
typedef void (^BlockType1)(int a,int b);
typedef int (^BlockType2)(int a,int b);

//定义返回值是BlockType类型的函数
BlockType test(){
    
    //定义block变量 b1
    void (^b1)() = ^{
        
        int a = 10;
        int s = a+100;
        NSLog(@"s = %d",s);
        
    };
    
    //b1();
    
    return b1;
}

//定义返回值是BlockType1的函数
BlockType1 test1(){

    return ^(int a,int b){
    
        NSLog(@"a+b = %d",a+b);
    
    };

}


BlockType2 test2(){
    //return 函数返回值
    return ^(int a,int b){
        //return block代码块的返回值
        return a+b;
    };
    
}


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        //定义BlockType 类型的变量,接收test函数返回的结果i
        BlockType bb = test();
        bb(); //执行 block
        
        BlockType1 b2 = test1();
        b2(34,10);
        
        BlockType2 b3 = test2();
        //b3接收了函数的返回值
        //因为函数的返回值是一个有参数,有返回值的block
        //所以,b3可以直接执行block,同时block返回值是int类型
        //故,s = b3(10,38);
        int s = b3(10,38);
        NSLog(@"s = %d",s);
        
    }
    return 0;
}

8.三种类型的block

根据Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock, NSMallocBlock。

NSGlobalBlock:类似函数,位于代码段;

NSStackBlock:位于栈内存,函数返回后Block将无效;

NSMallocBlock:位于堆内存 

1)全局block定义:定义在函数外部的block或者定义在函数内部但没有捕获任何自动变量(局部变量)成为全局block(ARC和MRC均适用)

 

 2)栈block

栈block定义:在MRC下,block内部使用了自动变量

 3)堆block

在ARC下访问自动变量

8.1注意事项一:

 

block类型变量内存管理参数为什么要使用copy ? 

如果block类型变量用assign修饰那么无论在ARC还是MRC环境下block都是栈block,也就意味着block定义的变量存放在栈区,给我们的影响就是block变量会提前被释放,导致无法继续访问.如下图所示:

ARC下:

 MRC下:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2016-10-18 23:46  忆缘晨风  阅读(198)  评论(0编辑  收藏  举报