内存中的五大区域
栈:存储局部变量
堆:程序员手动申请的字节空间
BSS段:存储未被初始化的全局变量 静态变量
数据段(常量区) :存储已经被初始化的全局变量,静态变量,常量数据,直到程序结束的时候才会被回收
代码段:存储程序的代码,如类第一次访问时,就把类加载到代码段
类加载
1).在创建对象的时候.肯定是需要访问类的
2).声明1个类的指针变量也会访问类的
在程序运行期间,当某个类第一次被访问到的时候,会将这个类存储到内存中的代码段区域,这个过程叫做类加载
只有类在第一次被访问的时候,才会做类加载
一旦类被加载到代码段以后,直到程序结束的时候才会被释放
- 类在代码段中存储的步骤?
a.现在代码断种创建个Class对象,Class是Foundation框架中的一个类.
b.将类的信息存储在Class对象中,这个Class至少有3个属性
类名,属性,方法,注意:还有一个isa指针,指向父类的类对象
存储类的这个Class对象,我们也叫类对象
任何存储在内存中的数据都有一个数据类型,任何在内存中申请的空间也有自己的类型
instancetype
当返回的是该类的对象时,用instancetype
static
1).修饰局部变量(在方法里面的变量).
使局部变量只初始化一次,局部变量在程序中只有一份内存,使和这个变量拥有记忆功能(可以被赋值).局部变量的作用域不变,但是生命周期变了(直到程序结束才销毁)
2).修饰的全局变量
拥有局部变量修饰时候的属性,不同的是该静态变量只能被本文件访问(作用域在本文件)
访问修饰符
用来修饰属性,可以限定对象的属性在哪个范围之中访问
@private:私有 被@private修饰的属性只能在本类的内部访问(本类的实现中访问)
@protected:受保护的,被@protected修饰的属性只能在本类和本类的子类中访问
@package:被@package修饰的属性,可以在当前的框架在访问
@public:公共的,被@public修饰的属性,可以在任意的地方访问
- 注意:如果不为属性指定访问修饰符,那么默认的就是@protected
- 子类仍然可以继承父类的私有属性,只不过,在子类中无法去直接访问从父类继承过来的私有属性,如果父类中有一个方法在为属性赋值或者取值,那么子类可以调用这个方法间接的访问父类的私有属性
- 访问修饰符的作用域:从写访问修饰符的地方开始往下,直到遇到另外一个修饰符或者结束大括号为止,中间的所有属性都应用这个访问修饰符
里氏替换原则
父类指针指向子类对象
当一个父类指针指向子类对象的时候,通过这个父类指针就只能去调用子类中的父类成员,子类独有的成员无法访问
当一个父类的指针指向子类对象时,通过这个父类指针调用的方法,如果在子类对象中重写了,调用的就是子类重写的方法
多态
指的是同一个行为,对于不同的事物具有不同的表现形式
结构体和类的区别
1).结构体只能封装数据,而类不仅可以封装数据,还可以封装方法
2).结构体变量分配在栈空间(如果是局部变量的情况下),而对象分配在堆空间
栈空间:空间相对较小,但是存储在栈中的数据访问效率更高一些
堆空间:空间相对较大,但是存储在栈中的数据访问效率相对要低
3).赋值
//结构体 Student
Student s = {"小明",19}
//类 Student
Student *s = [Student new];
s.name= @"小明";
s.age = 19;
id
万能指针.也可以作为返回值.在编译阶段不会去检查
instancetype
只能作为返回值,返回当前对象
dealloc
重写父类的dealloc方法时,必须要调用父类的dealloc方法,即[super dealloc].且要放到方法的最后面.在ARC的机制下,retain,release和dealloc这些方法无法调用,即[super dealloc]不要去调用
对象回收的本质
所谓的对象回收,指的是对象占用的空间可以分配给别人,当这个对象占用的空间还没分配给别人使用之前,其实这个对象的数据还在
野指针
已经释放的对象指针,就称为野指针
僵尸对象
一个已经被释放的对象,但这个对象的空间还没分配给别人,这样的对象叫做僵尸对象.
我们通过野指针去访问僵尸对象的时候,有可能没问题,也有可能有问题.当僵尸对象占用的空间还没有分配给别人之前,这是可以访问的,当僵尸对象占用的空间已经分配给别人了,就不允许访问
@property参数
@property(参数1,参数2,参数3...)数据类型 变量名称
1.与多线程有关的两个参数:atomic,nonatomic
atomic:默认值,如果写atomic,这个时候生成的setter方法的代码就会被加上一把线程安全锁.特点:安全,效率低
nonatomic:nonatomic,这个时候生成的setter方法的代码就不会被加上一把线程安全锁.特点:不安全,效率高
建议:使用nonatomic
2.与生成的setter方法实现有关的参数:assign,retain(MRC),strong,weak,copy....
assign:默认值,生成的setter方法实现就是直接赋值.在ARC和MRC下都可以使用
retain:只能用在MRC模式下,生成的setter实现就是标准的mrc内存格式下的实现.先判断旧的对象和新的对象是不是同一个值,如果不是,就realease旧的,retain新的
使用:当属性的类型是oc对象的时候就是用retain.当属性的类型是非oc对象就是用assign
在ARC机制下,当属性类型是oc对象时,不能使用retain,绝大数时间应该使用strong
strong强类型指针,weak弱类型指针
3.与生成只读,读写有关的参数:readonly,readwrite
readwrite:默认值,代表同时生成getter和setter.
readonly:只会生成getter,不会生成setter
4.与生成getter,setter方法名字有关的参数:getter,setter
默认情况下,@property生成的getter,setter都是标准的名字.其实我们也可以通过参数来着指定@property生成的方法的名字
@property(nonatomic,assign,getter=自定义名字)int age
@class和#improt的区别
import是将指定的文件内容拷贝到写指令的地方,@class并不会拷贝任何内容.只是告诉编译器,这是一个类,这样编译器在编译的时候才可以知道这是一个类
Block
block在oc中是一种数据类型
,所以可以作为变量,参数,返回值使用.
语法:返回值类型 (^block名字)(参数列表) = ^返回值类型(参数列表){...}
int (^MyBlock)(Nsstring *name) = ^void(Nsstring *name){
NSLog(@"%@",name);
}
//关于block的简写
//如果block表达式(代码段)中没有返回值,那么代码段中的void可以省略
void (^MyBlock)(Nsstring *name) = ^(Nsstring *name){
...
}
//注意:1.代码段中的void可以省略,声明block变量的返回值无论是什么都不可以省略的.2.如果代码段中没有参数,代码段中表示参数列表的()也可以省略掉,声明block变量的参数()不能省略
void (^MyBlock2)() = ^{
...
};
//声明block变量的时候,如果有指定参数,可以只写参数的类型而不写参数的名称
int (^MyBlock3)(int,int) = ^(int num1,int num2){
......
};
//注意,这个地方是声明block变量的时候,写代码段的时候类型和形参名字都要写
//在写代码段中,其实无论时候有返回值,都可以省略返回值类型,那么代码段的返回值类型就感觉代码段中代码决定,如果代码中的返回值和声明block的返回值不一样,那么程序就会报错
NSString (^Block4)(NSString) = (NSString *str){
return @"你高啊"
};
//用typedef声明block
typedef 返回值类型(^Block类型新名)(参数列表)
//关于block访问外部变量
1).在block代码块内部可以取定义在外部的局部变量和全局变量的值.
2).在block代码块内部可以修改全局变量的值,但不能修改定义在外部的局部变量的值,如果非要改,在局部变量前面加__block的修饰符.
当block作为函数的返回值时,就必须要使用typedef来定义block的类型,不然会报错
typedef
使用场景:将一个长类型定义成一个短类型.
在使用block时,可以将长的block声明定义成一个变量
//如系统的NSUInteger定义
typedef unsigned long NSUInteger;
---------
NS_ASSUME_NONNULL_BEGIN
typedef void (^Block)(NSString *str);
@interface Person : NSObject
@property(nonatomic, copy)Block block;
@end
NS_ASSUME_NONNULL_END
协议:protocol
作用:专门用来声明一大堆方法.(不能声明属性,也不能实现方法,只能用来写方的声明).只要某个类遵守了这个协议,就相当于拥有个这个协议中的所有方法声明,而不用自己去定义
///协议的声明
@protocol 协议名称
方法的声明
@end
///某个类遵守了协议
@interface 类名: 父类名<协议名称>
@end
@required和@optional是专门修饰协议中的方法.默认是@required
在协议中被@required修饰的方法,那么遵守这个协议的类必须要实现这个方法,否则编译器会发出警告.
在协议中,被@optional修饰的方法,那么遵守协议的这个类可以实现这个方法,也可以不实现这个方法,编译器不会发出警告
协议与协议之间可以相互继承.语法:
@protocol 协议名称 <父协议名称>
@end
通过协议的继承,子协议中不仅有自己的方法的声明,还有父协议中的所有方法的声明.如果一个类遵守了某个协议,那么这个类就拥有了这个协议和这个协议的父协议的所有方法的声明
字符串的 == 判断(不要用==去判断字符串是否相等)
NSString *str1 = @"你好";
NSString *str2 = [NSString stringwithFormat:@"你好"];
Bool res = str1 == str2;
NSLog(@"%@",res); //输出结果是NO
NSString *str3 = @"你好";
NSString *str4 = @"你好";
Bool re1 = str3 == str4;
NSLog(@"%@",res1); //输出结果是YES
//注意:字符串==判断是判断两个字符串指针的值.用NSString *str1 = @"你好";定义的字符串在常量区存储,而且相同的字符串不会重复创建,[NSString stringwithFormat:@"你好"];创建的字符串在堆区,相同的字符串也不会重复创建
copy 拷贝
1).copy是一个方法,定义在NSObject类之中,作用:拷贝对象
NSString -->copy--->不可变字符串 没有产生新对象,而是直接返回原对象的地址,这种拷贝也称浅拷贝
NSMutableString-->copy--->是一个不可变字符串对象,产生一个新的对象,这样的拷贝成为深拷贝
2).mutableCopy定义在NSObject类之中,作用:拷贝对
NSString--->mutableCopy---->可变字符串对象,深拷贝
NSMutableString-->mutableCopy--->可变字符串对象,产生一个新的对象为深拷贝
- 总结:
- copy出来的字符串一定是不可变字符串,如果传入的是可变字符串,会发生深拷贝,否则是浅拷贝
- mutableCopy一定是深拷贝,拷贝出来的一定是可变字符串或者数组,即使传入的是不可变字符串或者数组
为什么NSString使用copy修饰也就可以理解了。使用copy修饰之后,即使属性拷贝来自可变字符串,也会被深拷贝成不可变字符串,也就是源字符串修改之后不会影响到属性字符串,增强了代码的健壮性
%p 打印的是指针变量的值.%@ 打印的是指针指向的对象