面试题答案
一键面试分析可能出现的内存问题
- 内存泄漏:循环引用会导致对象之间相互持有强引用,使得垃圾回收器无法回收这些对象,即使它们在程序逻辑上不再被需要,从而造成内存泄漏。
- 数据竞争:多线程频繁访问和操作这些对象时,不同线程可能同时读写同一对象的属性,导致数据不一致,引发未定义行为。
解决方案
- 使用弱引用(weak)
- 弱引用不会增加对象的引用计数,当对象的强引用计数为0时,对象会被释放,而持有该对象的弱引用会自动被设置为nil。
- 在循环引用的场景中,将其中一个方向的引用改为弱引用,以打破循环。例如,在Objective - C中:
@interface ClassA;
@interface ClassB {
// 使用弱引用打破循环
__weak ClassA *a;
}
@end
@interface ClassA {
ClassB *b;
}
@end
- 块(block)的正确使用方式
- 在block中使用对象时,要注意避免循环引用。在Objective - C中,可以使用
__weak
修饰符来解决。例如:
- 在block中使用对象时,要注意避免循环引用。在Objective - C中,可以使用
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
// 访问strongSelf的属性和方法
[strongSelf doSomething];
}
};
这里先将self
用__weak
修饰,在block内部再用__strong
修饰,这样可以保证在block执行期间,self
不会被释放,同时又避免了循环引用。
3. 手动打破循环引用的时机
- 在对象生命周期结束前,手动打破循环引用。例如,在对象的
dealloc
方法中,将循环引用的对象设置为nil。
@implementation ClassA
- (void)dealloc {
self.b = nil;
}
@end
@implementation ClassB
- (void)dealloc {
self.a = nil;
}
@end
- 多线程数据竞争问题的解决
- 使用锁:可以使用互斥锁(
pthread_mutex_t
在C语言中,@synchronized
在Objective - C中)来保证同一时间只有一个线程可以访问共享对象。例如,在Objective - C中:
- 使用锁:可以使用互斥锁(
- (void)accessSharedObject {
@synchronized(self) {
// 访问和操作共享对象的代码
self.sharedObject.property = newValue;
}
}
- 使用队列:将对共享对象的操作放在一个串行队列中执行,以避免数据竞争。例如,在Objective - C中使用Grand Central Dispatch(GCD):
dispatch_queue_t queue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
// 访问和操作共享对象的代码
self.sharedObject.property = newValue;
});
代码框架展示
以下是一个简单的Objective - C代码框架,展示了如何综合应用上述方法:
#import <Foundation/Foundation.h>
@interface ClassB;
@interface ClassA : NSObject
@property (nonatomic, strong) ClassB *b;
- (void)doSomething;
@end
@interface ClassB : NSObject
@property (nonatomic, weak) ClassA *a;
- (void)doAnotherThing;
@end
@implementation ClassA
- (void)doSomething {
NSLog(@"ClassA doSomething");
}
- (void)dealloc {
NSLog(@"ClassA dealloc");
self.b = nil;
}
@end
@implementation ClassB
- (void)doAnotherThing {
NSLog(@"ClassB doAnotherThing");
}
- (void)dealloc {
NSLog(@"ClassB dealloc");
self.a = nil;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
ClassA *a = [[ClassA alloc] init];
ClassB *b = [[ClassB alloc] init];
a.b = b;
b.a = a;
// 使用块
__weak typeof(a) weakA = a;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__strong typeof(weakA) strongA = weakA;
if (strongA) {
[strongA doSomething];
}
});
// 模拟多线程访问
dispatch_queue_t queue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
@synchronized(a) {
// 访问和操作a的属性
// 这里可以添加具体的属性访问和修改代码
}
});
// 释放对象
a = nil;
b = nil;
}
return 0;
}
在这个代码框架中,通过__weak
修饰符打破了ClassA
和ClassB
之间的循环引用,使用块时避免了循环引用,通过@synchronized
和串行队列处理了多线程访问问题。同时在dealloc
方法中手动打破循环引用以确保对象能正确释放。