MST

星途 面试题库

面试题:Objective-C属性修饰符与多线程编程的关联及潜在问题

在多线程编程环境下,Objective-C的属性修饰符(strong、weak、copy)可能会引发哪些潜在问题?例如,在不同线程中对使用这些修饰符的属性进行访问和修改时,可能会出现什么情况?如何通过合理使用修饰符以及其他手段来避免这些问题,确保多线程环境下程序的稳定性和正确性?请结合实际代码示例进行说明。
13.2万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

strong修饰符潜在问题及解决办法

  1. 潜在问题:在多线程环境下,如果多个线程同时对一个strong修饰的属性进行赋值操作,可能会导致内存管理问题,比如对象被意外提前释放或过度释放。因为每个线程都可能认为自己是最后一个持有对象的线程从而释放它。另外,读写操作如果没有同步,可能会读到未完成赋值的“脏数据”。
  2. 示例代码
@interface MyClass : NSObject
@property (nonatomic, strong) NSString *myString;
@end

@implementation MyClass
@end

// 多线程操作
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);

MyClass *obj = [[MyClass alloc] init];
dispatch_async(queue1, ^{
    for (int i = 0; i < 1000; i++) {
        obj.myString = [NSString stringWithFormat:@"Value from queue1 - %d", i];
    }
});
dispatch_async(queue2, ^{
    for (int i = 0; i < 1000; i++) {
        obj.myString = [NSString stringWithFormat:@"Value from queue2 - %d", i];
    }
});
  1. 解决办法:可以使用锁(如@synchronizeddispatch_semaphore等)来同步对该属性的访问。
@interface MyClass : NSObject
@property (nonatomic, strong) NSString *myString;
@property (nonatomic, strong) id lock;
@end

@implementation MyClass
- (instancetype)init {
    self = [super init];
    if (self) {
        self.lock = [[NSObject alloc] init];
    }
    return self;
}
- (void)setMyString:(NSString *)myString {
    @synchronized(self.lock) {
        _myString = myString;
    }
}
- (NSString *)myString {
    @synchronized(self.lock) {
        return _myString;
    }
}
@end

weak修饰符潜在问题及解决办法

  1. 潜在问题:weak修饰的属性本身不会增加对象的引用计数,在多线程环境下,一个对象可能在某线程中被释放,而其他线程中还持有该weak指针。当访问这个已释放对象的weak指针时,会得到nil,可能导致程序逻辑错误,例如在期望对象存在并执行方法时发生崩溃。
  2. 示例代码
@interface ParentClass : NSObject
@property (nonatomic, strong) ChildClass *child;
@end

@implementation ParentClass
@end

@interface ChildClass : NSObject
@end

@implementation ChildClass
@end

// 多线程操作
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);

ParentClass *parent = [[ParentClass alloc] init];
dispatch_async(queue1, ^{
    parent.child = [[ChildClass alloc] init];
    // 模拟一些操作
    [NSThread sleepForTimeInterval:1];
    parent.child = nil;
});
dispatch_async(queue2, ^{
    [NSThread sleepForTimeInterval:0.5];
    ChildClass *weakChild = parent.child;
    // 这里weakChild可能已经是nil,如果继续操作可能崩溃
    [weakChild doSomeMethod]; 
});
  1. 解决办法:在使用weak指针前,需要进行非空判断,确保对象存在。
dispatch_async(queue2, ^{
    [NSThread sleepForTimeInterval:0.5];
    ChildClass *weakChild = parent.child;
    if (weakChild) {
        [weakChild doSomeMethod]; 
    }
});

copy修饰符潜在问题及解决办法

  1. 潜在问题:在多线程环境下,多个线程同时对copy修饰的属性进行赋值操作,可能会导致性能问题,因为每次赋值都需要进行一次拷贝操作。而且如果拷贝操作没有同步,可能会导致数据不一致。
  2. 示例代码
@interface MyClass : NSObject
@property (nonatomic, copy) NSString *myCopyString;
@end

@implementation MyClass
@end

// 多线程操作
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);

MyClass *obj = [[MyClass alloc] init];
dispatch_async(queue1, ^{
    for (int i = 0; i < 1000; i++) {
        obj.myCopyString = [NSString stringWithFormat:@"Value from queue1 - %d", i];
    }
});
dispatch_async(queue2, ^{
    for (int i = 0; i < 1000; i++) {
        obj.myCopyString = [NSString stringWithFormat:@"Value from queue2 - %d", i];
    }
});
  1. 解决办法:同样可以使用锁来同步对该属性的访问,减少不必要的拷贝操作,同时保证数据一致性。
@interface MyClass : NSObject
@property (nonatomic, copy) NSString *myCopyString;
@property (nonatomic, strong) id lock;
@end

@implementation MyClass
- (instancetype)init {
    self = [super init];
    if (self) {
        self.lock = [[NSObject alloc] init];
    }
    return self;
}
- (void)setMyCopyString:(NSString *)myCopyString {
    @synchronized(self.lock) {
        _myCopyString = myCopyString;
    }
}
- (NSString *)myCopyString {
    @synchronized(self.lock) {
        return _myCopyString;
    }
}
@end