__weak __typeof(self)weakSelf = self和__strong __typeof(weakSelf)strongSelf = weakSelf

本文深入探讨了Objective-C与Swift在iOS开发中的应用与最佳实践,包括循环引用、block方法的合理使用、IBOutlet的声明方式以及如何避免资源泄露。详细解释了在学习AFNetworking过程中关于循环引用的解决策略,并通过代码示例展示了如何在AFNetworking中正确地使用weak和strong引用。同时,解析了AFNetworking源码中关于weak和strong引用的巧妙运用,以及它们在确保代码安全性和高效性方面的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、默认strong,可选weak。strong下不管成员变量还是property,每次使用指针指向一个对象,等于自动调用retain(), 并对旧对象调用release(),所以设为nil等于release。

2、只要某个对象被任一strong指针指向,那么它将不会被销毁,否则立即释放,不用等runloop结束。所有strong指针变量不需要在dealloc中手动设为nil,ios会自动处理,debug可以看到全部被置为nil,最先声明的变量最后调用dealloc释放。

3、官方建议IBOutlet加上__weak,实际上不用加也会自动释放;

4、优先使用私有成员变量,除非需要公开属性才用property。

5、避免循环引用,否则手动设置nil释放。

6、block方法常用声明:@property (copy) void(^MyBlock)(void);  如果超出当前作用域之后仍然继续使用block,那么最好使用copy关键字,拷贝到堆区,防止栈区变量销毁。

7、创建block匿名函数之前一般需要对self进行weak化,否则造成循环引用无法释放controller:

     __weak MyController *weakSelf = self 或者 __weak __typeof(self) weakSelf = self;

    执行block方法体的时候也可以转换为强引用之后再使用:MyController* strongSelf = weakSelf; if (!strongSelf) { return; }




在学习AFNetWorking的过程中,经常看到类似:

__weak __typeof(self)weakSelf = self
然后在block中,看到:
__strong __typeof(weakSelf)strongSelf = weakSelf;

如下代码:

- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler {

    [self.lock lock];

    if (!self.backgroundTaskIdentifier) {

        UIApplication *application = [UIApplication sharedApplication];

        __weak __typeof(self)weakSelf = self;

        self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{

            __strong __typeof(weakSelf)strongSelf = weakSelf;

            

            if (handler) {

                handler();

            }

            

            if (strongSelf) {

                [strongSelf cancel];

                

                [application endBackgroundTask:strongSelf.backgroundTaskIdentifier];

                strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;

            }

        }];

    }

    [self.lock unlock];

}


这是因为:没有添加__strong 引用的话,编译器会有警告,为什么会警告呢,因为弱引用的weakself会在某个时间被释放,有可能是在执行之后的block之前就会被释放,这样在后续的操作操作就有可能出错,所以最好是添加一个对weakSelf的__strong引用。




在不久前看AFNetworking的源码时候发现了这么一句:

<span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<code class="objc" style="padding: 0.3em; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: 0px; display: block; overflow-y: auto;"><span class="line"><span class="c1"><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">// 不知道这行代码的使用场景的同学你该去自习看看ARC的注意事项和Block的使用了</span></span>
</span><span class="line"><span class="c1"><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">// AFNetworking的写法</span></span>
</span><span class="line"><span class="n">__weak</span> <span class="n">__typeof</span><span class="p">(</span><span class="o">&*</span><span class="n"><span class="keyword" style="font-weight: bold;">self</span></span><span class="p">)</span><span class="n">weakSelf</span> <span class="o">=</span> <span class="n"><span class="keyword" style="font-weight: bold;">self</span></span><span class="p">;</span>
</span><span class="line">
</span><span class="line"><span class="c1"><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">// 我之前一直这么写的</span></span>
</span><span class="line"><span class="n">__weak</span> <span class="n">__typeof</span><span class="p">(</span><span class="n"><span class="keyword" style="font-weight: bold;">self</span></span><span class="p">)</span> <span class="n">weakSelf</span> <span class="o">=</span> <span class="n"><span class="keyword" style="font-weight: bold;">self</span></span><span class="p">;</span>
</span><span class="line"><span class="c1"><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">// 或者这么写</span></span>
</span><span class="line"><span class="n">__weak</span> <span class="n">XxxViewController</span> <span class="o">*</span><span class="n">weakSelf</span> <span class="o">=</span> <span class="n"><span class="keyword" style="font-weight: bold;">self</span></span><span class="p">;</span>
</span><span class="line"><span class="c1"><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">// 或者这么写</span></span>
</span><span class="line"><span class="n">__weak</span> <span class="kt">id</span> <span class="n">weakSelf</span> <span class="o">=</span> <span class="n"><span class="keyword" style="font-weight: bold;">self</span></span><span class="p">;</span></span></code>
处理下面代码可能出现线程问题的情况- (void)shutterCamera { AVCaptureConnection *videoConnection = [self.ImageOutPut connectionWithMediaType:AVMediaTypeVideo]; UIDeviceOrientation curDeviceOrientation = [[UIDevice currentDevice] orientation]; if (!videoConnection || !self.session.isRunning) { ATLog(@"take photo failed!"); return; } AVCaptureVideoOrientation avcaptureOrientation = [self avOrientationForDeviceOrientation:curDeviceOrientation]; [videoConnection setVideoOrientation:avcaptureOrientation]; [videoConnection setVideoScaleAndCropFactor:self.effectiveScale]; MJWeakSelf; [self.ImageOutPut captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) { dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ __strong typeof(weakSelf) strongSelf = weakSelf; if (!strongSelf) return; if (imageDataSampleBuffer) { NSData * imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; strongSelf.image = [UIImage imageWithData:imageData]; if (strongSelf.image) { strongSelf.image = [UIImage updateImageSize:strongSelf.image]; } strongSelf.image = [[TZImageManager manager] fixOrientation:strongSelf.image];//[UIImage fixOrientation:weakSelf.image]; PHAuthorizationStatus authorStatus = [PHPhotoLibrary authorizationStatus]; if (authorStatus == PHAuthorizationStatusAuthorized) { [[TZImageManager manager] savePhotoWithImage:strongSelf.image completion:^(PHAsset *asset, NSError *error) { [strongSelf addPHAsset:asset]; }]; }else{ [strongSelf gotoEditImageController:strongSelf.image asset:nil]; } } }); }]; } - (void)gotoEditImageController:(UIImage *)image asset:(PHAsset *)asset { if (!image) { [ATMBPHud showErrorToView:self.view text:@"获取图片失败,请重新再试"]; return; } if (image) { image = [UIImage updateImageSize:image]; } YasicClipPage *vc = [[YasicClipPage alloc] init]; vc.userId = self.userId; vc.attention = self.attention; vc.targetImage = image; vc.searchType = self.searchType; vc.asset = asset; // [self stopSession]; dispatch_async(dispatch_get_main_queue(), ^{ [[KDUserInterfaceTool currenrtopViewController].navigationController pushViewController:vc animated:YES]; [self removeViewController:self]; }); } - (void)addPHAsset:(PHAsset *)asset { MJWeakSelf; TZAssetModel *assetModel = [[TZImageManager manager] createModelWithAsset:asset]; [ATHUD show]; [[TZImageManager manager] requestImageDataForAsset:assetModel.asset completion:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { [ATHUD dismiss]; UIImage *image = [UIImage imageWithData:imageData];//[[TZImageManager manager] fixOrientation:[UIImage imageWithData:imageData]]; image = [[TZImageManager manager] fixOrientation:image]; [weakSelf gotoEditImageController:image asset:asset]; } progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { ATLog(@"%f",progress); }]; } #pragma --mark 获取大图 - (void)getImageWithModel:(TZAssetModel *)model { if (model == nil) { ATLog(@"图片为空"); return; } [ATHUD show]; WS(weakSelf) [[TZImageManager manager] requestImageDataForAsset:model.asset completion:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { [ATHUD dismiss]; UIImage *image = [UIImage imageWithData:imageData]; if (weakSelf.downBtnClick) { weakSelf.downBtnClick(image, model); } } progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { ATLog(@"%f",progress); }]; }
最新发布
07-25
<think>我们正在处理关于AVCaptureStillImageOutput的captureStillImageAsynchronouslyFromConnection方法的线程安全问题。 根据引用[1]:AVCaptureStillImageOutput用于静态图片输出。 根据引用[2]:我们在视图出现消失时启动停止会话,这涉及到主线程。 根据引用[3]:人脸识别示例中,我们看到了一个委托回调,它可能会在不同的线程中执行。 关键点: 1. captureStillImageAsynchronouslyFromConnection:completionHandler: 是一个异步方法,它的完成回调可能在非主线程中执行。 2. 在完成回调中,我们通常需要处理图像数据(如保存到相册、显示预览等),这些操作可能涉及UI更新或资源访问,需要线程安全。 解决方案: - 在完成回调中,如果需要更新UI,必须将相关代码放到主线程执行。 - 对于共享资源的访问(如写入文件、修改状态等),需要使用适当的同步机制(如锁、串行队列)来避免竞态条件。 示例代码: 假设我们有一个拍照按钮,点击时触发拍照: ```objective-c - (IBAction)takePhoto:(id)sender { AVCaptureConnection *connection = [self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo]; [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) { if (imageDataSampleBuffer) { NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; UIImage *image = [UIImage imageWithData:imageData]; // 回到主线程更新UI dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = image; }); // 保存到相册(注意:写入相册的权限线程) // 由于写入相册的操作本身是异步且线程安全的,但为了统一,我们也可以放在主线程,或者使用PHPhotoLibrary(iOS8+) // 但是,PHPhotoLibrary的保存操作需要在主线程执行。所以,我们回到主线程保存。 dispatch_async(dispatch_get_main_queue(), ^{ UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL); }); } }]; } - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { if (error) { // 保存出错 } else { // 保存成功 } } ``` 注意: 1. 上述代码中,我们将更新UI保存图片都放到了主线程。因为UIKitPhotos框架要求在主线程使用。 2. 如果我们需要进行大量的图像处理,那么不应该在主线程进行,而应该在一个后台线程处理,处理完成后再回到主线程更新UI。 另外,关于线程安全的其他考虑: - 如果我们在拍照过程中有状态标记(例如isCapturing),那么修改读取这个状态标记应该使用线程安全的方式(例如使用串行队列或者使用atomic属性,但更推荐使用串行队列来确保操作的顺序性)。 例如,我们可以设置一个串行队列来管理拍照状态: ```objective-c @property (nonatomic, strong) dispatch_queue_t captureQueue; @property (nonatomic, assign) BOOL isCapturing; // 注意:在多个线程中修改读取 // 初始化队列 self.captureQueue = dispatch_queue_create("com.example.captureQueue", DISPATCH_QUEUE_SERIAL); // 在拍照方法中: - (IBAction)takePhoto:(id)sender { // 防止连续点击 dispatch_async(self.captureQueue, ^{ if (self.isCapturing) { return; } self.isCapturing = YES; // 回到主线程进行拍照操作(因为AVFoundation的调用不需要在特定队列,但通常UI操作在主线程,所以这里我们为了简单,将拍照触发放在主线程) dispatch_async(dispatch_get_main_queue(), ^{ AVCaptureConnection *connection = [self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo]; [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) { // 处理图像(可能耗时,所以放在后台队列) dispatch_async(self.captureQueue, ^{ // 处理图像数据 // ... // 处理完成后,更新状态 self.isCapturing = NO; }); // 更新UI需要主线程 dispatch_async(dispatch_get_main_queue(), ^{ // 更新UI }); }]; }); }); } ``` 但是注意:上面的代码中,我们在captureQueue中修改isCapturing,并且通过captureQueue来防止连续点击。同时,我们将实际的拍照触发放在主线程(因为AVFoundation的调用通常可以在任意线程,但是涉及到UI(比如显示预览)最好在主线程)。在完成回调中,我们将图像处理放到captureQueue,这样就不会阻塞主线程,并且处理完成后将isCapturing设为NO。 总结: 1. 使用串行队列来管理状态,避免多个线程同时修改状态。 2. 将UI操作需要主线程的操作(如保存到相册)放到主线程。 3. 对于耗时的图像处理,使用后台队列,避免阻塞主线程。 这样,我们可以确保线程安全,避免竞态条件。 注意:在iOS中,AVFoundation的会话(AVCaptureSession)本身是线程安全的,可以在任意线程创建修改,但是最好在一个串行队列中统一管理配置(比如开始、停止、修改输入输出等)。但是,在这个问题中,我们主要关注的是拍照的瞬间,所以使用一个队列来管理拍照状态即可。 根据引用[4],我们也可以了解到AVFoundation提供了强大的多媒体支持,但同时也需要注意线程安全。 因此,处理线程安全的关键是:将状态管理放在串行队列中,将UI操作放在主线程,合理使用队列来隔离线程。 相关问题: 1. 在AVFoundation中,除了拍照,还有哪些操作需要注意线程安全? 2. 如何设计一个线程安全的相机管理类? 3. 在iOS中,处理图像数据时有哪些常见的线程安全问题?</think>### 处理 AVCaptureStillImageOutput 的线程安全问题 在 Objective-C 中使用 `AVCaptureStillImageOutput` 的 `captureStillImageAsynchronouslyFromConnection:completionHandler:` 方法时,需特别注意以下线程安全问题及解决方案: #### 1. **主线程原则** - **UI 操作必须回到主线程**:完成处理器(completionHandler)在后台线程执行,所有 UI 更新(如显示预览图)必须调度到主线程。 ```objectivec [stillImageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:^(CMSampleBufferRef buffer, NSError *error) { // 后台线程执行 NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:buffer]; UIImage *image = [UIImage imageWithData:imageData]; dispatch_async(dispatch_get_main_queue(), ^{ // 切换到主线程 self.previewImageView.image = image; // 安全更新UI }); }]; ``` #### 2. **资源竞争防护** - **使用串行队列**:创建专用队列管理拍照状态,避免同时触发多次拍照。 ```objectivec @property (nonatomic, strong) dispatch_queue_t captureQueue; @property (nonatomic, assign) BOOL isCapturing; // 拍照状态标记 // 初始化队列(ViewDidLoad中) self.captureQueue = dispatch_queue_create("com.example.captureQueue", DISPATCH_QUEUE_SERIAL); // 拍照方法 - (void)capturePhoto { dispatch_async(self.captureQueue, ^{ if (self.isCapturing) return; // 避免重入 self.isCapturing = YES; dispatch_async(dispatch_get_main_queue(), ^{ AVCaptureConnection *conn = [self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo]; [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:conn completionHandler:...]; }); }); } // 在完成处理器中重置状态 completionHandler:^(...){ dispatch_async(self.captureQueue, ^{ self.isCapturing = NO; // 安全修改状态 }); } ``` #### 3. **会话生命周期管理** - **会话启停与视图生命周期同步**:根据引用[2],在 `viewDidAppear`/`viewDidDisappear` 中控制会话运行,避免后台线程冲突。 ```objectivec - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self.captureSession startRunning]; // 主线程启动 } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; [self.captureSession stopRunning]; // 主线程停止 } ``` #### 4. **内存管理** - **避免循环引用**:使用 `__weak` 防止 completionHandler 捕获 self 导致内存泄漏。 ```objectivec __weak typeof(self) weakSelf = self; [stillImageOutput captureStillImageAsynchronouslyFromConnection:conn completionHandler:^(...){ __strong typeof(weakSelf) strongSelf = weakSelf; if (!strongSelf) return; // 处理逻辑 }]; ``` #### 完整示例 ```objectivec - (void)safeCapturePhoto { dispatch_async(self.captureQueue, ^{ if (self.isCapturing) return; self.isCapturing = YES; __weak typeof(self) weakSelf = self; dispatch_async(dispatch_get_main_queue(), ^{ AVCaptureConnection *conn = [self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo]; [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:conn completionHandler:^(CMSampleBufferRef buffer, NSError *error) { // 后台线程 NSData *data = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:buffer]; UIImage *image = [UIImage imageWithData:data]; // 更新UI需切回主线程 dispatch_async(dispatch_get_main_queue(), ^{ weakSelf.previewImageView.image = image; }); // 重置状态(通过安全队列) dispatch_async(weakSelf.captureQueue, ^{ weakSelf.isCapturing = NO; }); }]; }); }); } ``` ### 关键总结 1. **线程分工**: - 主线程:会话控制、UI 更新。 - 专用串行队列:状态管理。 - 后台线程:图像数据处理。 2. **冲突避免**:通过状态标记 (`isCapturing`) 防止并发拍照请求。 3. **内存安全**:使用 `__weak` 打破循环引用链。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值