关键字
static
不能修饰属性,也不能修饰方法。但是可以修饰方法中的局部变量(下次再执行此方法时直接使用不会再声明)。
(类似于java中的类属性)
Self
指向当前类或对象的指针(和java中的this类似)
Super
指向父类,想使用父类的方法时使用
@property
作用:自动生成getter和setter方法的声明
使用:
@property 数据类型 名称;
@property int age;//去掉下划线. _age
原理:
编译器在编译的时候会根据@property生成getter和setter方法的声明
可以批量声明(需类型一致)。
@synthesize
作用:
生成一个真私有属性与@property修饰的名字一样
自动生成getter、setter方法的实现(生成的setter方法没有逻辑判断,可以根据需求重写),将参数赋值给自动生成的私有属性
使用:
@synthesize 名称;
不生成私有属性的方法:
@synthesize @property名称 = 已经存在的属性名;
@synthesize age = _age;
可以批量声明@synthesize
@property增强
生成私有属性(私有的名称和property的名称一致, 属性的名称自动加下划线)
生成getter setter的声明
生成getter setter的实现(无逻辑验证)
可以批量声明相同类型属性,重写setter或getter任意一个,另一个都会自动生成,如果同时重写就不会生成私有属性。
继承
父类的@property可以被子类继承,虽然@property生成的属性是私有的,在子类的内部无法直接访问,但是可以通过setter和getter来访问;
[super setter:XXXX];
参数
- 多线程相关
- Atomic(默认值),会被加上线程安全锁
特点:安全、效率低
- Nonatomic不加锁
- 与生成的setter方法的实现相关的参数
- Assign(默认),生成的setter方法的实现就是直接赋值
- Retain 生成的setter方法的实现是标准版的MRC。但不会自动的在dealloc中生成release代码,所以要手动添加。
- (void)setCar:(Car *) car{ if(_car != car){ [_car release]; -car = [car retain]; } }
属性类型是OC对象时用retain,非OC对象时,assign
- 与生成只读、读写相关的参数
- Readonly 只会生成getter
- Readwrite(默认值)生成getter setter
- 与生成的setter getter方法名字相关的参数
- Getter修改getter的名字
- setter修改setter的名字
- 与生成的属性类型强弱有关的参数
- Strong
- Weak
- 无论是MRC还是ARC,只要类型是NSString,都用copy
类和对象
访问修饰符(只能修饰属性,不能修饰方法)
@private 只能在本类内部或本类的方法实现中访问
@protected 只能在本类及其子类中访问(默认等级)
@package 可以在当前框架中访问
@public 任意地方都可以访问
私有属性
使用@private修饰的属性 虽然是私有属性,但是依然会被提示,只是没有权限
如果声明属性在类的实现中那么将不会被提示,可以做到真正的私有
私有方法
只能在本类中的其它方法调用(只写实现不写声明)
类的封装
Setter方法与getter方法
set+变量名 提供一个方法给外界,设置变量的值; get+变量名 返回对象变量的值;set方法后跟的变量名首字母必须大写。
-(void)setName:(NSString * )name;//setter
-(NSString *)name;//getter
根据具体使用场景来进行封装,如只读封装和只写封装等
如果方法的返回值是当前类,关键字用instancetype
description
使用%@输出创建的对象时,会先调用传入的对象的description方法,返回一个NSString字符串,然后将这个字符串输出在控制台。
默认方法实现 @"<Class:对象地址>";
可以通过重写来自定义输出格式
多态
里氏替换原则
子类可以替换父类,并且不影响原有功能
同一行为,对于不同的事物(子-父类)具有完全不同的表现形式,可以对父类的方法进行重写来实现自己独有的功能。
匿名对象
不使用指针接收对象的地址,每次创建的对象都是不同的;
[MyObject new];
使用场景:对象只用一次
得到类在代码段中的地址方式
- 调试查看对象的isa指针的值
- 在类方法中查看self的值
- 调用对象或者类的class方法
OC中类之间的关系
组合
1个对象是由多个对象组合起来的.
比如.计算机对象. 是由主板对象、CPU对象、内存对象、硬盘对象...组合起来的.
主板、内存、硬盘作为计算机对象的属性.
那么这个时候,计算机对象和主板、内存、硬盘的关系为 组合关系.
依赖
1个对象的方法的参数是另外1个对象.那么我们就说他们的关系是依赖关系.
比如,B类是A类方法的参数,我们就说A类依赖于B类.
人打电话的例子.
人类:
callWithPhone:(Phone *)phone;
我们就说人类依赖于电话类. 人要打电话 就必须要有电话对象.
电话类:
耦合度: 当修改1个对象的时候 对另外1个对象的影响程度.
A类和B类. 如果修改了B类. 发现A类就无法使用了,我们就说他们的耦合度很高.
低耦合: 当修改1个对象的时候 对另外1个对象的影响较小甚至没有影响.
高内聚: 1个对象仅仅做自己相关的事情. 跟自己无关的事情就不要写在类中.不要写1个大杂烩.
单一职责原则:1个类只做自己的事情.别人的事情给别人做.
关联
关联体现的是两个类之间语义级别的一种强依赖关系,
比如我和我的朋友,这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的,一般是长期性 的,
而且双方的关系一般是平等的。关联可以是单向、双向的。
表现在代码层面,
为被关联类B以类的属性形式出现在关联类A中,也可能是关联类A引用了一个类 型为被关联类B的全局变量。
继承
面向对象编程(OOP)语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
OC 的类是单继承,一个类只能有一个父类
OC中所有类的源头是NSObject
异常
Objective-C的异常比较像Java的异常处理,也有@finally的处理,不管异常是否捕获都都要执行。
异常处理捕获的语法:
@try {
<#statements#>
}
@catch (NSException *exception) {
<#handler#>
}
@finally {
<#statements#>
}
@catch{} 块 对异常的捕获应该先细后粗,即是说先捕获特定的异常,再使用一些泛些的异常类型。
内存管理
内存中的五大区域
栈:局部变量,当局部变量的作用域被执行完毕后。立马被系统回收
堆:OC对象,使用C函数申请的空间(系统不会自动回收,直至程序结束)
BSS段:未初始化的全局变量、静态变量,一旦初始化就回收,并转存到数据段中
数据段:已经初始化的全局变量、静态变量。程序结束才会回收
代码段:代码,程序结束的时候,系统自动回收存在代码段的数据
需要自己手动管理的只有堆。
引用计数器
- 每一个对象都有一个属性,叫做retainCount。类型是unsigned long8个字节,用来记录有多少个对象引用它。(默认情况:创建一个对象出来就是1)
- 当retainCount为0时,代表这个对象无人可用,这时系统自动回收(调用dealloc方法)
操作:
- 为对象发送一条retain消息,retainCount+1
- 发送release,-1
- 发送retainCount,可以取到计数器的值
分类:
MRC和ARC
MRC-手动引用计数器(使用时要关闭ARC)
重写dealloc规范:必须调用父类的dealloc方法,并且要放到最后一行
有创建就要有对应的release
一个retain和一个release对应
setter规范
- (void)setCar:(Car *) car{
if(_car != car){
[_car release];
-car = [car retain];
}
}
dealloc规范
-(void)dealloc{
[_car releadse];
[super dealloc];
}
自动释放池
- 原理:存到自动释放池中的对象,在自动释放池被销毁的时候,会自动调用存储在该自动释放池中的所有对象的release方法(只是发送一次)
- 创建和使用:
@autoreleasepool { Person * p = [[[Person alloc] init] autorelease]; }
只有里面调用autorelease才能自动释放(同一个对象调用几次,发送几次release)
集合
在集合中存储数据时,引用计数自动加一,当集合销毁时,会自动减一
字符串
copy深浅拷贝
- 本身NSString自带的copy是浅copy(没有创建新对象)
- NSMutableString的copy是深copy,但产生的字符串对象不可变(创建了一个新对象)
- mutableCopy定义在NSObject中,是深copy并且字符串对象可变
常量区的字符串不会被回收,其引用计数器是一个超大的数,并且retain和release无效
堆区的字符串和普通对象一样,且引用计数器默认是1
ARC-自动引用计数器
系统自动retain、release、autorelease、dealloc
本质:当引用计数器为0时,自动释放
表象:只要没有强指针指向这个对象,这个对象就会立即回收
强弱指针
可以通过@property中的weak和strong来标识或者__strong __weak
默认情况下,所有指针都是强指针
ARC中当对象被回收时,弱指针的值被设为nil
ARC中的内存泄漏
和下面的循环引用同理。
解决:一个使用strong一个使用weak
ARC与MRC的兼容
在编译设置->Build Phasses->Complie Sources中,选择要修改的类,在flags中写入 -fno-objc-arc即可让此类不用ARC
MRC转化为ARC
点击菜单里的edit->convert->ARC 即可(慎用,因为只是简单的删除,可能会出错)
ARC与垃圾回收器的区别
- GC:程序在运行期间,存在垃圾回收器,不断扫描堆中的对象是否 无人使用
- ARC:编译时在合适的地方插入retain,无引用时回收
集合
ARC模式下集合的元素是一个强类型的指针
内存泄漏
一个对象没有被及时回收,一直留在内存里,直到程序结束才回收。
单个对象的内存泄漏
- 有对象创建物release
- retain和release数量不匹配
- 在不适当的时候给指针设为nil
- 在方法中为传入的对象不适当的retain
野指针
C:定义一个指针变量,没有初始化。变量的值是一个垃圾值,随机指向一块空间
OC:指针的对象已经被回收了
僵尸对象
一个已经被释放的对象,但是这块空间还未被分配给别人
通过野指针访问此对象时,有可能有问题(空间被分配给别人),也可能没有问题(空间还没有被分配)
存在一个僵尸对象的实时检查机制,打开后,只要访问的是僵尸对象,无论空间是否被分配,都会报错(非常消耗性能)
避免僵尸对象错误
当指针成为野指针后,将值设为nil(nil调用方法不会报错,访问属性会报错)
@class
当多文件开发时,两个类互相包含。如Person.h中包含Book.h,而Book.h中包含Person.h。这个时候就会出现循环引用问题,造成无限递归,导致编译无法通过
解决方案:
不使用#import,使用@class类名来标注这是一个类,在.m文件中在#import对方的头文件就可以使用了
与#import区别
- #import是将指定的文件的内容拷贝到写指令的地方
- @class并不会考呗任何内容,只是告诉编译器,这是一个类。
循环引用
A、B互为对象,两边都使用retain,就会发生内存泄漏
解决方法:
一端使用retain,另一端使用assign,在使用assign的那一端在dealloc中不再需要release
文件存储
字符串读写
- 将字符串内容写入到磁盘上的某个文件中
- (BOOL)writeToFile:(NSString *)path
//文件路径
atomically:(BOOL)useAuxiliaryFile
//YES 先将内容写到临时文件里,成功后再写到指定目录,安全,效率低
//NO 直接将内容写到指定文件, 不安全,效率高
encoding:(NSStringEncoding)enc
//指定写入使用的编码一般UTF8,NSUTF8StringEncoding
error:(NSError **)error;
//二级指针,传入NSError指针的地址
返回值为BOLL类型,返回是否成功写入
- 从指定的文件中读取字符串
+ (nullable instancetype)stringWithContentsOfFile:(NSString *)path
encoding:(NSStringEncoding)enc
error:(NSError **)error;
- 使用URL来读取字符串数据
- 优势:既可以读本地磁盘文件,又可以读网页文件、发FTP服务器上的文件
- 不同类型的URL地址写法不同
- 本地磁盘文件:file:///Users/admin/Desktop/XXX.xxx
- 网页地址;http://gz.xxxx.cn
- FTP文件:ftp://server.xxx.cn/xx/xxx.xx
- 将不同类型的地址封装在NSURL对象中
- 读
NSURL *u1 = [NSURL URLWithString:@"https://www.bilibili.com/video/BV1NJ411T78u?p=167&spm_id_from=pageDriver&vd_source=4852f0fd2838c9dc71dd11e6d14e8735"]; NSError *err; NSString *str = [NSString stringWithContentsOfURL:u1 encoding:NSUTF8StringEncoding error:&err]; NSLog(@"%@", str);
- 写
NSURL *u1 = [NSURL URLWithString:@"file:///Users/admin/Desktop/abc.txt"];
NSString *str = @"hello";
[str compare:str options: 1];
NSError *err;
BOOL res = [str writeToURL:u1 atomically:NO encoding:NSUTF8StringEncoding error:&err];
if(res == YES){
NSLog(@"写入成功");
}else{
NSLog(@"失败");
NSLog(@"%@",err.localizedFailureReason);
}
数组读写
- 保存到磁盘
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile A
PI_DEPRECATED_WITH_REPLACEMENT("writeToURL:error:", macos(10.0, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED));
- (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically
API_DEPRECATED_WITH_REPLACEMENT("writeToURL:error:", macos(10.0, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED));
- 从磁盘读取
+ (nullable NSArray<ObjectType> *)arrayWithContentsOfFile:(NSString *)path API_DEPRECATED_WITH_REPLACEMENT("arrayWithContentsOfURL:error:", macos(10.0, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED));
+ (nullable NSArray<ObjectType> *)arrayWithContentsOfURL:(NSURL *)url API_DEPRECATED_WITH_REPLACEMENT("arrayWithContentsOfURL:error:", macos(10.0, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED));
字典读写
- 存入磁盘
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile API_DEPRECATED_WITH_REPLACEMENT("writeToURL:error:", macos(10.0, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED));
- (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically API_DEPRECATED_WITH_REPLACEMENT("writeToURL:error:", macos(10.0, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED)); // the atomically flag is ignored if url of a type that cannot be written atomically.
- 从磁盘取出
NSMutableDictionary *newdict = [NSMutableDictionary dictionaryWithContentsOfFile:@"/Users/admin/Desktop/dict.plist"];
+ (nullable NSMutableDictionary<KeyType, ObjectType> *)dictionaryWithContentsOfFile:(NSString *)path;
+ (nullable NSMutableDictionary<KeyType, ObjectType> *)dictionaryWithContentsOfURL:(NSURL *)url;
- (nullable NSMutableDictionary<KeyType, ObjectType> *)initWithContentsOfFile:(NSString *)path;
- (nullable NSMutableDictionary<KeyType, ObjectType> *)initWithContentsOfURL:(NSURL *)url;
NSFileManager
管理文件的一个类
文件判断
- 判断指定文件或文件夹是否存在
- (BOOL)fileExistsAtPath:(NSString *)path;
- 判断路径是否存在并且判断是文件路径还是文件夹路径
- (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(nullable BOOL *)isDirectory;
YES 是文件夹
NO
- 判断指定的文件夹或文件是否可以读取(有些文件权限不够不能读取)
- (BOOL)isReadableFileAtPath:(NSString *)path;
- 判断指定的是否可以写入
- (BOOL)isWritableFileAtPath:(NSString *)path;
- 判断指定文件是否可以删除
- (BOOL)isDeletableFileAtPath:(NSString *)path;
文件获取信息
- 获取属性信息(要拿到特定的信息通过Key
- (nullable NSDictionary<NSFileAttributeKey, id> *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- 获取指定目录下的所有的文件和目录,子目录的子目录也可获得
- (nullable NSArray<NSString *> *)subpathsAtPath:(NSString *)path;
- 只拿到子目录
- (nullable NSArray<NSString *> *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
文件/目录 创建
- 指定路径创建文件
- (BOOL)createFileAtPath:(NSString *)path 路径
contents:(nullable NSData *)data 文件的二进制数据
attributes:(nullable NSDictionary<NSFileAttributeKey, id> *)attr; 指定创建文件的属性,默认为nil
实例:
NSString *str = @"lalalala";
NSDate *data = [str dataUsingEncoding:NSUTF8StringEncoding];
[manager createFileAtPath:@"/Users/admin/Desktop/la.txt" contents:data attributes:nil];
- 指定路径创建文件夹
- (BOOL)createDirectoryAtPath:(NSString *)path w
ithIntermediateDirectories:(BOOL)createIntermediates
YES 做一路创建(没有上级,可以直接创建) NO 不会一路创建(没有上级,不会创建)
attributes:(nullable NSDictionary<NSFileAttributeKey, id> *)attributes
error:(NSError **)error
API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
拷贝文件
- (BOOL)copyItemAtPath:(NSString *)srcPath
toPath:(NSString *)dstPath
error:(NSError **)error
API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
移动文件(剪切,重命名)
- (BOOL)moveItemAtPath:(NSString *)srcPath
toPath:(NSString *)dstPath
error:(NSError **)error
API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
删除文件
不会删到废纸篓,而是直接删除,谨慎使用
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
NSFileHandle
NSFileHandle类允许更有效的使用文件,可以实现如下功能:
- 对文件,执行读、写或更新读写操作;
- 在文件中查找指定位置;
- 从文件中读取特定数目的字节,或将特定数目的字节写入文件中
另外,NSFileHandle类提供的方法也可以用于各种设备或套接字。一般而言,我们处理文件时都要经历三个步骤:打开文件,获取一个NSFileHandle对象;对打开文件执行相关操作;关闭文件。
一般流程(下面还有实例)
一、只读读取文件内容
//NSFileHandle对文件内容进行操作
//获取沙盒中某txt文件的路径
NSString *homePath = NSHomeDirectory();
NSString *path = [homePath stringByAppendingPathComponent:@"Documents/file.txt"];
//以只读的方式打开文件生成文件句柄
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
//注:内存:内部存储器;硬盘:外部存储设备。从硬盘到内存(从文件到内容)叫做读,从内存到文件(硬盘)叫做写
//读取文件内容的两种方式
// NSData *data = [fileHandle readDataOfLength:3];
// data = [fileHandle readDataOfLength:5]; //继续上面3个字节后,继续读取5个字节
NSData * data = [fileHandle readDataToEndOfFile];//如果文件内容不是特别多,可以直接读取全部内容
二、只写修改文件内容
//NSFileHandle对文件内容进行操作
//获取沙盒中某txt文件的路径
NSString *homePath = NSHomeDirectory();
NSString *path = [homePath stringByAppendingPathComponent:@"Documents/file.txt"];
//以只写方式打开文件生成句柄
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:path];
[handle writeData:[@"Hello world!!!" dataUsingEncoding:NSUTF8StringEncoding]];//直接覆盖掉前面相应数量的字符
[handle truncateFileAtOffset:0];//将文件字节截短至0,相当于将文件清空,可供文件填写
[handle writeData:[@"Hello world." dataUsingEncoding:NSUTF8StringEncoding]];//填写文件
[handle seekToEndOfFile];//将读写指针设在文件的尾端
[handle writeData:[@"ni hao" dataUsingEncoding:NSUTF8StringEncoding]];
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"---%@",str);
常用方法
+(NSFileHandle*)fileHandleForReadingAtPath:path 打开一个文件用于读入
+(NSFileHandle*)fileHandleForWritingAtPath:path 打开一个文件用于写入
+(NSFileHandle*)fileHandleForUpdatingAtPath:path 打开一个文件用于读写
-(NSData*)availableData 从设备或者通道返回可用数据
-(NSData*)readDataToEndOfFile 读取其余的数据知道文件的末尾(最多UINT_MAX字节)-(NSData*)readDataOfLength:(NSUInteger)bytes 从文件中读取指定字节的内容
-(void)writeData:data 将data写入文件
-(unsigned long long)offsetInFile 获取当前偏移量
-(void)seekToFileOffset:offset 设置偏移量
-(unsigned long long)seekToEndOfFile 将偏移量定位到文件的末尾
-(void)truncateFileAtOffset:offset 讲文件的长度设置为offset字节
-(void)closeFile 关闭文件
方法fileHandleForWritingAtPath和fileHandleForUpdatingAtPath所指定的文件必须是已经存在的,否则返回nil,另外对于这两个方法中文件的偏移量都是为文件的开始。
方法readDataToEndOfFile每次从文件中读取最多UNIT_MAX字节的数据,这个量定义在
<limits.h>中。
实例:
#import <Foundation/Foundation.h>
int main(int argc, const charchar * argv[])
{
@autoreleasepool {
NSFileHandle *inFile,*outFile;
NSData *buffer;
NSString *fileContent = @"这些是文件内容,这些是文件内容,这些是文件内容,这些是文件内容,这些是文件内容";
NSFileManager *fm = [NSFileManager defaultManager];
//创建一个文件
[fm createFileAtPath:@"testFile.txt" contents:[fileContent dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
//创建一个需要写入的文件
[fm createFileAtPath:@"outFile.txt" contents:nil attributes:nil];
//读取文件
inFile = [NSFileHandle fileHandleForReadingAtPath:@"testFile.txt"];
//写入文件
outFile = [NSFileHandle fileHandleForWritingAtPath:@"outFile.txt"];
if(inFile!=nil){
//读取文件内容
buffer = [inFile readDataToEndOfFile];
//将文件的字节设置为0,因为他可能包含数据
[outFile truncateFileAtOffset:0];
//将读取的内容内容写到outFile.txt中
[outFile writeData:buffer];
//关闭输出
[outFile closeFile];
//验证outFile内容
NSLog(@"%@",[NSString stringWithContentsOfFile:@"outFile.txt" encoding:NSUTF8StringEncoding error:NULL]);
//创建一个新的文件用来循环写入
[fm createFileAtPath:@"outFile2.txt" contents:nil attributes:nil];
//打开一个新的输出
outFile = [NSFileHandle fileHandleForWritingAtPath:@"outFile2.txt"];
//设置一个循环写入10条数据,每条数据都再后面添加上而不是覆盖
for (int i = 0; i<10; i++) {
//将偏移量设置为文件的末尾
[outFile seekToEndOfFile];
//写入数据
[outFile writeData:buffer];
}
//验证内容
NSLog(@"outFile2:%@",[NSString stringWithContentsOfFile:@"outFile2.txt" encoding:NSUTF8StringEncoding error:NULL]);
//关闭所有
[outFile closeFile];
[inFile closeFile];
}
}
return 0;
}
文件存储-补充:
文件存储-补充
OC中的特性语法
Class
有基本的三个属性:类名、属性S、方法S
类是以class对象的形式存储在代码段中的,class中也有isa指针,这个指针指向存储父类的类对象
可以使用class调用类的类方法
拿到这个类对象
调用类的类方法class
调用对象的对象方法class
对象中的isa指针的值就是代码段中存储类的类对象地址
声明class指针的时候不需要加*,在typedef的时候已经加过
SEL
SEL就是selector选择器
SEL是一个数据类型(class的属性)(可以理解为类),因此要申请分配空间,用来存储方法的
拿到存储方法的SEL对象
SEL s = @selector(方法名)
SEL是typedef的类型,在自定义时加过*了
调用方法的本质-消息机制
过程:
[object init];
- 先拿到存储init方法的对象,也就是拿到存储init方法的SEL数据。SEL消息
- 将这个消息发送给object对象
- object对象接收到这个SEL消息后,就知道要调用方法
- 根据对象的isa指针找到存储类的类对象
- 找到这个类对象以后 在这个类对象中搜寻是否有和SEL数据相匹配的。如果没有就再找父类
手动发送SEL消息
- (id)performSelector:(SEL)aSelector;
无参方法
Person *p = [Person new];
SEL s = @selector(say);
[p performSelector:s];//与[p say]效果一样
有参方法
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
多个参数
将参数封装起来再调用- (id)performSelector:(SEL)aSelector withObject:(id)object;
点语法
当类的属性有getter和setter时
person.name = @"10";//原理,调用setter方法。getter同理
等价于
[person setName:10];
在getter和setter中慎用点语法,可能造成无限递归
- (void)setAge:(int)age{
self.age = age;//等价于[self setAge:age]
}
弱语言
OC是弱语言,编译器编译时语法检查不严格
int num = 12.2;
优点:灵活
缺点:太灵活
静态类型和动态类型
静态:指针指向本类对象
Animal *a = [Animal new];
动态:指针指向非本类对象
Animal *a = [Pig new];
Animal *a = @"animal";//用NSLog输出后是animal
NSObject
是一个万能指针,可以指向任意对象;
缺点:调用子类的独有方法时需要类型转换。
是Foundation中的一个类,是所有类的基类
id指针
万能指针,可以指向任意对象
id是个typedef自定义类型,在定义时已经加了*
id和NSObject的异同
相同点:万能指针, 都可以执行任意OC对象
不同点:通过NSObject指针调用对象的方法时,编译器会做编译检查
id指针调用时,直接通过检查。
id指针只能调用对象的方法,不能使用点语法
动态类型检测
判断当前对象中是否存在某一方法,避免调用不存在方法导致报错
- (BOOL)respondsToSelector:(SEL)aSelector;
判断当前类是否存在改类方法
+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
判断指定的类是否为某类或其的子类
- (BOOL)isKindOfClass:(Class)aClass;
判断是否是该类,不包括子类
- (BOOL)isMemberOfClass:(Class)aClass;
init-初始化方法
给对象赋一个初值(0,null,nil)
重写init
规范:
- 必须要先调用父类的init方法,然后将方法的返回值赋给self
- 调用init方法有可能会失败,如果失败返回的就是nil
- 判断父类是否初始化成功。判断self的值是否为nil,不为nil说明成功
- 如果初始化成功,就初始化当前对象
- 最后返回self的值
-(instancetype)init{
self = [super init];
if(self){
//初始化
}
return self;
}
init的返回值必须是instancetype,名字必须以initWith开头,方法的实现和init的要求一样。
category-类别、分类
将一个类分成多个模块,这样方便维护和管理
创建:
- 建立一个新文件,类型选择category,选择要要分类的类
- 会生成一个.h 和.m文件,文件名为:类名+分类名
- 添加的分类也分为声明和实现
@interface 本类名 (分类名) @end @implementation 本类名 (分类名) @end
使用:
- 需要把分类的头文件引进来
注意:
- 分类只能增加方法,不能增加属性
- 在分类中可以写@property但是不会自动生成私有属性,也不会生成getter和setter的实现。只会生成getter和setter的声明(不能生成可以手写)
- 在分类的方法实现中不可以访问本类的真私有属性(@property生成的),但是可以调用本类的getter和setter来访问
- 当分类中有和本类中同名的方法时,优先调用分类的方法,哪怕没有引入分类的头文件。如果多个分类中有相同的方法,优先调用最后的分类
作用:
- 把一个类分成多个模块
- 为一个已经存在的类添加方法
延展- Extension
- 是一个特殊的分类,是类的一部分
- 特点:没有名字、只有声明没有实现,与本类共享一个实现
语法:
@interface NSObject ()
@end
与分类的区别
- 分类有名字,延展没有,是一个匿名的分类
- 每个分类都有单独的声明和实现,而延展只有声明没有单独实现,和本类共享一个实现
- 分类中只能新增方法,而延展中任意的成员都可以写
- 分类中可以写@property,但是只会生成getter和setter的声明,延展中写@property会自动生成私有属性,也会生成getter和setter的声明和实现
应用场景
- 为类写一个私有的@property,相当于只生成了私有属性,没有声明setter和getter,实现了setter和getter
- 延展不会单独占一个文件,都是直接写在本类的实现文件中,相当于这个类的私有成员,只能在本类的实现中访问,外部不能访问
- 想为类定义真私有属性,方法(方便查看有哪些私有方法),@property时,使用
Block
block类型的变量中专门存储一段代码,这段代码中可以有参数,可以有返回值(类似于函数指针和lambda表达式)
格式
void (^myblock) () = ^void (){
NSLog(@"block");
};
void (^myblock) (int num) = ^void (int num){
NSLog(@"block=%d", num);
};
int (^myblock) (int num) = ^int (int num){
NSLog(@"block=%d", num);
return num;
};
简写
- 代码段的返回值时void可以省略,声明不能
void (^myblock) () = ^ (){
NSLog(@"block");
};
- 如果没有参数,代码段的括号可以省略
void (^myblock) () = ^{
NSLog(@"block");
};
- 声明block时,如果有指定参数,可以只写参数的类型,不写名称
int (^myblock) (int) = ^int (int num){
NSLog(@"block=%d", num);
return num;
};
- 可以省略代码段的返回值类型
NSString* (^myblock2) (NSString *) = ^(NSString* num){
NSLog(@"block2=%@", num);
return num;
};
block内部访问外部变量
- 在内部可以取定义在外部的变量值(局部、全局)
- 内部可以修改全局变量的值,但是不能直接修改定义在外部的局部变量值
- 如果一个外部的局部变量参数想让block内部修改,要加一个__block
block作为参数
typedef void (^NewType)();
void test(NewType block){//. void test(^(void)(){})
block();
}
NewType type = ^{};
test(type);
test(^{});
作为返回值
- 作为返回值时要使用typedef定义的短类型
block与函数
- 相同点:都是封装一段代码
- 不同点:
- block是一个数据类型,函数就是函数
- 可以声明block的变量
- block可以作为函数的参数,函数不能
协议
作用
- 专门用来声明一大堆方法(不能声明属性,也不能实现方法,只能写方法的声明)
- 只要某个类遵守了这个协议,就相当于拥有了这个协议中的所有方法的声明,不用自己去定义
声明
@protocol MyProtocol <NSObject>
-(void) run: (NSString *)str;
@end
使用
@interface Player : NSObject <Prot>{//类名:父类名 <协议名>
@private
NSString *_name;
int _score;
Type _selectType;
}
类是单继承的,但是协议可以多遵守(<XXX, XXX, XX>)
@required和@optional
- @required,被此修饰的方法,必须实现,不实现就爆警告
- @optional,不实现不回警告
不实现都不会报错
协议的继承
协议可以被继承,并且是可以多继承
@protocol MyProtocol <NSObject,XXX,XXX>
@end
NSObject
Foundation中还存在一个NSObject协议,NSObject协议被NSObject类遵守,所以NSObject协议中的所有方法,全部的OC类都拥有。NSObject协议叫做基协议。(协议名可以和类名重复)
协议类型限制
如果该对象没有实现该协议,会报警告
id<MyProtocol> obj = [Player new];
NSObject<MyProtocol> *obj = [Player new];
id<MyProtocol, YourProtocol> obj = [Player new];
非正式协议
为系统写的分类就是非正式协议
特性语法-补充
特性语法-补充
关于Xcode
查看文档:点击window找到documentation可以查看文档
右侧边栏中点击问号,然后在点击代码即可看到相关解释