OC基础易忘知识点

OC基础易忘知识点_第1张图片
屏幕快照 2017-06-22 上午12.15.06.png

];
p->_size = 3.5;
p->_color = 0;
p->_model = 4;
p->_cpu = 1;


####类

创建对象的时候返回的地址其实就是对象的第0个属性的地址
     但是需要注意的是: 对象的第0个属性并不是我们编写的属性, 而是一个叫做isa的属性
     isa是一个指针, 占8个字节
     
     其实类也是一个对象, 也就意味着Person也是一个对象
     平时我们所说的创建对象其实就是通过一个 类对象 来创建一个 新的对象
     类对象是系统自动帮我们创建的, 里面保存了当前对象的所有方法
     而实例对象是程序自己手动通过new来创建的, 而实例对象中有一个isa指针就指向了创建它的那个类对象

如下图:


![类对象](http://upload-images.jianshu.io/upload_images/2264508-a9485608a39cffd8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

#####成员变量,局部变量,全局变量

    写在类声明的大括号中的变量, 我们称之为 成员变量(属性, 实例变量)
    成员变量只能通过对象来访问
    注意: 成员变量不能离开类, 离开类之后就不是成员变量 
    成员变量不能在定义的同时进行初始化
    存储: 堆(当前对象对应的堆的存储空间中)
    存储在堆中的数据, 不会被自动释放, 只能程序员手动释放

    写在函数和大括号外部的变量, 我们称之为全局变量
    作用域: 从定义的那一行开始, 一直到文件末尾
    局部变量可以先定义在初始化, 也可以定义的同时初始化
    存储: 静态区
    程序一启动就会分配存储空间, 直到程序结束才会释放

    写在函数或者代码块中的变量, 我们称之为局部变量
    作用域: 从定义的那一行开始, 一直到遇到大括号或者return
    局部变量可以先定义再初始化, 也可以定义的同时初始化
    存储 : 栈
    存储在栈中的数据有一个特点, 系统会自动给我们释放

#####点语法

    如果给属性提供了getter和setter方法, 那么访问属性就又多了一种访问方式 , 点语法
    点语法其实它的本质是调用了我们的setter和getter方法
    点语法是一个编译器的特性, 会在程序翻译成二进制的时候将.语法自动转换为setter和getter方法
    如果点语法在=号的左边, 那么编译器会自动转换为setter方法
    如果点语法在=号的右边, 或者没有等号, 那么编译器就会自动转换为getter方法
     点语法的注意点:
     点语法一般用于给成员变量赋值, 如果不是给成员 
     变量赋值一般情况下不建议使用, 但是也可以使用     
     如调用test方法,可以写成p.test,相当于[p test]


#####多态

     多态是事物的多种表现形态

     在程序中如何表现:
     父类指针指向子类对象
     
     优点:
     提高了代码的扩展性
     
     注意点:
     如果父类指针指向子类对象, 如果需要调用子类特有的方法, 必须先强制类型转换为子类才能调用
    
    动态类型: 在编译的时候编译器只会检查当前类型   对应的类中有没有需要调用的方法
    在运行时,系统会自动判断a1的真实类型

例如:

Animal:动物类

import

@interface Animal : NSObject
{
int _age;
}

  • (void)eat;
    @end

import "Animal.h"

@implementation Animal

  • (void)eat
    {
    NSLog(@"吃东西");
    }
    @end

Dog狗类,继承动物类:

import

import "Animal.h"

@interface Dog : Animal

  • (void)kanJia;
    @end

import "Dog.h"

@implementation Dog

  • (void)eat
    {
    NSLog(@"啃骨头");
    }

  • (void)kanJia
    {
    NSLog(@"看家, 旺旺叫");
    }
    @end

Animal *a1 = [Dog new];
[a1 eat];

编译时因为动物有eat方法,不报错。但是运行时判断为dog对象,调用dog对象的eat方法


#####实例变量修饰符

@public 就是实例变量修饰符
 
 @public
 >可以在其它类中访问被public修饰的成员变量
 >也可以在本类中访问被public修饰的成员变量
 >可以在子类中访问父类中被public修饰的成员变量
 
 @private
 >不可以在其它类中访问被private修饰的成员变量
 >可以在本类中访问被private修饰的成员变量
 >不可以在子类中访问父类中被private修饰的成员变量
 
 @protected
 >不可以在其它类中访问被protected修饰的成员变量
 >可以在本类中访问被protected修饰的成员变量
 >可以在子类中访问父类中被protected修饰的成员变量
 
 注意: 默认情况下所有的实例变量都是protected
 
 @package
 >介于public和private之间的
 如果是在其它包中访问那么就是private的
 如果是在当前代码所在的包种访问就是public的

#####description
    NSlog(@"",moumoumou)
    %@是用来打印对象的, 其实%@的本质是用于打印字符串
    只要利用%@打印某个对象, 系统内部默认就会调用父类的description方法
    调用该方法, 该方法会返回一个字符串, 字符串的默认格式 <类的名称: 对象的地址>
    可以重写description方法, 返回我们需要打印的内容
    只要利用%@打印对象, 就会调用description
    如果通过%@打印对象就会调用-号开头的
    如果通过%@打印类对象就会调用+号开头的

    建议: 在description方法中尽量不要使用self来获取成员变量
    因为如果你经常在description方法中使用self, 可能已不小心就写成了 %@, self
    如果在description方法中利用%@输出self会造成死循环,例如下面:

  • (NSString *)description
    {
    return [NSString stringWithFormat:@"%@", self];
    }

####@porperty

在Xocde4.4之前, 可以使用@porperty来代替getter/setter方法的声明
 也就是说我们只需要写上@porperty就不用写getter/setter方法的声明

 @synthesize是一个编译器指令, 它可以简化我们getter/setter方法的实现
 
 什么是实现:
 在声明后面写上大括号就代表着实现
  (1)在@synthesize后面告诉编译器, 需要实现哪个@property生成的声明
  (2)告诉@synthesize, 需要将传入的值赋值给谁和返回谁的值给调用者
    如果在@synthesize后面没有告诉系统将传入的值赋值给谁, 系统默认会赋值给和@synthesize后面写得名称相同的成员变量

例如:
@synthesize age;就代表实现age成员变量的get, set,不是_age.

从Xcode4.4以后apple对@property进行了一个增强, 以后只要利用一个@property就可以同时生成setter/getter方法的声明和实现
 没有告诉@property要将传入的参数赋值给谁, 默认@property会将传入的属性赋值给_开头的成员变量
 
 @property有一个弊端: 它只会生成最简单的getter/setter方法的声明和实现, 并不会对传入的数据进行过滤
 如果想对传入的数据进行过滤, 那么我们就必须重写getter/setter方法
 如果不想对传入的数据进行过滤, 仅仅是提供一个方法给外界操作成员变量, 那么就可以使用@property
 
 如果利用@property来生成getter/setter方法, 那么我们可以不写成员变量, 系统会自动给我们生成一个_开头的成员变量
 注意: @property自动帮我们生成的成员变量是一个私有的成员变量, 也就是说是在.m文件中生成的, 而不是在.h文件中生成的

如果重写了setter方法, 那么property就只会生成getter方法
如果重写了getter方法, 那么property就只会生成setter方法
如果同时重写了getter/setter方法, 那么property就不会自动帮我们生成私有的成员变量

####id类型

id是一个数据类型, 并且是一个动态数据类型

默认情况下所有的数据类型都是静态数据类型
     静态数据类型的特点: 
     在编译时就知道变量的类型, 
     知道变量中有哪些属性和方法
     在编译的时候就可以访问这些属性和方法, 
     并且如果是通过静态数据类型定义变量, 如果访问了不属于静态数据类型的属性和方法, 那么编译器就会报错
     
动态数据类型的特点:
     在编译的时候编译器并不知道变量的真实类型, 只有在运行的时候才知道它的真实类型
     并且如果通过动态数据类型定义变量, 如果访问了不属于动态数据类型的属性和方法, 编译器不会报错

#####类的本质

类其实也是一个对象, 这个对象会在这个类第一次被使用的时候创建
     只要有了类对象, 将来就可以通过类对象来创建实例对象
     实例对象中有一个isa指针, 指向创建自己的类对象
     
 类对象中保存了当前对象所有的对象方法
     当给一个实例对象发送消息的时候, 会根据实例对象中的isa指针去对应的类对象中查找


![Snip20150623_6.png](http://upload-images.jianshu.io/upload_images/2264508-1a692817a227e781.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


启动过程:

只要程序启动就会将所有类的代码加载到内存中, 放到代码区。load方法会在当前类被加载到内存的时候调用, 有且仅会调用一次。如果存在继承关系, 会先调用父类的load方法, 再调用子类的load方法。当当前类第一次被使用的时候就会调用(创建类对象的时候)。initialize方法在整个程序的运行过程中只会被调用一次, 无论你使用多少次这个类都只会调用一次。initialize用于对某一个类进行一次性的初始化。initialize和load一样, 如果存在继承关系, 会先调用父类的initialize再调用子类的initialize。


#####内存管理

野指针:
只要一个对象被释放了, 我们就称这个对象为 "僵尸对象"。当一个指针指向一个僵尸对象, 我们就称这个指针为野指针。只要给一个野指针发送消息就会报错。为了避免给野指针发送消息会报错, 一般情况下, 当一个对象被释放后我们会将这个对象的指针设置为空指针。

#####@class

应用场景1:

声明一个类

应用场景2:

防止循环拷贝,造成死循环。(import引入会拷贝文件内容,如果.h1引入.h2,.h2又引入.h1,会造成循环拷贝)

#####Block

MRC:

返回值 (^block名字 )(参数列表)

取别名

typedef (^bieblock)(int,int)

就可以用 bieblock来定义新block,类型为上面

如:

bieblock block1;
block1 = ^(int value1, int value2){
        return value1 + value2;
    };

// 1.block中可以访问外面的变量
/*
int a = 10;
void (^myBlock)() = ^{
NSLog(@"a = %i", a);
};
myBlock();

打印a = 10

// 2.block中可以定义和外界同名的变量, 并且如果在block中定义了和外界同名的变量, 在block中访问的是block中的变量
/*
int a = 10;
void (^myBlock)() = ^{
int a = 20;
NSLog(@"a = %i", a);
};
myBlock();
*/

打印a = 20;

//

// 3.默认情况下, 不可以在block中修改外界变量的值
// 因为block中的变量和外界的变量并不是同一个变量
// 如果block中访问到了外界的变量, block会将外界的变量拷贝一份到堆内存中
// 因为block中使用的外界变量是copy的, 所以在调用之前修改外界变量的值, 不会影响到block中copy的值
/*
int a = 10;
NSLog(@"&a = %p", &a);
void (^myBlock)() = ^{
// a = 50;
NSLog(@"&a = %p", &a);
NSLog(@"a = %i", a);
};
a = 20;
myBlock();
*/

/*
// 如果想在block中修改外界变量的值, 必须在外界变量前面加上__block
// 如果在block中修改了外界变量的值, 会影响到外界变量的值
__block int a = 10;
NSLog(@"&a = %p", &a);
void (^myBlock)() = ^{
a = 50;
NSLog(@"&a = %p", &a);
NSLog(@"a = %i", a);
};
myBlock();
NSLog(@"a = %i", a);
*/

/*

// 默认情况下block存储在栈中, 如果对block进行一个copy操作, block会转移到堆中
// 如果block在栈中, block中访问了外界的对象, 那么不会对对象进行retain操作
// 但是如果block在堆中, block中访问了外界的对象, 那么会对外界的对象进行一次retain

// 如果在block中访问了外界的对象, 一定要给对象加上__block, 只要加上了__block, 哪怕block在堆中, 也不会对外界的对象进行retain
// 如果是在ARC开发中就需要在前面加上__weak

内存管理:

MRC:管理block
总结:只要block没有引用外部局部变量,block放在全局区
只要Block引用外部局部变量,block放在栈里面.
block只能使用copy,不能使用retain,使用retain,block还是在栈里面
只要block没有引用外部局部变量,block放在全局区

ARC:管理block
只要block引用外部局部变量,block放在堆里面

    block使用strong.最好不要使用copy

#####copy属性

// 如果是通过不可变对象调用了copy方法, 那么不会生成一个新的对象
// 原因: 因为原来的对象是不能修改的, 拷贝出来的对象也是不能修改的
// 既然两个都不能修改, 所以永远不能影响到另外一个对象, 那么已经符合需求
// 所以: OC为了对内存进行优化, 就不会生成一个新的对象(比如nsstring->nsstring)

 正是因为调用copy方法有时候会生成一个新的对象, 有时候不会生成一个新的对象
     所以: 如果没有生成新的对象, 我们称之为浅拷贝, 本质就是指针拷贝
          如果生成了新的对象, 我们称之为深拷贝, 本质就是会创建一个新的对象

// 3.注意点: copy block之后引发循环引用(MRC中)
(
    // 如果对象中的block又用到了对象自己, 那么为了避免内存泄露, 应该将对象修饰为__block
    __block Person *p = [[Person alloc] init]; // 1
    p.name = @"lnj";
    NSLog(@"retainCount = %lu", [p retainCount]);
    p.pBlock = ^{
        NSLog(@"name = %@", p.name); // 2
    };
    NSLog(@"retainCount = %lu", [p retainCount]);
    p.pBlock();
    
   [p release]; // 1
)


![屏幕快照 2017-06-22 上午12.10.13.png](http://upload-images.jianshu.io/upload_images/2264508-a8b0c54130bc743c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)



![Uploading 屏幕快照 2017-06-22 上午12.15.06_880264.png . . .]

你可能感兴趣的:(OC基础易忘知识点)