字符串,oc转c[msg UTF8String]
c转oc[NSString stringWithUTF8String:oper]
@public : 公共的,在任意的地方都可以通过对象名来访问.
@protected : 受保护的. 只能在本类以及本类的子类中访问.
@package : 被@package 修饰的属性只能在当前框架中访问.
@private 私有的,被@private修饰的属性只能在本类的对象方法实现中访问.
在子类中都无法访问.
子类继承父类的所有的成员.包括私有成员,只不过私有的成员在子类无法直接访问
当类第1次被访问的时候.会将这个类存储到代码段之中.类一旦被存储到代码段之中.直到程序结束的时候才会被回收.类是以Class对象的形式存储在代码段之中的.
Class c1 = [Person class];
C1等价于Person类
Class有个SEL类对象的属性,用来储存方法,
SEL s1 = @selector(sayHi);获取方法对象SEL
[p1 performSelector:SEL消息];发消息
传递参数
[p1 performSelector:s1 withObject:@"东北大乱炖"];
3). 如果参数超过了两个.
参数设计为1个数组. 或者将参数设计为1个对象.对象的属性就是参数.
@property NSString *name,*gender;
Setter,getter声明
@property float height,weight;
@synthesize name = _name,age = _age ,weight = _weight,height = _height;//基本不用
@property增强了,一个搞定声明实现都有了
静态类型: 指针指向的对象就是1个本类对象 那么这样就叫做静态类型.
Person *p1 = [Person new];
动态类型: 指针指向的对象不是本类对象,而是子类对象 这样就叫做动态类型.
Person *p1 = [Student new];
当1个父类指针指向1个子类对象的时候,只能通过这个父类指针调用这个子类对象中的父类成员.
子类独有的成员无法调用.
a. NSObject指针在编译器编译的时候.会做编译检查.
b. id指针在编译的时候,编译器不会做编译检查.直接通过.
a. 如果指针类型是id类型的,只能通过id类型的指针调用方法.
b. 不能通过id指针访问属性,也不能通过id指针使用点语法.
如果方法的返回值是当前类的对象.返回值写instancetype.
id和instancetype.
1). instancetype只能作为方法的返回值.代表返回值是当前类的对象.
id可以作为返回值也可以作为参数.
2). id是1个类型. instancetype不是1个类型是1个关键字.
1). 判断指针指向的对象中是否有指定的方法.这个方法能不能执行.对象方法
- (BOOL)respondsToSelector:(SEL)aSelector; *****
+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
判断类的对象是否可以调用指定的方法.
2). 判断类方法是否可以执行.
[类名 respondsToSelector:SEL];
3). 判断对象是否为指定类的对象或者子类对象.
- (BOOL)isKindOfClass:(Class)aClass;
4).判断对象是否为指定类的对象.不包括子类.
- (BOOL)isMemberOfClass:(Class)aClass;
5).判断1个类是否为另外1个类的子类.
+ (BOOL)isSubclassOfClass:(Class)aClass;
1). 如果希望对象1创建出来 对象的属性的值是我们指定的值 那么就可以重写构造方法.
2). 如果有1段代码.你希望在创建对象的同时就自动执行.那么就把这段代码写在构造方法中.
- (instancetype)init
{
if(self = [super init])
{
初始化当前子类对象的成员.
}
return self;
}
内存及MRC与ARC的使用
栈: 局部变量. 当局部变量的作用域被执行完毕之后,这个局部变量就会被系统立即回收.
堆: OC对象.使用C函数申请的空间.
BSS段: 未初始化的全局变量、静态变量. 一旦初始化就回收 并转存到数据段之中.
数据段: 已经初始化的全局变量、静态变量. 直到程序结束的时候才会被回收.
代码段: 代码. 程序结束的时候,系统会自动回收存储在代码段中的数据.
栈、BSS段、数据段、代码段存储在它们中的数据的回收,是由系统自动完成的.不需要我们干预.
在MRC环境下
分配在堆区中的OC对象,是肯定需要程序员回收的.
每1个对象都有1个属性.叫做retainCount.叫做引用计数器. 类型是unsigned long 占据8个字节.
NSUInteger count = [p1 retainCount];或p1.retainCount获取引用计数器
+1[p1 retain];
-1[p1 release];
默认是1,销毁为0 ,自动调用dealloc方法
- (void)dealloc
{
NSLog(@"当你看到这句话的时候,说明我已经离开这个世界了.");
[super dealloc];
}
mrc内存管理的原则
1). 有对象的创建,就要匹配1个release
2). retain的次数和release的次数要匹配.
3). 谁用谁retain. 谁不用谁release.
谁负责retain 谁就负责relase
4). 只有在多1个人用的时候才retain 少1个人使用的时候才release
野指针和僵尸对象
C语言中的野指针: 定义1个指针变量.没有初始化.这个指针变量的值是1个垃圾值,指向1块随机的空间.这个指针就叫做野指针.
OC中的野指针: 指针指向的对象已经被回收了.这样的指针就叫做野指针.
内存回收的本质:
申请1个变量,实际上就是向系统申请指定字节数的空间.这些空间系统就不会再分配给别人了.
当变量被回收的时候,代表变量占用的字节空间从此以后系统可以分配给别人使用了.
但是字节空间中存储的数据还在.
回收对象:
所谓的对象的回收,指的是对象占用的空间可以分配给别人.
当这个对象占用的空间没有分配给别人之前 其实对象数据还在.
僵尸对象
1个已经被释放的对象,但是这个对象所占的空间还没有分配给别人.这样的对象叫做僵尸对象.
我们通过野指针去访问僵尸对象的时候.有可能没问题 也有可能有问题.
当僵尸对象占用的空间还没有分配给别人的时候.这是可以的.
当僵尸对象占用的空间分配给了别人使用的时候 就不可以.
我们认为只要对象称为了僵尸对象,无论如何 都不允许访问了.
就希望如果访问的是僵尸对象,无论如何报错.
僵尸对象的实时检查机制.可以将这个机制打开. 打开之后. 只要访问的是僵尸对象,无论空间是否分配 就会报错.
为什么不默认打开僵尸对象检测.
一旦打开僵尸对象检测 那么在每访问1个对象的时候 都会先检查这个对象是否为1个僵尸对象,
这样是极其消耗性能的.
使用野指针访问僵尸对象会报错. 如何避免僵尸对象错误..
当1个指针称为野指针以后.将这个指针的值设置nil
当1个指针的值为nil 通过这个指针去调用对象的方法(包括使用点语法)的时候.不会报错. 只是没有任何反应.
但是如果通过直接访问属性 -> 就会报错
内存泄露.
指的是1个对象没有被及时的回收.在该回收的时候而没有被回收
一直驻留在内存中,直到程序结束的时候才回收.
单个对象的内存泄露的情况.
1). 有对象的创建,而没有对应的relase
2). retain的次数和relase的次数不匹配.
3). 在不适当的时候,为指针赋值为nil
4). 在方法中为传入的对象进行不适当的retain
多对象引用时
- (void)setCar:(Car *)car
{
if(_car != car)//说明新旧对象不是同1个对象.
{
[_car release];//才去release旧的
_car = [car retain];//retain新的.
}
}
对象挂掉前,把它指向的对象➖1
- (void)dealloc
{
NSLog(@"人死了");
[_car release];
[_name release];
[super dealloc];
}
当属性的类型是OC对象类型的时候,那么就使用retain
当属性的类型是非OC对象的时候,使用assign.
retain参数.只是生成标准的setter方法为标准的MRC内存管理代码 不会自动的再dealloc中生成relase的代码.
所以, 我们还要自己手动的在dealloc中release
readwrite: 默认值.代表同时生成getter setter
readonly: 只会生成getter 不会生成setter
属性为BOOL,参数getter=isGoodMan
避免头文件导入循环引用@class Book;
1. 当两个对象相互引用的时候.
A对象的属性是B对象 B对象的属性是A对象.
这个时候 如果两边都使用retain 那么就会发生内存泄露.
2. 解决方案: 1端使用retain 另外1端使用assign 使用assign的那1端在dealloc中不再需要release了.
将创建的对象,存入到自动释放池之中. 就不再需要手动的relase这个对象了.
因为池子销毁的时候 就会自动的调用池中所有的对象的relase。
自动释放池的好处: 将创建的对象存储到自动释放池中,不需要再写release
@autoreleasepool
// {
// Person *p3 = [[[Person alloc] init] autorelease];
// }
autorelease 的调用只有放在自动释放池之中 才可以讲其存储到自动释放池之中, 对象的创建可以在外面
使用类方法得到的对象,要求这个对象就已经被autorelease过了.
+ (instancetype)person
{
return [[[self alloc] init] autorelease];
}
这样,我们直接调用类方法.就可以得到1个已经被autorelease过的对象.
什么时候使用结构体: 1). 封装数据只有属性 2) 属性较少. 3个以下.
什么时候使用类: 1).封装数据既有属性也有行为. 2).只有属性 但是属性较多.
在ARC环境下
在ARC机制下,retain release dealloc autorelease retainCount这些方法方法无法调用.
dealloc不允许[super dealloc];
__strong来显示的声明强指针.
弱指针: 使用__weak标识的指针就叫做弱指针.
在ARC机制下. 当对象被回收的时候. 原来指向这个对象的弱指针会被自动设置为nil
在ARC的机制下,@property参数不能使用retain,使用参数, strong和weak,如果不写,默认是strong.
当两个对象循环引用时,一个用weak,一个用strong
在ARC下使用MRC类,使用命令. -fno-objc-arc
将MRC文件转换为ARC
GC: 程序在运行的期间,有1个东西叫做垃圾回收器.不断的扫描堆中的对象是否无人使用.
分类(延展)协议
当一个类有多个方法时,把功能相近的方法放进一个分类中category
使用分类注意的几个地方:
1. 分类只能增加方法,不能增加属性
2. 在分类之中可以写@property 但是不会自动生成私有属性. 也不会自动生成getter setter的实现.
只会生成getter setter的声明.
所以,你就需要自己写getter 和 setter的声明. 也需要自己定义属性 这个属性就必须在本类中.
3. 在分类的方法实现中不可以直接访问本类的真私有属性(定义在本类的@implementation之中)
但是可以调用本类的getter setter来访问真私有属性.
4. 分类中可以存在和本类同名方法的.
当分类中有和本类中同名的方法的时候,优先调用分类的方法.哪怕没有引入分类的头文件.
如果多个分类中有相同的方法,优先调用最后编译的分类.
为系统自带的类写分类 这个就叫做非正式协议.
unichar ch = [self characterAtIndex:i];从字符串中取出第i个字符.
延展: Extension
是1个特殊的分类. 所以延展也是类的一部分.
只有声明没有实现.和本类共享1个实现.
写在延展中的成员,就相当于是这个类的私有成员.只能在本类的实现中访问.
外部不能访问.延展天生就是来私有化类的成员的.
延展和分类的区别
1). 分类有名字.延展没有名字 是1个匿名的分类.
2). 每1个分类都有单独的声明和实现. 而延展只有声明 没有单独的实现 和本类共享1个实现,
3). 分类中只能新增方法. 而延展中任意的成员都可以写.
4). 分类中可以写@property 但是只会生成getter setter的声明.
延展中写@property 会自动生成私有属性 也会生成getter setter的声明和实现.
在.m文件中延展增加私有属性和方法
@interface Student ()
{
NSString *_name;
}
@property(nonatomic,assign)int age;
- (void)study;
- (void)play;
@end
协议:protocol.
专门用来声明一大堆方法. (不能声明属性,也不能实现方法,只能用来写方法的声明).
@protocol 协议名称 <NSObject>
方法的声明;
@end
无论是@required还是@optional你都可以不实现. 编译器是不会报错的. 仍然可以编译 运行.
唯一的区别就是: 当遵守协议的类不实现协议中的方法的时候,@required会给1个警告. @optional警告都木有.
默认的是@required
协议可以继承另外1个协议.A 协议 继承了 B协议. 那么A协议中不仅有自己的方法的声明,还有B协议中的方法的声明.
可以使用id指针.
id<协议名称> 指针名;这个指针所指向的对象拥有了协议的方法的声明
代理设计模式
代理遵守协议,
主人定义代理属性,
主人的代理属性设置代理源,
代理去实现方法,
主人让代理调用代理方法
block是1个数据类型.能作为函数的参数
typedef void (^NewType)();起别名 void test(NewType block1)无参数五返回值block变量作为函数参数
调用block1()
void test2(int (^paramsBlock)(int num1,int num2))有参有返回值的block作为函数参数
- (void)bianLiWithBlock:(void (^)(int val))processBlock//参数传递代码段
定义一个代码段
void (^pBlock)(int val) = ^(int val){
NSLog(@"val = %d",val + 1);
};
NSArray NSDictionary NSFileManger
无论是NSArray还是NSMutbaleArray里面都只能存储OC对象.
基本数据类型是无法存储的.
NSArray *arr = @[@"jack",@"rose",@"lili"];
For in 遍历数组, for(元素类型 变量名 in 数组名)
声明在for()中的变量叫做迭代变量.
block遍历
[arr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *stop) {
// NSLog(@"%@.lenth = %lu",obj,[obj length]);
// NSLog(@"idx = %lu",idx);
// //如果想停止遍历,就讲stop指针指向的BOOL变量的值改为YES
// if(idx == 1)
// {
// *stop = YES;
// }
// }];
NSNumber是Foundation框架中定义好的1个类.这个类的对象的作用就是用来包装基本数据类型的.
@10;包装为对象
如果后面的数据是1个变量 那么这个变量就必须要使用小括弧括起来.
@(num)
NSDictionary 字典数组
键: 只能是遵守了NSCoping协议的对象. 而NSString就是遵守了这个协议.
值: 只能是OC对象.
NSDictionary *dict = @{@"name":@"rose",@"age":@"18",@"addredd":@"BeiJingXXSttreet"};
存取键值对的时候,哈希算法.
存储进去之后,一旦要取值.就是全部取出. NSArray
存储进去之后.取值只会取指定的几个元素 字典数组.
将字典数组的信息保存到plist文件中.
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;
从plist文件中还原回字典.
+ (nullable NSDictionary<KeyType, ObjectType> *)dictionaryWithContentsOfFile:(NSString *)path;
在MRC下使用@[]或者@{}创建的集合已经是被autorelease过的了.
直接调用和类同名的类方法创建的对象 也是被autorelease过的了.
NSFileManger是Foundation框架提供的1个类.
这个类作用: 用来操作磁盘上的文件 文件夹 对他们进行创建、删除、复制 拷贝 移动.....
这个类的对象是以单例模式创建的.
NSFileManager *fileManager = [NSFileManager defaultManager];
1). 判断指定的文件或者文件夹在磁盘上是否真实的存在
- (BOOL)fileExistsAtPath:(NSString *)path;
2).判断指定的路径是否真实的存储在我们的磁盘之上,并且判断这个路径是1个文件夹路径还是1个文件路径.
- (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)isDirectory;
返回值:代表这个路径是否真实存在.
参数指针: 代表这个路径是否是1个文件夹路径
3). 判断指定的文件夹或者文件是否可以读取.
- (BOOL)isReadableFileAtPath:(NSString *)path;
4). 判断指定的文件夹或者文件是否可以写入.
- (BOOL)isWritableFileAtPath:(NSString *)path;
5). 判断指定的文件夹或者文件是否可以删除.
- (BOOL)isDeletableFileAtPath:(NSString *)path
1).获取指定文件或者文件夹的属性信息.
- (NSDictionary *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error
返回的是1个字典,如果要拿到特定的信息 通过key
2).获取指定目录下的所有的文件和目录. 是拿到指定目录下的所有的文件和目录 所有的后代目录和文件.
子目录的子目录的子目录 所有的都可以拿到.
- (NSArray *)subpathsAtPath:(NSString *)path;
3).获取指定目录下的所有的子目录和文件 不保护孙子辈.
- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error
1). 在指定的目录创建文件.
- (BOOL)createFileAtPath:(NSString *)path contents:(NSData *)data attributes:(NSDictionary *)attr
第1个参数: 要创建的文件的路径.
第2个参数: 这个文件的内容. 要传递这个文件的二进制格式.
这个二进制的数据格式 使用NSData对象来封装.
NSData: 将别的格式的数据转换为二进制数据.
将字符串转换为NSData二进制的方式.调用字符串对象的
- (NSData *)dataUsingEncoding:(NSStringEncoding)encoding
编码参数: NSUTF8StringEncoding
指定1个编码 就可以将字符串转换为二进制数据 存储在NSData对象之中.
最后再将这个二进制对象通过这个方法写入.
如果想创建1个空文件 第2个参数就给nil
第3个参数: 指定创建的文件的属性.如果想要使用系统的默认值使用nil
2). 在指定的目录创建文件夹.
- (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary *)attributes error:(NSError **)error
第1个参数: 路径.
第2个参数: YES,做一路创建. 如果是NO就不会做一路创建.
第3个参数: 指定属性 nil为系统默认属性.
第4个参数.
3).拷贝文件.
- (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error
4).移动文件 剪切 文件的重命名. 重名的原理: 将文件移动到原来的目录并改名.
- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error
5).删除文件.
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
注意 删除的文件不会倒废纸篓 而是直接删除,所以请小心使用.
CGRect rect;
rect.origin = (CGPoint){10,20};
rect.size = (CGSize){100,30};
NSValue 类 的对象就是用来包装结构体变量的.
CGSize s1 = CGSizeMake(100, 10);
NSValue *v1 = [NSValue valueWithSize:s1];
NSLog(@"%@",NSStringFromSize(v1.sizeValue));
NSDate
NSDate *date = [NSDate date];格林威治时间. 0时区的时间. 东8区.
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy年MM月dd日 HH点mm分ss秒";
NSString *str =[formatter stringFromDate:date];
- (NSString *)stringFromDate:(NSDate *)date; //将日期类型换换为字符串
- (NSDate *)dateFromString:(NSString *)string;//将字符串转换为日期对象.
+ (instancetype)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs;
在当前的时间的基础之上,新增指定的秒.后的时间
求两个时间之间的差.
可以实现的效果.就是可以计算出执行代码所花费的时间.
- (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate;
指定日历对象取到日期的对象的那些部分. 是要取那1个时间对象的部分.
NSCalendar *calendar = [NSCalendar currentCalendar];
// 返回1个日期组件对象.这个对象中就有指定日期的指定部分.
NSDateComponents *com = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:date];
NSString类型的. @property参数使用copy.
NSString ----> copy ---> 不可变字符串 没有产生新对象,而是直接将对象本身的地址返回. 这种拷贝我们叫做浅拷贝
NSMutableString --> copy --> 是1个可变的字符串对象 . 有产生1个新对象.这样的拷贝我们叫做深拷贝.
NSString ---> mutableCopy --> 可变字符串对象. 深拷贝.
NSMutableString --> mutableCopy --> 可变字符串对象 深拷贝.
字符串对象如果是浅拷贝. 会将对象的引用计数器+1
字符串对象如果是深拷贝. 原来的对象的引用计数器不变.新拷贝出来的对象的引用计数器为1.存储在常量区的字符串对象的引用计数器是1个超大的数.并且retain和release无效.
1. 单例模式:
1个类的对象,无论在何时创建也无论在什么地方创建 也无论创建多少次.创建出来的都是同1个对象.
2. 无论如何创建对象,最终都会调用alloc方法来创建对象.
1). alloc方法的内部. 其实什么都没有做,只是调用了allocWithZone:方法.
2). 实际上真正申请空间 创建对象的事情是allocWithZone:方法在做.
3. 要实现单例模式.
重写+ allocWithZone:
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static id instance = nil;
if(instance == nil)
{
instance = [super allocWithZone:zone];
}
return instance;
}
4. 单例模式的规范:
如果类是1个单例模式.要求为类提供1个类方法.来返回这个单例对象.
类方法的名称必须以 shared类名; default类名;