个人学习笔记,仅作为个人记录
OC
1. C语言字符串的存储方式
a.使用字符串存储
b.使用指针存储
2. OC 中存储字符串类型:NSString
NSString类型的指针变量 专门用来存储OC字符串的地址
3. OC字符串必须使用1个前缀 @ 符号
“jack” 这个是一个c语言字符串
@“jack”这个是一个OC字符串常量
NSString 类型的指针变量,只能存储OC字符串的地址
NSString *str = @“jack”
4. @符号
1). 将C字符串转换为OC字符串
2). OC中绝大部分关键字都是以@符号开头
1. 将源码编译为目标文件
cc -c xx.m
a. 预处理
b. 检查语法
c. 编译
2. 链接
cc xx.o
如果程序使用到了框架中的函数或者类,那么链接的时候要指定框架函数或者类
cc xx.o -framework 框架名称
cc xx.o -framework Foundation
生成 xx.out 可执行文件
1. OC中的数据类型
1). OC中支持C语言所有数据类型
a. 基本数据类型
char int float double
b. 构造类型
数组 结构体 枚举
c. 指针类型
int *p
d. 空类型
void
e. typedef 自定义类型
2). BOOL类型
a. YES 或者 NO 任意一个数据。
b. BOOL类型存储条件表达式结构,条件成功为YES,不成功为NO。
c. BOOL 实际上是1个有符号的char变量
typedef signed char BOOL;
#define YES ((BOOL)1)
#define NO ((BOOL)0)
3). Boolean
a. Boolean 存储 true或者false
b. Boolean 类型存储条件表达式结构,条件成功为YES,不成功为NO。
typedef unsigned char Boolean;
#define true 1
#define false 0
4). class 类型 类
5). id类型 万能指针
6). nil 与NULL差不多
7). SEL 方法选择器
8). block 代码段
类
1. 语法
a.类的声明
@interface 类名 : NSObject {
// 属性变量
}
// 定义方法
- (返回值类型) 方法名 :(参数类型) 参数名称 :(参数类型) 参数名称;
@end
@implementation 类名
// 实现方法
- (返回值类型) 方法名 :(参数类型) 参数名称:(参数类型) 参数名称 {
// 方法具体实现
}
@end
b. 示例
@interface Person : NSObject {
@public
NSString *_name;
int _age;
float _height;
}
- (void) run;
- (int) sum:(int) num1 :(int) num2;
@end
@implementation Persion
- (void) run {
NSLog(@"I am %@, running", _name);
}
- (int) sum:(int) num1 :(int) num2 {
return num1 + num2;
}
@end
Person *person = [Person new];
person._name = "name";
(*person)._age = 18;
NSLog(@"person._name = %@", person->_name)
NSLog(@"person._age = %d", (*person)._age)
// 方法调用
[person run]
[person sum: 10 : 20]
内存五大区域
栈 存储局部变量
堆 申请的字节空间 malloc calloc realloc函数
BSS段 存储未被初始化的全局变量 静态变量
数据段(常量区) 存储已被初始化的全局 静态变量 敞亮数据
代码段 存储代码
类加载
a.创建对象
b.申明类指针
c.加载类代码在代码段中,只有类第一次访问时才会加载,直到程序结束才会释放
对象内存申请
NSObject *p;
在栈内存中申请1个NSObject类型的指针变量 p,p是指针类型,只能存放地址。
[NSObject new]
a. 在堆内存申请一块大小合适的空间
b. 在这个空间根据类的模版创建对象
类中定义的属性一次申明在对象中,
对象中有个属性 isa 指针,指向对象所属类在代码段中的地址
c. 初始化对象的属性
对象属性的默认值
属性类型是基本类型 默认值为0
属性类型是C指针类型 默认值是NULL
属性类型是OC指针类型 默认值是nil
NULL和nil
1. NULL
可以作为指针变量的值,代表指针不指向任何空间
NULL等价于0 NULL是一个宏 为0
2. nil
只能作为指针的值,代表指针变量不指向内存中的任何空间
nil等价于0 也是一个宏 为0
所以NULL和nil是一摸一样的。
建议:
C指针使用NULL
int *p = NULL;
OC指针使用nil
NSObject *p = nil;
如果一个对象为nil,调用属性报错,调用方法不会执行但不报错。
xcode 分组导航
#pragma mark 分组名
在导航栏对应位置显示1个标题
#pargma mark -
在导航栏对应位置显示一个水平分割线
#pragma mark - 分组名
在导航栏对应位置显示一个水平分割线,再显示1个标题
易错点
异常处理
@try {
} @catcg(NSException *e) {
NSLog(@"%@", e)
}
@try 无法处理c语言的错误
判断类是否有指定方法
Person *p = {Person new];
if ([pi respondsToSelector:@selector(sayHi)]) {
[p1 sayHi];
}
类方法
1. 声明
a. 对象方法申明使用 -
- (void) 方法名:(参数类型) 参数名;
b. 类方法申明使用 +
+ (void) 方法名:(参数类型) 参数名;
2. 代码规范
创建一个需要申明一个与类名一致的类方法来创建对象。
%p 打印指针变量的值
%@ 打印指针变量指向的对象
+ (instancetype) 方法名:(参数类型) 参数名
instancetype 表示返回当前这个类的对象
NSString 字符串
创建字符串
NSString *str = [NSString new]; // 创建空白字符串对象
NSString *str = [NSString string]; // 创建空白字符串对象
格式化字符串
NSString *str = [NSString stringWithFormat:@"xxx %@", @"11"];
字符串长度 NSUInteger = unsigned long 类型
NSUInteger len = [str lenght]; // 字符串长度
获取字符串下标对应的值
unichar ch = [str characterAtIndex:2];
NSLog(@"ch = %C",ch);
字符串判断
BOOL result = [str1 isEqualToString:str2]
字符串比较
NSComparisonResult result = [str1 compare:str2];
NSComparisonResult是枚举类型,-1:小于 0:等于 1:大于
也可以用 int来接受返回值
static 关键字
C语言中的static
a. 修饰局部变量
b. 修饰全局变量
c. 修饰函数
OC中的static关键字
a. static不能修饰属性,也不能修饰方法
b. static可以修饰方法中的局部变量
如果在方法中使用static修饰变量,这个变量变成静态变量
存储在常量区,方法执行完毕不会回收,下次执行直接使用
@interface Student : NSObject {
int _number;
}
+ (instancetype) student;
@end
@implementation Student
+ (instancetype) student {
static int number = 1;
Student * stu = [Student new];
stu->_number = number++;
return stu;
}
@end
// static 修饰的number变量为静态变量。
self 关键字
调用自身的属性和方法
self 和 java中this相似。
self 是一个指针,指向自己本身
self在类方法中使用:
a. 类加载 当类第一次被访问会将类的代码存储在代码区
代码区中用来存储类的空间的地址,即isa属性
类方法中self相当于类名
对象方法中self相当于当前对象
继承
@interface Person : NSObject {
}
@end
@implementation Person
@end
单继承:子类拥有父类所有属性和方法和类方法
子类不可以覆盖父类属性
可以使用super关键字调用父类的方法
super只能用来调用父类的方法和类方法,不能访问属性。
修饰符
修饰符只能修饰属性,不能修饰方法。
@interface Person : NSObject {
@private
NSString * _name;
int _age;
@package
int _x;
int _y;
@protected
int _scan;
@public
int _display;
}
@end
@implementation Person {
// 这里定义的属性,外界无法感知
int _isShow;
}
@end
description (toString)
@"%p" 打印指针的值
@"%@" 打印指针指向的对象
重写 - (NSString *) description; 方法
SEL 选择器
1. SEL 全称叫 SEL 选择器
SEL 是一个数据类型
2. 类是以Class对象的形式存储在内存中
类名:NSString 存储类名
方法:
a. 创建1个SEL对象
b. 将方法信息存储在这个SEL对象中
c. 将这个SEL对象作为类对象的属性
3. 拿到存储方法的SEL对象
1. 因为SEL是一个typedef类型的 在自定义的时候已经加了 * 号
所以声明SEL指针的时候 不需要加 *
2. 获取存储方法的SEL对象
SEL s1 = @selector(方法名);
4. 调用方法的本质
[p1 sayHi];
内部原理:
a. 拿到存储sayHi方法的SEL对象,拿到存储sayHi方法的SEL数据,SEL消息
b. 将这个SEL消息发送给p1对象
c. p1对象接收到SEL消息后,调用方法
d. 根据对象的isa指针找到存储类的类对象
e. 找到类对象,搜寻是否有与传入SEL数据匹配的,有则执行,没有则到父类中找,
直到NSObject 没有则报出异常。
OC最重要的1个机制:消息机制
调用方法的本质其实就是为对象发送SEL消息。
5. 手动调用SEL
Person *p = [Person new];
SEL sel = @selector(sayHi);
[p performSelector:sel];
@interface Person {
NSString * _name;
}
- (void) sayHi;
- (void) eat:(NSString *) food;
@end
@implementation Person {
}
- (void) sayHi {
NSLog(@"Hi!");
}
- (void) eat:(NSString *) food {
NSLog(@"eat %@", food);
}
@end
Person * p = [Person new];
[p sayHi]
SEL s1 = @selector(sayHi);
[p performSelector:s1]; // 手动调用
// 手动调用有参数方法,有参方法在方法名后加冒号
SEL s2 = @selector(eat:);
[p performSelector:s2 withObject:@"apple"];
点语法
@interface Person : NSObject {
NSString * _name;
int _age;
}
- (void) setName:(NSString *) name;
- (NSString *) name;
- (void) setAge:(int) age;
- (int) age;
@end
@implementation Person
// getter setter 实现
@end
Person *p = [Person new]
p.name = @"233"; // 相当于 [p setName:@"233"];
NSString *name = p.name; // NSString *name = [p name];
要是用点语法,需要属性按照规范封装 getter/setting 方法
@property 自动生成 getter、setter方法声明
@interface Person : NSObject {
NSString * _name;
int _age;
}
// 自动生成 age getter、setting声明
@property int age;
@end
@synthesize 自动生成 getter、setter方法实现
@implementation Person
// 自动生成 age getter、setting实现
@synthesize age;
@end
------------- 相当于 -------------
@implementation Person {
int age
}
- (void) setAge:(int) age {
self->age = age;
}
- (int) age {
return age;
}
@end
1. @synthesize age; 在实现中生成1个真私有属性,属性的类型、名称和
@synthesized对应的@property一致。
2. 自动生成setter方法的实现
实现的方式:将参数直接赋值给自动生成的那个私有属性。
3. 自动生成getter方法的实现
实现的方式:将生成的私有属性的值返回。
@implementation Person
// 自动生成 age getter、setting实现
@synthesize age = _age;
@end
------------- 相当于 -------------
@implementation Person {
int _age; // 真私有属性,只能在本实现类中使用
}
- (void) setAge:(int) age {
_age = age;
}
- (int) age {
return _age;
}
@end
1. @synthesize age = _currentAge;
不会自动生成 _age 属性,而是生成getter和setter方法使用 _currentAge 属性
如需做数据校验,可以按照规范在类的实现中重写对应的方法
@implementation Person
// 自动生成 age getter、setting实现
@synthesize age = _age;
// 如需做数据校验,可以按照规范重新对应的方法
- (void) setAge:(int) age {
_age = age > 0 ? age : 0;
}
@end
批量申明
@interface Person {
int _x;
int _y;
}
// 同种类型属性可以放在一起写
@property int x,y;
@end
@implementation Person
@synthesize x,y;
@end
增强@property
只需写一个 @property 类型 属性名 编译器会自动
a. 生成私有属性 类型 _属性名
@property增强
id指针
1. id 指针:万能指针,可以指向任意OC对象。
a. id是一个typedef 自定义类型,在定义时已添加 *,所以声明id指针不需要加*
b. id 是一个万能指针,可以指向任意OC对象。
2. NSObject 和 id 的异同
相同:万能指针 都可以指向任意OC对象。
不同:通过NSObject指针调用对象的方法时编译器会做编译检查。
通过id指针调用对象的方法时编译器直接通过。
注意:id指针只能调用对象的方法,不能使用点语法。
NSObject *p = [Person new];
[(Person *)p sayHi];
id id1 = [Person new];
[id1 sayHi]; // 使用id指针可以调用任意方法不做检查
3. 使用id返回对应类型
@implementation Person
+ (id) create {
return [self new];
}
@end
将id作为返回值,任意指针都可以接受,建议使用 isntancetype
判断指针对象是否存在方法
id id1 = [Person new];
BOOL result = [id1 respondsToSelector:@selector(sayHi)];
判断指定对象是否是该类或其子类的对象
BOOL result = [id1 isKindOfClass:[Person class]];
判断指定对象是否是某个类的对象(不包括子类对象)
BOOL result = [id1 isMemberOfClass:[Person class]];
判断指定对象是否是某个类的子类
BOOL result = [id1 isSubclassOfClass:[Person class]];
判断类是否存在方法(类方法)
BOOL result = [Person instancesRespondToSelector:@selector(sayHi)];
注意:以上的判断针对类 @implementation 实现的方法为准。
init构造方法
Person *p = [Person new];
// 相当于一下操作
Person *p2 = [Person alloc];
Person *p3 = [p2 init];
// Person *p = [[Person alloc] init];
1. init方法:构造方法
作用:初始化对象,为对象属性赋初始值
@implementation Persion
- (instancetype) init {
self = [super init]; // 必须先调用父类 init 方法
if(self != nil) {
// if (self = [super init]) {
self.name = @"233";
}
return self;
}
@end
2. init 自定义构造方法:要在头文件定义自定义构造方法
@interface Person
@property NSString *name;
@property int age;
- (instancetype) initWithName:(NSString *) name andAge:(int) age;
@end
@implementation Persion
- (instancetype) initWithName:(NSString *) name andAge:(int) age {
if(slef = [super init]) {
self.name = name;
self.age = age;
}
}
@end
Person *p = [[Person alloc] initWith:@"233" andAge:2]
构造方法规范:
1. 申明不同构造方法时,要申明对应参数的类方法。
2. 类构造方法规范-需要调用autorelease。
内存管理
只有OC对象需要做内存管理。
1. 引用计数器:retainCount
每个对象都有一个计数器,计算用多少个持有改应用。对象初始化retainCount为1.
2. 如何操作引用计数器
a. 为对象发送一条 retain 消息,对象引用计数器加1.
b. 为对象发送一条 release 消息,对象饮用计数器减1.
c. 为对象发送一条 retainCount 消息,就可以获取对象引用计数器的值。
当对象的计数器为0时,对象会被系统立即回收。
当对象被回收时,会自动调用对象的dealloc方法。
3. 内存管理分类
MRC:Manual Refernce Counting 手动引用计数,手动管理内存管理。
当 使用或减少 对 对象实用的时候,要求手动发送 retain或release 消息。
ARC:Automatic Reference Count 自动引用计数,自动内存管理。
系统自动在合适的地方发送 retain release 消息。
2011年之后才有
关闭ARC,ARC默认开启,需要在项目xcodeproj-->对应的TARGETS-->Build Setting--〉Basic
Objective-c Automatic Reference Counting --> 设置为no
4. 内存管理原则
1. 对象创建,要匹配一个release
2. retain次数和release次数要匹配
3. 谁负责retain, 谁负责release
4. 只有在多一个人使用者用的时候才retain,少一个使用者的时候才release
有始有终,有加有减,有一个retain就要匹配一个release,一定要平衡。
5. 重写dealloc:需要在方法末尾调用父类dealloc。
@interface Person
@property NSString * name;
@property int age;
@end
@implementation Person
- (void) dealloc {
NSLog(@"%@ dealloc", self.name);
[super dealloc]; // 需要在方法末尾调用父类dealloc
}
@end
int main() {
Person *p = [Person new];
NSLog(@"p.retainCount = %lu", [p retainCount]); // p.retainCount = 1
[p retain];
NSLog(@"p.retainCount = %lu", [p retainCount]); // p.retainCount = 2
[p release];
NSLog(@"p.retainCount = %lu", [p retainCount]); // p.retainCount = 1
[p release];
NSLog(@"p.retainCount = %lu", [p retainCount]); // p.retainCount = 0
}
当 p.retainCount = 0时, p变成野指针, p之前指向的对象地址区域变成僵尸对象.
野指针
内存泄露:手动MAR 标准写法
@interface Person {
NSString *_name;
}
-(void) setName:(NSString *) name;
@implementation Person
-(void) setName:(NSString *) name {
if (_name != name) {
[_name release];
_name = [name retain];
}
}
-(void) dealloc {
[_name release];
[super dealloc];
}
@end
@property 参数
1. @property可以带参数
@property(参数1,参数2) 参数类型 名称;
2. @property 参数介绍
a. 与多线程相关
atomic、nonatomic
b. 与生成的setter方法实现相关
assign、retain
c. 与生成只读、读写相关
readonly、readwrite
d. 与生成getter、setter方法名字相关
getter、setter
3. 与多线程相关
atomic:默认值,生成的setter会加上一把线程安全锁,安全、效率低。
nonatomic:不会在setter加线程锁,不安全、效率高
@property(nonatomic) int age;
4. 与生成的setter方法实现相关
assign:默认值 生成的setter实现 直接赋值。
retrain:生成的setter实现标准MRC内存管理代码。还需手动在dealloc中release
@property(retain) NSString *name;
5. 与生成只读、读写的封装
readwrite:读写方法
readonly:只有getter方法
@property(readwrite) int age;
6. 与生成getter、setter方法名相关
@interface Person
@property(getter=xxx,setter=ooo:) int age;
@end
Person *p = [person init];
[p ooo:18]; // 设置age值
[p xxx]; // 获取age值
p.age = 20; // 等于 [p ooo:20];
int age = p.age; // 等于 [p xxx];
除getter和setter外,同一组参数只能使用一种。
@class:互相引用头文件,使用@class Xxx;
1. @class 与 #import的区别
1. #import 会将制定的文件的内容拷贝到写指令的地方
2. @class 不会拷贝内容。只会告诉编译器这是一个类
在头文件使用 @class Xxx; 需要在实现文件 #import “Xxx.h” 才会有提示。
循环retain
1. 当两个对象互相引用的时候
A对象属性有B对象,B对象属性有A对象
这时候两边都retain,会发生内存泄漏
2. 解决方法:1端使用retain 另外1端使用assing
自动释放池:统一释放对象
@interface Person : NSObject
@property(nonatomic,retain) NSString *name;
@end
@implementation Person
-(void) dealloc {
[self.name release];
[super dealloc];
}
@end
int main() {
Person *per = [Person new];
@autoreleasepool {
[per autorelease];
Person *p = [Person new];
[p autorelease]; // 将对象存储到当前自动释放池
Person *p1 = [[Person new] autorelease];
}
}
1. 在@autoreleasepool范围内调用对象的autorelease方法才可以生效
2. 在自动释放池结束后仅仅是调用托管对象的release方法。
3. 在自动释放池多次调用 autorelease 方法,在自动释放池结束时会调用对象多次release
自动释放池嵌套
@autoreleasepool {
Person *p1 = [[Person new] autorelease];
@autoreleasepool {
Person *p2 = [[Person new] autorelease];
@autoreleasepool {
Person *p3 = [[Person new] autorelease];
Person
}
}
}
p1、p2、p3 在各自自动释放池结束仅仅调用一次 release 方法。
ARC机制
强指针(默认)
__strong Person *p1 = [Person new];
弱指针
__weak Person *p2 = [Person new];
1. 当没有任何强指针指向对象
a. 指向对象的所有指针被回收。
b. 指向对象的指针等于nil。
控制@property生成的属性强弱类型
@property(strong) NSString * name;
@property(weak) NSString * name;
2. ARC模式下对象循环引用
解决方法:其中一方属性使用weak弱引用
回顾:MRC模式下,@property(retain) 循环引用时,其中一方属性使用 将retain修改为assign。
ARC和MRC的兼容
在项目中修改单独类使用MRC模式
项目 --> Build Phases --> Compile Sources --> 双击需要修改模式的类,输入 -fno-objc-arc
将整个MRC转成ARC
Edit --> convert -->
ARC机制与垃圾回收机制的区别
1. GC : 程序运行期间,垃圾回收器不断扫描堆中的对象是否无人使用。
ARC : 编译时在合适地方插入retain、release等
分类
new file --> Objective-C File --> 输入文件名、选择Category、Class 选择本类
Person.h
@interface Person : NSObject
@property NSString * name;
@end
Person.m
@implementation Person
@end
Person+Life.h
@interface Person (Life)
-(void) eat;
@end
Person+Life.m
@implementation Person (Life)
-(void) eat {}
@end
1. 分类中只能增加方法,不能增加属性
2. 在分类中使用 @property 只会自动生成申明,不会自动生成私有属性和getter、setter实现方法,
3. 在分类的方法实现中不能访问本类的真私有属性(@implementation),可以调用getter、setter方法访问。
4. 分类中可以存在和本类同名方法。优先调用分类的方法(即使没有引入分类头文件也一样)。
如果多个分类中有相同的方法,优先调用最后面编译分类的方法(Compile Sources中的顺序)。
当一个类方法很多很杂,可以把相似功能放在分类模块中。
二. 分类的第二个作用:为1已经存在的类添加扩展方法
为 NSObject 添加 showInfo 打印信息方法
@interface NSObject (Ext)
-(void) showInfo;
@end
@implementation NSObject (Ext)
-(void) showInfo {
NSLog(@"showInfo + %@", [self class]);
}
@end
延展:Extension 特殊的分类
延展使用场景 : 不会单独使用,一般写在实现类中,为实现类定义私有属性和私有方法
延展使用场景 (不会单独使用,一般写在实现类中,为实现类定义私有属性和私有方法)
1. 延展:Extension
1). 是一个特殊的分类,延展也是类的一部分
2). 特殊之处:
a. 延展这个特殊的分类没有名字
b. 只有申明没有实现,跟本类共享实现类
2. 延展语法
@interface 本类名 ()
@proerty Xxx xxx;
@end
3. 延展和分类的区别
1). 分类有名字,延展没有名字,是一个匿名的分类.
2). 每一个分类有单独的申明和实现,延展只有申明没有单独的实现和本类共用1个实现.
3). 分类只能新增方法, 延展可以新增属性和方法定义.
4). 分类中写 @property 只会生成getter、setter申明.
延展中写 @property 会自动生成私有属性并生成getter、setter属性.
4. 延展使用场景 (不会单独使用,一般写在实现类中,为实现类定义私有属性和私有方法)
- 与分类互补,延展定义 @property 属性, 分类定义实现扩展方法.
- 可以将延展写在本类实现中,相当于实现类的私有属性.
- 可以将延展写在本类实现中,在本类实现类中定义私有方法
Person.m
@interface Person () // 开发规范: 在延展定义私有属性和方法
@property(nonatimic,strong) NSString * name; // 外部无法访问
-(void) say; // 外部无法访问
@end
@implementation Person
-(void) say {
NSLog(@"%@ say", self.name);
}
@end
block
1. OC在c的基础上新增的数据类型
BOOL、Boolean、class、nil、SEL、id、block
2. block类型的变量专门用来存储1段代码.这段代码可以有参数和返回值
3. block变量声明
1. 声明格式
返回值类型 (^block变量名) (参数类型 参数名,...);
void (^myBlock)();
int (^myBlock2)();
int (^myBlock3)(int num1,int num2);
2. 代码段格式
^返回值类型 (参数列表) {
代码段;
}
3. 无返回值无参数代码段
^void(){ NSLog(@"123") };
myBlock = ^void() { NSLog("123") };
void(^myBlock) = ^void() { NSLog("123") }
4. 有返回值代码段
myBlock2 = ^int() { return 20; };
int (^myBlock2)() = ^int() { return 20 };
5. 有返回值和参数
myBlock3 = ^int(int num1, int num2) { return (num1 + num2); };
4. 执行block
block变量名();
myBlock();
int sum1 = myBlock2();
int sum2 = myBlock3(10, 20);
5. block 简写
1. 代码段没有返回值,可以省略void
void (^xxx)() = ^(){ NSLog(@"123"); };
2. 代码段没有参数,可以省略小括号
void (^xxx) = ^{ NSLog(@"123"); };
3. block 声明时,可以只写参数类型
int(^xxx)(int,int) = ^int(int num1, int num2) { return (num1 + num2); };
4. 代码段有返回值时也可以省略,编译器会根据代码段的retrun决定返回。
int(^xxx)() = ^{ retrun 20; };
6. 简化block变量的定义
typedef使用场景:将一个长类型定义为一个短类型
NSUInteger i = 10; // 等效于: unsigned long i = 10;
定义无参无返回值类型
定义: typedef void (^NewType)();
使用: NewType block1;
typedef int (^NewType2) (int, int);
NewType2 block2 = ^(int num1,int num2) { return 20; };
7. block代码块内部访问外变量
int num = 100, num1 = 100; // 全局变量
int main() {
int num = 200, num2 = 200; // 局部变量
__block int num22 = 200;
^{
int num = 300, num3 = 300;
NSLog(@"num = %d", num); // 300 就近原则
num1++; // block 可以操作全局变量
num2++; // 错误, block 不可以修改外部局部变量
num22++; // 可以修改 __block 修饰的外部局部变量
num3++; //
}();
}
8. block使用:作为方法参数
void test(void(^block1)(void)) { block1(); }
void test1(int(^blokck1)(int,int)) { blokck1(10, 30); }
int main() {
test(^{ NSLog(@"123"); });
test1( ^(int num1, int num2) { return num1 + num2; } );
}
@protocol 协议
Sprot.h
@protocol Sprot <NSObject>
-(void) run;
-(void) sleep;
@end
Person.h
#import"Sport.h"
@interface Person : NSObject <Sport>
@end
@implementation Person
-(void) run {}
-(void) sleep {}
@end
- 类是单继承,协议可以遵守多个协议
@interface 类名 : 父类名 <协议,协议,...>
@end
- 协议声明修饰
1. @required 遵守协议的类必须实现该方法,否则编译器发出警告
2. @optional 遵循协议的类可以实现也可以不实现该方法,编译器不会报警告
- 协议之间的继承
1. 格式
@protocol 协议名称 <父协议名称>
@end
2. 示例
@protocol Play <NSObject>
-(void) playLoL;
@end
@protocol SportProtocol <Play>
-(void) run;
@end
@interface Person : NSObject <SportProtocol>
@end
- 协议的类型限制
1. 申明遵守该协议的对象
NSObject<Play> *obj = [Person new];
id<Play> id1 = [Person new];
2. 申明遵守多个协议的对象
NSObject<Play, SportProtocol> *obj = [Person new];
id<Play, SportProtocol> id1 = [Person new];
Foundation框架
NSString
@define LogBOOL(var) NSLog(@"%@", var==YES?@"YES":@"NO")
1. NSString 恒定性
- 内存位置
@"233" 这种创建字符串对象是存放在数据段
[NSString new] 这种创建字符串对象是存放在堆区
- 无法修改
@"233" 存放在数据段内存中,无法修改
- 常量共享
NSString *str1 = @"233";
NSString *str2 = @"233";
stri1 和 str2 地址相同.
// C字符串 转 OC字符串
char *str = "233";
NSString * str1 = [NSString stringWithUTF8String:str];
// OC字符串 转 C字符串
char *str2 = str1.UTF8String;
2. NSString 常用方法
1. 将字符串内容写入文件
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile
encoding:(NSStringEncoding)enc error:(NSError **)error;
path:文件路径
useAuxiliaryFile:
YES:先写入临时文件,再写入目标文件(安全,效率低)
NO:直接写入目标文件(不安全,效率高)
enc:字符串编码
error: NSError类 二级指针
NSError *err;
BOOL result = [@"233" writeToFile:@"/Users/test.txt" atomically:NO encoding:NSUTF8StringEncoding error:&err];
if (err == nil) { // if (result) {
NSLog(@"write success");
} else {
NSLog(@"write fail: %@", err.localizedDescription);
}
2. 读取文件字符串
NSString *str = [NSString stringWithContentsOfFile:@"/Users/test.txt" encoding:NSUTF8StringEncoding error:&err];
3. 用 NSURL 来读取 本地文件 网页地址 ftp文件
a. 本地文件: file:///Users/xxx/test.txt
b. 网页地址: http://xxx.com
c. ftp地址: ftp://xxx/test.txt
NSURL *url = [NSURL URLWithString:@"https://juejin.cn/"];
NSString *str = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
NSLog(@"%@", str);
用 NSURL 写入文件(需要有写入权限)
[@"233" writeToURL:@"ftp///Users/xxx/test.txt" atomically:NO encoding:NSUTF8StringEncoding error:nil];
4. 字符串比较
NSComparisonResult result = [@"abc" compare:@"bcd"];
switch (result) {
case NSOrderedAscending:
NSLog(@">");
break;
case NSOrderedSame:
NSLog(@"=");
break;
case NSOrderedDescending:
NSLog(@"<");
break;
}
// 忽略大小写比较
result = [@"abc" compare:@"bcd" options:NSCaseInsensitveSearch];
// 比较格式固定字符串的数字 : Foo2.txt < Foo7.txt < Foo25.txt
result = [@"abc" compare:@"bcd" options:NSNumbericSearch];
5. 判断开头是否匹配
BOOL result = [@"233" hasPrefix:@"2"]; // YES
6. 判断结尾是否匹配
BOOL result = [@"233" hasSuffix:@"2"]; // NO
7. 搜索子字符串
- (NSRange)rangeOfString:(NSString *)searchString;
NSRange result = [@"123456" rangeOfString:@"45"];
NSLog(@"%lu", result.location); // 子字符串下表,找不到为 NSNotFound
NSLog(@"%lu", result.length); // 子字符串长度,没找到值为0
// 从后面开始搜索
result = [@"123123" rangeOfString:@"12" options:NSBackwardsSearch];
8. 字符串截取
[@"0123456" substringFromIndex: 3]; // 3456 从指定下表截取到最后
[@"0123456" substringToIndex: 3]; // 012 从第0个开始截取指定个数
[@"0123456" substringWithRange: (NSRange){2, 3}]; // 从第location个开始截取length个字符
9. 字符串替换
// 所有匹配的子字符串都会替换
[@"0123456" stringByReplacingOccurrencesOfString:@"23" withString:@"32"]
10.字符串数据转换
int i = @"123".intValue; // 123
int i = @"120aa120".intValue // 120
11.去除字符串前后空格
[@" 012 3456 " stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; // "12 3456"
12.转成大写 [@"abc" uppercaseString]; // "ABC"
13.转成小写 [@"ABC" lowercaseString]; // "abc"
14.字符串分割
- (NSArray<NSString *> *)componentsSeparatedByString:(NSString *)separator;
NSArray *arr = [@"000 111 222 333" componentsSeparatedByString:@" "];
NSLog(@"%@", arr); // [000, 111, 222, 333]
- NSRange 结构体 : 用来表示范围
typedef struct _NSRange {
NSUInteger location; // 子字符串下表,找不到为 NSNotFound
NSUInteger length; // 子字符串长度,没找到值为0
} NSRange;
a. NSRange 创建方式:
1). NSRange range.location = 3; range.length = 2;
2). NSRange range = {3, 2};
3). NSRange range = {.location = 3, .length = 2};
4). NSRange range = NSMakeRange(loc, len);
b. NSRange 格式化
NSString &str = NSStringFromRange(range); // {3, 2}
NSMutableString : 可变字符串
NSMutableString : NSString 继承 NSString
NSMutableString *str = [NSMutableString string];
for (int i=0; i < 100000; i++) {
[str appendFormat:@"@d", i];
}
- 使用建议
平时使用NSString,效率高.
做大量拼接使用NSMutableString
NSArray
NSArray
1. 是Foundation框架中的一个类.具备数组功能
2. 特点
a. 只能存储OC对象
b. 长度固定,无法新增和删除
c. 元素紧密相连,每个元素有自己的下表
d. 可以存储任意类型(id类型)
3. 创建
NSArray *arr = [NSArray arrayWithObject:@"233"]; // ["233"]
// ["233", "2"] 使用 arrayWithObjects 必须以nil结束,遇到nil结束
NSArray *arr = [NSArray arrayWithObjects:@"0",@"1", nil, @"3"];
// ["0", "1", "2"] 简写模式,不可以包含 nil(nil=0,不可以包含非OC)
NSArray *arr = @[@"0", @"1", @"2"];
4. 格式化为字符串
NSLog(@"arr = %@", arr) // [...]
5. 取值和赋值
NSArray *arr = @[@"0", @"1", @"2"];
[arr objectAtIndex:0]; // "0"
arr[0]; // "0"
6. 获取长度 arr.count
7. 是否包含对象
- (BOOL)containsObject:(ObjectType)anObject;
arr.containsObject:@"233"
8. 获取数组第一个对象: arr.firstObject;
9. 获取数组最后一个对象: arr.lastObject;
10.查找指定元素在数组中的下表:
- (NSUInteger)indexOfObject:(ObjectType)anObject;
NSUInteger result = [arr indexOfObject:@"233"];
没有找到返回 NSNotFound
11.遍历数组
a. for循环
for(int i=0; i < arr.count; i++) {
NSLog(@"%@", arr[i]);
}
b. 增强循环
for(元素类型 变量名 in 数组变量) {}
for(NSString *str in arr) {
NSLog(@"%@", str);
}
c. 使用Block: 如果想停止循环,可以讲 BOOL *stop 设置为 YES
- (void)enumerateObjectsUsingBlock:(void (NS_NOESCAPE ^)(ObjectType obj, NSUInteger idx, BOOL *stop))block);
12.将数组中的元素连接起来生成字符串
NSArray *arr = @[@"000", @"111", @"222", @"333"];
NSString *str = [arr componentsJoinedByString:@" "]; // "000 111 222 333"
13.数组持久化
NSArray *arr = @[@"123",@"456",@"789"];
BOOL result = [arr writeToFile:@"/Users/xxx/Desktop/abc.plist" atomically:NO];
arr = [NSArray arrayWithContentsOfFile:@"/Users/xxx/Desktop/abc.plist"];
NSMutableArray 可变数组
- NSMutableArray 是 NSArray 子类,具备 NSArray 数组的特点,
- NSMutableArray 可以动态新增和删除元素.
1. 创建
NSMutableArray *arr1 = [NSMutableArray new];
NSMutableArray *arr2 = [[NSMutableArray alloc] init];
NSMutableArray *arr3 = [NSMutableArray array];
NSMutableArray *arr4 = [NSMutableArray arrayWithObjects:@"00", @"11", nil];
NSMutableArray *arr5 = [NSMutableArray arrayWithArray:@[@"00", @"11"]];
2. 添加元素
[arr addObject:@"00"];
[arr addObject:@[@"000",@"111"]]; // ["00", ["000", "111"]]
[arr addObjectFormArray:@[@"000",@"111"]]; // ["00", "000", "111"]
指定下表插入元素 [arr insertObject:@"555", atINdex:2]; // 在下表2位置插入数据
3. 删除元素
- 根据下标删除 : [arr removeObjectAtIndex:1];
- 根据对象删除 : [arr removeObject:@"111"];
- 删除最后一个元素 : [arr removeLastObject];
- 删除所有对象 : [arr removeAllObjects];
NSNumber
- NSNumber 将基础数据类型包装成OC对象类型
1. 创建
NSNumber *intNum = [NSNumber numberWithInt:10]; // NSNumber *intNum = @10
NSNumber *floatNum = [NSNumber numberWithFloat:10.1]; // NSNumber *floatNum = @10.1
NSNumber *boolNum = [NSNumber numberWithBool:YES]; // NSNumber *boolNum = @YES
NSNumber *doubleNum = [NSNumber numberWithDouble:20.22]; // NSNumber *doubleNum = @20.22
NSArray *arr = @[@10, @10.1, @YES, @20.22];
int num = 10;
NSNumber number = @(num);
NSDictionay 字典 (键值对)
- NSDictionary 是数组,以键值对形式存储数据
- 键 : 只能是遵守 NSCoping 协议的对象, 例如: NSString ...
- 值 : 只能是OC对象
1. 创建
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"value",@"key", @"value1",@"key1", @"value1",@"key0", nil];
NSLog(@"%@", dict); // {key=value; key0=value1; key1=value1;}
// 简写模式
NSDictionary *dict = @{@"key":@"value", @"key1":@"value1", @"key0": @"value0"};
NSLog(@"%@", dict); // {key=value; key0=value1; key1=value1;}
2. 取值
NSString *str = [dict objectForKey:@"key"]; // 简写: dict[@"key"];
如果key不存在,怎返回 nil
3. 获取字典键值对个数
NSDictionary *dict = @{@"key":@"value"};
dict.count; // 1
4. 遍历字典数组
NSDictionary *dict = @{@"key":@"value", @"key1":@"value"};
for (NSString *str in dict) {
NSLog(@"%@ : %@", str, dict[str]); // str 是key
}
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
NSLog(@"%@ : %@", key, obj); // str 是key
}];
5. 字典数组 持久化
[dict writeToFile:@"/Users/owm/desktop/dict.plist" atomically:NO];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:@"/Users/owm/desktop/dict.plist"];
NSMutableDictionary 动态字典
- NSMutableDictionary 是 NSDictionary 的子类
- NSMutableDictionary 可以动态 增加 修改 删除 键值对
1. 创建
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:@{@"key":@"value"}];
NSLog(@"%@", dict);
2. 添加修改 : key没有则添加,有则替换对应value
[dict setObject:@"value1" forKey:@"key1"];
3. 删除
// 删除指定键值对
[dict removeObjectForKey:@"key"];
// 删除所有键值对
[dict removeAllObjects];
集合的内存管理
1. 集合: NSArray集合 NSDictionary字典集合 ... 统称集合
2. 在MRC模式下,将一个对象存储到集合中,会为这个对象引用计数器+1
当集合销毁时,会将集合中所有对象发送release消息.
3. 使用@[]或者@{}创建的集合已经被autorelease过
直接调用和类同名的类方法创建的对象 也是被autorelease过
4. 在ARC模式下,集合的元素是一个强类型的指针
NSFileManager
- NSFileManager 是一个类, 单例实现
1. 获取 NSFileManager 实例
NSFileManager *fm = [NSFileManager defaultManager];
// BOOL result = [fm fileExistsAtPath:@"/Users"];
BOOL result = NO;
2. 是否存在 , 是否文件夹
BOOL isDir = NO;
result = [fm fileExistsAtPath:@"/Users" isDirectory:&isDir];
NSLog(@"fileExistsAtPath = %@", (result == YES ? @"YES" : @"NO"));
NSLog(@"isDirectory = %@", result == YES ? @"YES" : @"NO");
3. 是否可以读取
result = [fm isReadableFileAtPath:@"/Users"];
NSLog(@"isReadableFileAtPath = %@", (result == YES ? @"YES" : @"NO"));
4. 是否可以写入
result = [fm isWritableFileAtPath:@"/Users"];
NSLog(@"isWritableFileAtPath = %@", (result == YES ? @"YES" : @"NO"));
5. 是否可以删除
result = [fm isDeletableFileAtPath:@"/Users"];
NSLog(@"isDeletableFileAtPath = %@", (result == YES ? @"YES" : @"NO"));
6. 获取文件属性
NSError *error = nil;
NSDictionary *info = [fm attributesOfItemAtPath:@"/Users" error:&error];
NSLog(@"info = %@", info);
NSLog(@"info.NSFileSize = %@", info[NSFileSize]);
NSLog(@"error = %@", error);
7. 获取文件夹下所有文件
NSArray *subFile = [fm subpathsAtPath:@"/Users/xxx/Desktop"];
NSLog(@"subFile = %@", subFile);
8. 创建文件
NSDate *data = [@"2333" dataUsingEncoding:NSUTF8StringEncoding];
// NSDate contents 二进制数据
result = [fm createFileAtPath:@"/Users/owm/Desktop/test1.txt" contents:data attributes:nil];
NSLog(@"createFileAtPath = %@", (result == YES ? @"YES" : @"NO"));
9. withIntermediateDirectories YES则一路创建
result = [fm createDirectoryAtPath:@"/Users/owm/Desktop/AA/BB/CC" withIntermediateDirectories:YES attributes:nil error:nil];
NSLog(@"createDirectoryAtPath = %@", (result == YES ? @"YES" : @"NO"));
10. 复制文件
result = [fm copyItemAtPath:@"/Users/.txt" toPath:@"/Users/xxx1.txt" error:nil];
NSLog(@"copyItemAtPath = %@", (result == YES ? @"YES" : @"NO"));
11. 移动文件
- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;
12. 删除文件
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error;
Foundation框架常用结构体
1. CGPoint (NSPoint)
1. 结构体定义
struct
CGPoint {
CGFloat x;
CGFloat y;
};
typedef struct CG_BOXABLE CGPoint CGPoint;
2. CGPoint 创建
CGPoint point = {20, 30};
point = {.x = 20, .y = 30};
3. Foundation 框架中提供函数快速创建
CGPoint point = CGPointMake(20, 30);
NSLog(@"point = %@", NSStringFromPoint(point));
// typedef CGPoint NSPoint;
NSPoint nsPoint = NSMakePoint(20, 30);
NSLog(@"nsPoint = %@", NSStringFromPoint(nsPoint));
2. CGSize (NSSize)
1. 结构体定义
自定义保存大小结构体
typedef struct {
double width;
double height;
} XSize;
XSize xSize = {20, 30};
// Foundation框架中定义好的 CGSize
struct CGSize {
CGFloat width;
CGFloat height;
};
typedef struct CG_BOXABLE CGSize CGSize;
typedef CGSize NSSize;
2. CGSize 创建
CGSize size = {100, 100};
3. CGRect (NSRect)
1. 定义
struct CGRect {
CGPoint origin;
CGSize size;
};
typedef struct CG_BOXABLE CGRect CGRect
typedef CGRect NSRect;
2. 创建
CGRect rect;
rect.origin = (CGOrigin) {10, 20}; // 当结构体包含结构体,不可以直接 {} 赋值
rect.size = (CGSize) {100, 200};
rect = CGRectMake(10, 20, 100, 30);
NSRect nsRect = NSMakeRect(10, 20, 100, 30);
NSValue
- NSRange CGPoint CGSize CGRect 是结构体, 无法存储在集合中
- 先将 结构体变量包装成 OC 对象 NSValue
CGPoint point = {20, 30};
CGSize size = {100, 100};
CGRect rect = CGRectMake(10, 20, 100, 30);
NSValue * valuePoint = [NSValue valueWithPoint:point];
NSValue * valueSize = [NSValue valueWithSize:size];
NSValue * valueRect = [NSValue valueWithRect:rect];
NSArray * array = @[valuePoint, valueSize, valueRect];
NSLog(@"%@", array);
NSDate
// yyyy 年份 4位
// MM 月份 2位
// dd 天
// HH 时 24小时制
// hh 时 12小时制
// mm 分 分钟
// ss 秒
NSDateFormatter * formatter = [NSDateFormatter new];
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
NSLog(@"%@", [formatter stringFromDate:[NSDate new]]);
NSDate *date = [formatter dateFromString:@"2021-06-15 15:22:08"];
NSLog(@"%@", date);
- 计算时间
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:3600];
NSLog(@"%@", date);
// 时间差
NSDate *start = [NSDate new];
NSDate *end = [NSDate new];
double interval = [end timeIntervalSinceDate:start];
NSLog(@"interval = %lf", interval);
// 单独获取年月日等
NSDate *date = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *com = [calendar components:NSCalendarUnitYear fromDate:date];
NSLog(@"yeas = %lu", com.year);
copy
无论在MRC还是ARC下, NSString 类型的 @property 参数使用 copy .
- 浅拷贝
NSString * str1 = @"2333";
NSString * str2 = [str1 copy];
- 深拷贝
NSMutableString *str1 = [NSMutableString stringWithFormat:@"2333"];
NSString *str2 = [str copy];
NSMutableString *str3 = [str1 mutableCopy];
NSString * str1 = @"2333";
NSString * str2 = [str1 mutableCopy];
在常量区的字符串对象计数器retainCount是一个极大值, 且不可以修改
在堆区的字符串对象计数器跟其他对象一直.
字符串对象如果浅拷贝, 对象计数器+1
字符串对象如果深拷贝, 对象计数器不变
@property(nonatamic, copy) NSString *name;
- 实现 NSObject copy 方法
实现 NSCopying 协议
@interface Person : NSObject <NSCopying>
- property(nonatomic, copy) NSString *name;
- property(nonatomic. assgin) int age;
@end
@implementation Person
-(id)copyWithZone:(NSZone *)zone {
// 浅拷贝
// return self;
// 深拷贝
Person *p = [Person new];
p.name = self.name;
p.age = self.age;
return self;
}
@end
Person *p = [Person new];
Person *p1 = [p copy];
单例模式
1. 单例模式 : 无论怎样创建,该类只有一个实例
@interface Person : NSObject
+ (instancetype) sharePerson;
+ (instancetype) defaultPerson;
@end
@implementation Person
+ (instancetype) allocWithZone:(struct _NSZone *)zone {
static id instance = nil;
if (instance == nil) {
instance = [super allocWithZone:zone];
}
return instance;
}
+ (instancetype) sharePerson {
return [self new];
}
+ (instancetype) defaultPerson {
return [self new];
}
@end
2. 单例模式规范
重写 allocWithZone 方法,返回单一实例,
提供获取实例的类方法 share类名 或者 default类名