];
p->_size = 3.5;
p->_color = 0;
p->_model = 4;
p->_cpu = 1;
####类
创建对象的时候返回的地址其实就是对象的第0个属性的地址
但是需要注意的是: 对象的第0个属性并不是我们编写的属性, 而是一个叫做isa的属性
isa是一个指针, 占8个字节
其实类也是一个对象, 也就意味着Person也是一个对象
平时我们所说的创建对象其实就是通过一个 类对象 来创建一个 新的对象
类对象是系统自动帮我们创建的, 里面保存了当前对象的所有方法
而实例对象是程序自己手动通过new来创建的, 而实例对象中有一个isa指针就指向了创建它的那个类对象
如下图:
![类对象](https://img.haomeiwen.com/i2264508/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 <Foundation/Foundation.h>
@interface Animal : NSObject
{
int _age;
}
- (void)eat;
@end
import "Animal.h"
@implementation Animal
- (void)eat
{
NSLog(@"吃东西");
}
@end
Dog狗类,继承动物类:
import <Foundation/Foundation.h>
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](https://img.haomeiwen.com/i2264508/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](https://img.haomeiwen.com/i2264508/a8b0c54130bc743c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![Uploading 屏幕快照 2017-06-22 上午12.15.06_880264.png . . .]
网友评论