1.理解NSProxy
ViewController *vc = [[ViewController alloc] init];
MJProxy:NSProxy
MJProxy *proxy1 = [MJProxy proxyWithTarget:vc];
MJProxy1:NSObject
MJProxy1 *proxy2 = [MJProxy1 proxyWithTarget:vc];
NSLog(@"%d %d",[proxy1 isKindOfClass:[ViewController class]], [proxy2 isKindOfClass:[ViewController class]]); 1 0
解释:
//[proxy1 isKindOfClass:[ViewController class]]不继承NSObject不走消息发送,直接走了消息转发
调用对象时vc
//[proxy2 isKindOfClass:[ViewController class]])调用对象是NSObject
@implementation MJProxy
+ (instancetype)proxyWithTarget:(id)target{
// NSProxy对象不需要调用init,因为它本来就没有init方法
MJProxy *proxy = [MJProxy alloc];
proxy.target = target;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation{
[invocation invokeWithTarget:self.target];
}
@end
@implementation MJProxy1
+ (instancetype)proxyWithTarget:(id)target{
MJProxy1 *proxy = [[MJProxy1 alloc] init];
proxy.target = target;
return proxy;
}
- (id)forwardingTargetForSelector:(SEL)aSelector{
return self.target;
}
@end
2.GCD不会产生循环引用,为什么?
diapatch_asyn^{ self }
因为只是产生了单方面的循环引用,GCD强引用了self,self没有强引用GCD。
Masonry的make同理不会。
3.如何解决NSTimer循环应用?
(1)使用GCDTimer,其跟系统内核有关,不会产生循环引用。
考虑 信号量,字典[@"1",timer]
(2)使用中间类 弱引用当前对象self和NSTimer
(3)使用NSProxy防止循环引用
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[MJProxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];
4.CADisplayLink、NSTimer应注意什么?
会导致循环引用,都依赖NSRunloop,当CPU需要处理大量事件时会会不精准。
5.内存布局是什么样子的?栈堆正好高低 就记住了
保留
低 代码段 编译后的代码
数据段 字符串常量
已初始化数据:已初始化全局变量、静态变量
未初始化数据:未初始化全局变量、静态变量
堆↓ alloc、malloc、calloc 分配内存空间越来越小 特点:程序员自己管理
高 栈↑ 局部变量 分配内存空间越来越大 特点:系统管理
内核区
6.TaggedPointer 技术:直接从指针提取数据,目的节省内存空间,使用效率的优化。
使用TaggedPointer 之前
number=0x10001 → 地址:0x10001 存储值:10
使用TaggedPointer 之后
number=0xb000a1 b和1是tag pointer中的a是值
当TaggedPointer技术存不下太大的值时又恢复成了number对象存值。
objc_msgSend能够识别Tagged Pointer,如NSNumber的IntValue方法,从中直接提取数据
NSNumber、NSDate、NSString用
7.ARC的本质?是转化成MRC
@property (copy) NSString *name;
- (void)setName:(NSString *)name {
if (_name != name) {
[_name release];先干掉旧的 再赋值新的
_name = [name copy];//_name = [name retain]; strong修饰变成retain其他不变
}
}
这两段代码会发生什么事?如何处理?有什么区别?
@property (strong, nonatomic) NSString *name;
// dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// for (int i = 0; i < 1000; i++) {
// dispatch_async(queue, ^{
// // 加锁
//5 self.name = [NSString stringWithFormat:@"abcdefghijk"];
// // 解锁
// });
// }
发生坏内存访问,因为第5句代码self.name时OC对象,在多线程下 [_name release]执行了2次即_name被释放了2次,第2次已经释放了又去访问。应加锁。
// dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// for (int i = 0; i < 1000; i++) {
// dispatch_async(queue, ^{
// 10 self.name = [NSString stringWithFormat:@"abc"];
// });
// }
不会发生坏内存访问。因为第10句self.name是taggedPointer不是OC对象,是指针赋值,没有调用setter方法,也不存在release。
8.理解MRC
4中情况 归根结底是第一种情况。
(1)一个alloc对应一个release或者autorelease,release在不用的时候release,怕忘记直接alloc后autorelease即可。
(2)一个@property (retain,nonatomic) NSMutableArray *data对应dealloc里的一个self.data = nil。
(3)一个self.data = [NSMutableArray array]和new对应dealloc里的一个self.data = nil,因为内部已经autorelease过了。
(4)非属性遵循(1)即可
+ (instencetype)array {
return [[[self alloc]init] autorelease];
}
@property (retain,nonatomic) NSMutableArray *data;
viewdidload{
self.data = [NSMutableArray array];
Person *person = [[[Person alloc]init]autorelease];
}
dealloc {
self.data = nil;
}
9.理解copy .一个copy相当于调用一个retain需要对应一个release,把copy后的对象当成一个全新的对象。
拷贝的目的:产生一个副本对象,跟源对象互不影响,修改了源对象,不会影响副本对象,修改了副本对象,不会影响源对象。
mian {
NSString *str1 = [[NSString alloc] initWithFormat:@"test11111111111111111"];
NSString *str2 = [str1 copy]; //相当于调用retain 浅拷贝,指针拷贝,没有产生新对象。
NSString *str3 = [str1 mutableCopy]; //深拷贝:内容拷贝,有产生新对象。
[str3 release];
[str2 release];
[str1 release];
}
alloc、new、copy、mutableCopy返回了一个对象,对应一个release和autorelease.
mutableCopy拷贝出来的一定是可变对象。
copy拷贝出来的一定是不可变对象。
mutableCopy一定是深拷贝。
copy原对象是不可变对象是浅拷贝,copy原对象是可变对象是深拷贝。
10.copy的实现原理?
@property (nonatomic,copy) NSArray *array; //copy->右边一定是不可变
//@property (nonatomic,copy) NSMutableArray *array //是错误的 会崩溃
- (void)setData:(NSArray *)data {
if (_data != data) {
` [ _data release];
_data = [data copy];
}
}
NSString为什么要用copy?可以从UI层面解释。
(copy) text
NSMutableString *str = [NSMutableString @'123'];
textField.text = str;
防止修改str的值textField.text 的值会发生变化。
一般情况下:
@property (nonatomic,strong) NSArray *array;
@property (nonatomic,copy) NSString *str;
11.自定义copy 实现原理
遵循NSCopying协议,实现- (id)copyWithZone:(nullable NSZone *)zone 方法。
.h
@interface Person : NSObject<NSCopying>
.m
- (id)copyWithZone:(nullable NSZone *)zone {
Person *person = [[Person alloc]init];
person.age = self.age;
person.weight = self.weight;
return person;
}
12.ARC帮我们做了什么?是llvm编译器和Runtime系统协作的结果。
__strong MJPerson *person1;
__weak MJPerson *person2;
__unsafe_unretained MJPerson *person3;
NSLog(@"111");
{
MJPerson *person = [[MJPerson alloc] init];
person3 = person;
}
NSLog(@"222 - %@", person3);
大括号结束llvm帮我们生成release代码。
程序运行时大括号结束person对象释放,Runtime清除当前对象person的弱引用指针。
13.weak指针实现原理?
当大口号结束时,person对象释放,会调用dealloc->rootdealloc系列调用,根据当前对象指针找到其在弱引用表中的索引,再将指向当前对象的所有弱引用指针全部清除。
14.extern如何使用?在类外部声明私有变量、方法,即可访问。
extern void person_test(void);
main() {
person_test();
}
Person.h person_test不在这里声明
Person.m
void person_test(){
NSLog(@"person - test");
}
15.autoreleasepool实现原理?从结构、push、autorelease、pop四方面去回答
底层数据结构:_AtAutoreleasePool、AutoreleasePoolPage
AutoreleasePoolPage管理autorelease对象
class AutoreleasePoolPage {
pthread_t const thread;
id *next; //指向下一个能存放autorelease对象的地址的内存区域
AutoreleasePoolPage *const parent;
AutoreleasePoolPage *child;
}
调用push方法会将一个POOL_BOUNDARY入栈,并且返回其存放的内存地址。
调用autorelease的对象都会入栈;
调用pop方法时传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY
16.autorelease对象在什么时机会被调用release?分2种情况。大括号结束,休眠前,退出runloop时候。
第1种情况
是由runloop来控制的,,它可能是在某次runloop循环中runloop休眠之前调用的。
viewdidload {
Person *person = [[[Person alloc] init] autorelease];
}
第2种情况
viewdidload {
@autoreleasepool{
Person *person = [[[Person alloc] init] autorelease];
}//在这里释放
}
iOS在主线程的Runloop中注册了2个Observer
第1个Observer监听了kCFRunLoopEntry事件,会调用objc_autoreleasePoolPush()
第2个Observer
监听了kCFRunLoopBeforeWaiting事件,会调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()
监听了kCFRunLoopBeforeExit事件,会调用objc_autoreleasePoolPop()
14.方法里有局部对象,出了方法后会立即释放吗?2中情况。由编译器生成的release、autorelease决定。
viewdidload {
Person *person = [[Person alloc] init];
}
(1)如果编译器 Person *person = [[Person alloc] init];在这里立刻生成了autorelease方法的话,可能是在某次runloop循环中runloop休眠之前释放的。
(2)如果是在方法结束之前生成了[person release]则是在方法结束后
网友评论