博客> 从mrc的角度再探Strong 与weak
从mrc的角度再探Strong 与weak
2017-11-24 19:05 评论:0 阅读:1051 早起的虫子被鸟吃
GCD 夯实基础 调试思路+技巧

晚上闲来无事,逛逛博客,有重大发现。感谢这位朋友的博客给我提供了新的思路。传送门

重要更新

因为有朋友认为代码瑕疵会导致内存爆增而导致崩溃,实际上开辟子线程并不是无限制的,亲测,ios平台最大并发线程为22,测试过程:条件断点 i == 1000触发,保证充分利用了系统的线程。

所以并不是内存爆增导致的崩溃。为了更严谨的证明实际是过度释放。笔者特地打开了僵尸对象模式[zombie Object],得到错误打印:向一个已销毁的对象发送release消息!

*** -[CFString release]: message sent to deallocated instance 0x60800064bd00

系统的东西我只是通过结果猜测。下面我们通过dispatch_semaphore_t控制线程的并发。 设置线程的并发数为5,运行得到的结果也是一样的,所以,毕竟还是不同线程之间争夺资源而导致的崩溃啊,少年。

dispatch_semaphore_t semQueue = dispatch_semaphore_create(5);
    dispatch_queue_t taskQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    for (int i = 0; i < 10000 ; i++) {
        dispatch_semaphore_wait(semQueue, DISPATCH_TIME_FOREVER);
        dispatch_async(taskQueue, ^{
            self.str = [NSString stringWithFormat:@"Monkey-Sun %d",i];
            NSLog(@"%@   %lu", self.str, (unsigned long)self.str.retainCount);
            dispatch_semaphore_signal(semQueue);
        });
    }

关于GCD,传送门

================我是华丽的分割线================

没事逛了逛Apple的官网,其中关于weak 和 Strong属性在ARC的运行时代码。

strong

void objc_storeStrong(id *object, id value) {
  id oldValue = *object;
  value = [value retain];
  *object = value;
  [oldValue release];
}

retain新值,release旧值

weak:如果值为空指针或其指向的对象已开始销毁,则将对象分配为空和未注册为weak对象。否则,将对象注册为weak对象或将其注册更新为指向值。

也就是说,weak指针没法保持对象的生命。

id objc_storeWeak(id *object, id value);

If value is a null pointer or the object to which it points has begun deallocation, object is assigned null and unregistered as a __weak object. Otherwise, object is registered as a __weak object or has its registration updated to point to value.

Returns the value of object after the call.

retain:如果值是空,则retain没有任何效果,如果不为空,则执行保持对象的操作。

id objc_retain(id value);

Precondition: value is null or a pointer to a valid object.

If value is null, this call has no effect. Otherwise, it performs a retain operation exactly as if the object had been sent the retain message.

Always returns value.

所以strong 和 weak的指针,根本区别在于,strong执行了retain操作,而weak没有。

下面思考一个问题,多线程的情况下,使用strong会导致什么问题。

Objective-C

code

@interface ViewController ()
@property (nonatomic, strong) NSString *str;
@end

-----.m
for (int i = 0; i < 10000 ; i++) {
        dispatch_async(dispatch_async(dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_DEFAULT), ^{
            self.str = [NSString stringWithFormat:@"Monkey-Sun %d",i];
                NSLog(@"%@   %lu", self.str, (unsigned long)self.str.retainCount);
        });
    }

这里会崩溃,因为,使用strong修饰的属性,实际上它的setter是这样的.

-(void)setStr:(NSString *)str{
    [str retain];
    [_str release];
    _str = str;
}

在多线程下,线程1,如果执行到了release,同时线程2也执行到了release,那么_str所指向的对象就会过度释放。

可以理解会,多线程中,并发的访问一个数据,就会导致数据出错。不过这里可以看到出问题的原因之一。

处理这个问题,有几个解决方案

OC: 使用互斥锁,保证数据访问的唯一性

@property (nonatomic, strong) NSString *str;

----.m

for (int i = 0; i < 10000 ; i++) {
        dispatch_async(dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_DEFAULT), ^{
            @synchronized (self) {
                self.str = [NSString stringWithFormat:@"Monkey-Sun %d",i];
                NSLog(@"%@   %lu", self.str, (unsigned long)self.str.retainCount);
            }
        });
    }

由于移动设备内存较小,性能也比mac来的差,所以,一般我们描述属性都是用:nonatomic,也就是非原子操作。所以OC中还可以使用atomic来修饰属性来保证数据访问的唯一性。

@property (atomic) NSString *str;

---.m
for (int i = 0; i < 10000 ; i++) {
        dispatch_async(dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_DEFAULT), ^{
            self.str = [NSString stringWithFormat:@"Monkey-Sun %d",i];
                NSLog(@"%@   %lu", self.str, (unsigned long)self.str.retainCount);
        });
    }

Swift

swift的原理跟OC一致,就不重复解释原理了,但是swift没有这个内存语言的修饰 比如以下代码是等价的。

---this code from swift-3
fileprivate var str : String!
---this code from OC
@property (nonatomic, copy) NSString *str;

因此swift 版本的解决方案是:

fileprivate var str : String!
    override init() {
        super.init()

        let queue = DispatchQueue.global()

        for i in 0...10000{
            queue.async {
                objc_sync_enter(self)
                self.str = String("\(i)")
                print(self.str)
                objc_sync_exit(self)
            }
        }
    }

swift中的互斥锁:

objc_sync_enter(self)
//code
objc_sync_exit(self)

好了,到此为止,strong和weak的本质也看到了,并通过多线程中的尝试,获得了一些有趣的体验。

收藏
1
sina weixin mail 回到顶部