用弱指针weak解决循环引用的问题,只针对block
CADisplayLink、NSTimer使用注意:
一.CADisplayLink,NSTimer会对target产生强引用,如果target又对他们产生强引用,那么就会引发循环引用
解决方案:
1.使用block
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 repeats:YES block:1{
[weakSelf test];
}];
2.使用代理对象(NSProxy)
GCD定时器
1.NSTimer依赖于RunLoop,如果RunLoop的任务过于繁重,可能会导致NSTimer不准时
2.而GCD的定时器会更加准时
//创建一个定时器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//设置时间(start是几秒后开始执行,interval是时间间隔)
dispatch_source_set_timer(
timer,
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(start * NSEC_PER_SEC)),
(uint64_t)(interval *NSEC_PER_SEC),
0);
//设置回调
dispatch_source_set_event_handler(timer, ^{
});
//启动定时器
dispatch_resume(timer);
代码示例:
- (void)viewDidLoad{
[super viewDidLoad];
//队列
dispatch_queue_t queue = dispatch_get_main_queue();
//创建定时器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//设置时间
uint64_t start = 2.0; // 2秒后开始执行
uint64_t interval = 1.0; // 每隔1秒执行
dispatch_source_set_timer(
timer,
dispatch_time(DISPATCH_TIME_MOW, start * NSEC_PER_SEC),
interval * NSEC_PER_SEC, 0);
//设置回调
diapatch_source_set_event_handler(timer, ^{
NSLog(@“1111”);
});
//启动定时器
dispatch_resume(timer);
}
使用CADisplayLink,NSTimer有什么注意点?
介绍下内存的几大区域?
讲一下你对iOS内存管理的理解?
ARC都帮我们做了什么?
weak指针的实现原理?
autorelease在什么时机会被释放?
方法里有局部对象,出了方法后会立即释放吗?
iOS程序的内存布局:图形示例如下:
1.代码段:编译之后的代码
2.数据段:
字符串常量:比如NSString *str = @“123”;
已初始化数据:已初始化的全局变量,静态变量等
未初始化数据:未初始化的全局变量,静态变量等
3.栈:函数调用开销,比如局部变量,分配的内存空间地址越来越小
4.堆:通过alloc,malloc,calloc等动态分配的空间,分配的内存空间地址越来越大
int a = 10; 这是已初始化数据
int a; 这是未初始化数据
NSString *str = @“123”; 这是字符串常量
静态变量和全局变量的内存都是只有一份的
iOS程序的内存布局图形示例如下:
//代码示例各种变量
#import <Foundation/Foundation.h>
int a = 10;//已初始化全局变量
int b; //未初始化全局变量
int main(int argc, const char * argv[]) {
@autoreleasepool {
static int c = 20;//已初始化静态变量
static int d; //未初始化静态变量
int e; //栈
int f = 20;//栈
NSString *str = @“123”;//字符串常量
NSObject *obj = [[NSObject alloc] init];//堆
}
}
Tagged Pointer 介绍和使用方法
1.从64bit开始,iOS引入了Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象的存储
2.在没有使用Tagged Pointer之前,NSNumber等对象需要动态分配内存、维护引用技术等,NSNumber指针存储的是堆中NSNumber对象的地址值
3.使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中
4.当对象指针的最低有效位是1,则该指针为Tagged Pointer
5.当指针不够存储数据时,才会使用动态分配内存的方式来存储数据
6.objc_msgSend能识别Tagged Pointer,比如NSNumber的intValue方法,直接从指针提取数据,节省了以前的调用开销
7.如何判断一个指针是否为Tagged Pointer?
iOS平台,最高有效位是1
Mac平台,最低有效位是1
NSNumber、NSDate、NSString等小对象—当对这些小对象进行赋值时,系统会自动对其进行init。比如:
NSNumber *number = @10; 等价于 NSNumber *number = [NSNumber numberWithInt:10];
思考以下2段代码能发生什么事?有什么区别?
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
for (int i = 0; i < 1000; i++) {
dispatch_async(queue, ^{
self.name = [NSString stringWithFormat:@“abcdefghijk”];
});
}
/*
@property (nonatomic,copy) NSString *name;
上面这一段代码运行会崩溃报坏内存访问,因为对self.name赋值会调用setter方法:
- (void)setName:(NSString *)name{
_name = name;
}
然而setter方法的底层实现是这样的:
- (void)setName:(NSString *)name{
[_name release];
_name = [name copy];//name创建时用copy这里就用copy,如果创建时用strong这里用retain,如果是assign那么这里就不会进行release直接赋值
}
所以当多个线程同时调用setter方法时有可能出现_name刚release但是还没有copy的时候其它线程又调用release,对释放掉的内存又进行操作就会报错
*/
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
for (int i = 0; i < 1000; i++) {
dispatch_async(queue, ^{
self.name = [NSString stringWithFormat:@“abc”];
});
}
/*
上面这一段代码运行正常,因为赋值的数值@“abc”比较小所以就使用了Tagged Pointer方法,因为这个方法是存储在指针地址里的,所以数值太大指针地址放不下时就会用其它方法:
*/
OC对象的内存管理
1.在iOS中,使用引用计数来管理OC对象的内存
2.一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象就会销毁,释放其占用的内存空间
3.调用retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1
4.内存管理的经验总结:
当调用alloc、new、copy、mutableCopy方法返回一个对象,在不需要这个对象时,要调用release或者autorelease来释放它
想拥有某个对象,就让它的引用计数+1;不想再拥有某个对象,就让它的引用计数-1
5.可以通过以下私有函数来查看自动释放池的情况
extern void _objc_autoreleasePoolPrint(void);
int age;–基本数据类型
Person *person;–OC对象类型
拷贝的目的:产生一个副本对象,跟源对象互不影响;修改了源对象,不会影响副本对象;修改了副本对象,不会影响源对象
iOS提供了2种拷贝方法:
copy:不可变拷贝,产生不可变副本
mutableCopy:可变拷贝,产生可变副本
可变的字符串是可以进行拼接的,不可变的字符串不可以拼接
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *str1 = [NSString stringWithFormat:@“test”];
NSString *str2 = [str1 copy];//返回的是NSString
NSMutableString *str3 = [str1 mutableCopy];//返回的是NSMutableString
}
}
NSString *str1 = [NSString alloc] initWithFormat:@“test”];
NSString *str2 = [str1 copy];//浅拷贝,指针拷贝,没有产生新对象
NSMutableString *str3 = [str1 mutableCopy];//深拷贝,内容拷贝,有产生新对象
NSMutableString *str1 = [NSString alloc] initWithFormat:@“test”];
NSString *str2 = [str1 copy];//深拷贝,内容拷贝,有产生新对象
NSMutableString *str3 = [str1 mutableCopy];//深拷贝
所以定义属性的时候如果用copy那么后面只能跟不可变的类型,如果非要跟可变类型,copy后也会变为不可变类型,这时如果你当成可变类型来用就会报错了。定义属性的时候没有mutableCopy,mutableCopy只针对某些特定的类才有的,比如NSArray等用来调用的方法,不是定义属性的。
NSTimer *_Nonnull timer ↩︎