iOS多线程编程之多社交平台同步推送的设计与实现

简介:

功能介绍

在开发快易博的时候,有一个功能叫做“分享心情”【见下图】。它的主要功能是:用户可以一次发表一个微博(在人人网称之为新鲜事)到所有用户选择的绑定平台(其实就是通常所说的微博同步)。
进入之后:

这个功能实现起来并不难,说白了就是依次调用各个开放平台的关于“发表”相关的API就好了。但牵扯到几个给用户提供更好的“用户体验”的需求,就不得不使用多线程了,需求如下:

(1) 在图二对应的功能界面上,用户在发表完成之后,应该可以立即关闭;

(2) 在各个平台的独立发表界面,在发表完成之后,发表界面自动关闭,显示打开发表界面之前的一个界面。

注:分享心情界面其实实现的是一个多功能的发表界面,可以同步推送,也可以选择你想推送的平台。为了给用户提供方便,在你点进各个平台之后(比如新浪微博),它里面有单独的发表到该平台的发表界面来提供更多的功能,比如“@、##”等,见下图。


原先的处理方式

对于上面的需求(1),我原先的做法是,在用户点击右上角的发表按钮之后。用MBProgressHUD强制给当前界面设置一个“遮罩”,以让返回按钮不可点击。这样做的目的是为了停留在当前界面来等待执行各个开放平台提供的诸如“发表成功”、“发表失败”之类的回调协议,并且在回调协议里,通过在状态栏使用提示信息来提示用户是否成功。

假如让返回按钮可点击,用户有可能在发表完成之后立马选择返回,那么当前的UIViewController可能已经被释放,回调的时候就会导致应用崩溃。当然如果用MBProgressHUD hold主整个界面,用户除了选择等待就没有别的选择,而回调协议执行的时机其实取决于很多不确定因素(比如当前的网速,如果附带了图片,还要看图片的大小,以及各个开放平台对于API调用的响应速度等等),可能很快,也可能会等个好几十秒。而一旦你考验用户的耐心,那就是在给自己埋定时炸弹了。

对于需求(2),比需求(1)的关闭速度更快。我需要让用户在发表完成之后,发表界面立即关闭。所以当前UIViewController根本等不到回调协议的调用就已经释放,状态栏提示也无法实现。当然,妥协的办法还是用MBProgressHUD显示个“请稍后…”之类的强制让界面无法响应用户,等回调完成在选择关闭。

现在的处理方式

从上面的分析可以看得出,我希望回调协议与当前发表界面的UIViewController对象的生命周期没有关系。那么只能让这些“发表”操作都不在主线程(UI线程)上执行,这也就实现了发表操作与发表功能界面的解耦。

设计与代码实现

在iOS中实现多线程的方式有几种,这里使用NSOperation以及NSOperationQueue来实现。

UML简图:


大致代码如下:

PublishOperation:

#import <Foundation/Foundation.h>
#import "MTStatusBarOverlay.h"

@interface PublishOperation : NSOperation
<
MTStatusBarOverlayDelegate
>

@property (nonatomic,copy) NSString* txt;
@property (nonatomic,copy) NSString *publishImgPath;
@property (nonatomic,assign) BOOL completed;
@property (nonatomic,assign) BOOL canceledAfterError;
@property (nonatomic,retain) MTStatusBarOverlay *overlay;

- (void)clearPublishedImage;

- (id)initWithOperateParams:(NSString*)content;

@end

#import "PublishOperation.h"

@implementation PublishOperation

@synthesize completed;
@synthesize canceledAfterError;
@synthesize txt=_txt;
@synthesize overlay=_overlay;
@synthesize publishImgPath=_publishImgPath;

- (void)dealloc{
    [_txt release],_txt=nil;
    [_overlay release],_overlay=nil;
    [_publishImgPath release],_publishImgPath=nil;
    
    [super dealloc];
}

- (id)init{
    if (self=[super init]) {
        completed=NO;
        canceledAfterError=NO;
        [self initMTStatusBarOverlay];
    }
    
    return self;
}

- (id)initWithOperateParams:(NSString*)content{
    if (self=[self init]) {
        self.txt=content;
    }
    
    return self;
}

/*
 *实例化工具栏提示组件
 */
-(void)initMTStatusBarOverlay{
    _overlay = [MTStatusBarOverlay sharedInstance];
    _overlay.animation = MTStatusBarOverlayAnimationFallDown;  
    _overlay.detailViewMode = MTDetailViewModeHistory;         
    _overlay.delegate=self;
}

- (void)main{
    //操作既没有完成也没有因为发生错误而取消,则hold住当前run loop
    //防止返回主线程使得代理方法无法执行
    while (!(self.completed||self.canceledAfterError)) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 
                                 beforeDate:[NSDate distantFuture]];
        DebugLog(@"running!");
    }
    
}

#pragma mark - other methods -
- (void)clearPublishedImage{
    //清除緩存圖片
    if (fileExistsAtPath(self.publishImgPath)) {
        removerAtItem(self.publishImgPath);
    }
}

@end

SinaWeiboPublishOperation

#import "PublishOperation.h"
#import "WBEngine.h"

@interface SinaWeiboPublishOperation : PublishOperation
<
WBEngineDelegate
>

@end

#import "SinaWeiboPublishOperation.h"

@interface SinaWeiboPublishOperation()

@property (nonatomic,retain) WBEngine *engine;

@end

@implementation SinaWeiboPublishOperation

@synthesize engine=_engine;

- (void)dealloc{
    [_engine setDelegate:nil];
    [_engine release],_engine=nil;
    
    [super dealloc];
}

/*
 *business logic
 */
- (void)main{
    self.publishImgPath=PUBLISH_IMAGEPATH_SINAWEIBO;
    _engine = [[WBEngine alloc]initWithAppKey:kWBSDKDemoAppKey 
                                    appSecret:kWBSDKDemoAppSecret];
    _engine.delegate=self;
    [self.engine sendWeiBoWithText:self.txt 
                        image:[UIImage imageWithContentsOfFile:
                               PUBLISH_IMAGEPATH_SINAWEIBO]];
    
    [self.overlay postImmediateMessage:@"新浪微博发表中..." animated:YES];
    
    [super main];
}

#pragma mark - WBEngineDelegate Methods -
- (void)engine:(WBEngine *)engine requestDidSucceedWithResult:(id)result{
    [self clearPublishedImage];
    [GlobalInstance showOverlayMsg:@"新浪微博发表成功!" 
                       andDuration:2.0 
                        andOverlay:self.overlay];
    
     self.completed=YES;
     [NSThread sleepForTimeInterval:5];
}

- (void)engine:(WBEngine *)engine requestDidFailWithError:(NSError *)error{
    [self clearPublishedImage];
    [GlobalInstance showOverlayErrorMsg:@"新浪微博发表失败!" 
                            andDuration:2.0 
                             andOverlay:self.overlay];
    
    self.canceledAfterError=YES;
    [NSThread sleepForTimeInterval:5];
}

RenRenPublishOperation

#import "PublishOperation.h"

@interface RenRenPublishOperation : PublishOperation
<
RenrenDelegate
>

@end

#import "RenRenPublishOperation.h"

@implementation RenRenPublishOperation

/*
 *business logic
 */
- (void)main{
    self.publishImgPath=PUBLISH_IMAGEPATH_RENREN;
    BOOL hasImage=[[NSFileManager defaultManager]fileExistsAtPath:PUBLISH_IMAGEPATH_RENREN];
    if (hasImage) {
        ROPublishPhotoRequestParam *photoParams=[[[ROPublishPhotoRequestParam alloc]init]autorelease];
        photoParams.imageFile=[UIImage imageWithContentsOfFile:PUBLISH_IMAGEPATH_RENREN];
        photoParams.caption=self.txt;
        [[Renren sharedRenren] publishPhoto:photoParams andDelegate:self];
        [self.overlay postImmediateMessage:@"人人图片上传中..." animated:YES];
    }else {
        NSMutableDictionary *params=[NSMutableDictionary dictionaryWithCapacity:10];
        [params setObject:@"status.set" forKey:@"method"];
        [params setObject:self.txt
                   forKey:@"status"];
        [[Renren sharedRenren]requestWithParams:params andDelegate:self];
        [self.overlay postImmediateMessage:@"人人状态发表中..." animated:YES];
    }
    
    [super main];
}

#pragma mark - RenrenDelegate (ren ren) -
-(void)renren:(Renren *)renren requestDidReturnResponse:(ROResponse *)response{
    [self clearPublishedImage];
    [GlobalInstance showOverlayMsg:@"人人新鲜事发表成功!" 
                       andDuration:2.0 
                        andOverlay:self.overlay];
    self.completed=YES;
    [NSThread sleepForTimeInterval:5];
}

TencentWeiboPublishOperation

#import "PublishOperation.h"
#import "TencentWeiboDelegate.h"

@interface TencentWeiboPublishOperation : PublishOperation
<
TencentWeiboDelegate
>

@end

#import "TencentWeiboPublishOperation.h"
#import "TencentWeiboManager.h"
#import "OpenApi.h"

@implementation TencentWeiboPublishOperation

/*
 *business logic
 */
- (void)main{
    [self.overlay postImmediateMessage:@"腾讯微博发表中..." animated:YES];
    self.publishImgPath=PUBLISH_IMAGEPATH_TENCENTWEIBO;
    BOOL hasImage=[[NSFileManager defaultManager]fileExistsAtPath:PUBLISH_IMAGEPATH_TENCENTWEIBO];
    OpenApi *myApi=[TencentWeiboManager getOpenApi];
    myApi.delegate=self;
    if (!hasImage) {
        [myApi publishWeibo:self.txt 
                       jing:@"" 
                        wei:@"" 
                     format:@"json" 
                   clientip:@"127.0.0.1" 
                   syncflag:@"0"];
    }else{
        [myApi publishWeiboWithImageAndContent:self.txt 
                                          jing:@"" wei:@"" 
                                        format:@"json" 
                                      clientip:@"127.0.0.1" 
                                      syncflag:@"0"];
    }
    
}

#pragma mark - tencent weibo delegate -
- (void)tencentWeiboPublishedSuccessfully{
    [self clearPublishedImage];
    [GlobalInstance showOverlayMsg:@"腾讯微博发表成功!" 
                       andDuration:2.0 
                        andOverlay:self.overlay];
    [NSThread sleepForTimeInterval:5];
}

- (void)tencentWeiboRequestFailWithError{
    [self clearPublishedImage];
    [GlobalInstance showOverlayErrorMsg:@"腾讯微博发表失败!" 
                            andDuration:2.0 
                             andOverlay:self.overlay];
    
    [NSThread sleepForTimeInterval:5];
}


@end

注解:NSOperation在非并发的执行方式下,只需要重新实现它的main方法即可。

在PublishOperation的main方法中的那段代码的主要目的hold主当前的RunLoop(通俗一点讲其实就是阻塞当前执行的上下文,直到回调协议被执行),由此可见这里仍然存在回调协议无法执行的问题。为什么会出现这种情况,为什么要hold执行的上下文?因为各个平台的API通常都是异步发送请求的(腾讯微博除外),等到有响应了再来执行实现了的协议。而所谓的异步,其实也是操作系统在后台用另一个线程来处理的。而NSOperation的执行模式是它执行完实现在main方法里的逻辑就会退出,返回主线程。所以通常情况下这里的回调协议还是没有得到机会执行(机会其实是得到了,只是当前的NSOperation对象已被释放,导致内存访问错误)。所以这里用一个while循环hold主当前的runloop,一直保持到各个平台的异步回调完协议之后,设置completed或者canceledAfterError为YES,以退出while循环。注意这两个属性是异步设置的,也就是说是另一个线程来设置的。这里说明一下,在各个平台调用了[super main];方法之后,其实你不在回调协议中(或者说在另一个线程中)是无法设置这两个属性的。因为[super main];这句让当前执行NSOperation的线程阻塞了。

我顺便吐槽一下腾讯微博开放平台,它们开放的Framework中,发出的请求居然是同步获取结果的,而且它们居然都没有定义任何的回调协议,这里也让我很费神。所以在TencentWeiboOperation中的实现会跟新浪、人人有所不同。因为调用是同步的,所以在TencentWeiboPublishOperation最后直接调用[super main];是没用的(因为它会在回调协议之后才会执行,所以这边不需要调用它) ,这边调用线程的Sleep方法,让线程睡眠5秒钟,也就是延迟当前的NSOperation上下文5秒,延迟是因为在状态栏显示提示信息需要2秒,如果不延迟,那么在completed设置为YES之后NSOperation就会执行完成并退出,那MTStatusOverlayDelegate也没有机会执行。

/*
 *状态栏显示完成提示信息
 */
-(void)showOverlayMsg:(NSString*)msg andDuration:(NSTimeInterval)duration andOverlay:(MTStatusBarOverlay*)overlay{
    [overlay postImmediateFinishMessage:msg 
                               duration:duration animated:YES];
}

你在应用程序的AppDelegate中定义一个NSOperationQueue,用来执行NSOperation,就可以完成对发表操作与发表功能界面的Controller解耦。这样就可以在发表完成之后,直接关闭并释放Controller,并且由于功能的封装,它变成一个独立的、可复用的整体。在“分享心情”功能界面里,多平台同步推送的话,你只需要把各个平台的“PublishOperation”丢到NSOperationQueue去,其他就不用管,直接关闭或者返回即可。

新浪微博单独发表界面示例代码:

- (void)publishBtn_handle:(id)sender{	
    NSString *txt=self.publishTxtView.text;
    PublishOperation *publishOperation=[[[SinaWeiboPublishOperation alloc]initWithOperateParams:txt]autorelease];
    [((FastEasyBlogAppDelegate*)appDelegateObj).operationQueue addOperation:publishOperation];
    
    [self dismissModalViewControllerAnimated:YES];
}

分享心情发表界面示例代码:

/*
 *发送
 */
-(void)publish_click{    
	//移除高亮效果
	UIButton *btn=(UIButton*)self.navigationItem.rightBarButtonItem.customView;
	[btn setBackgroundImage:[UIImage imageNamed:@"publishBtn.png"] forState:UIControlStateNormal];
	
	if ([self.publishTxtView.text length]==0) {
		[GlobalInstance showMessageBoxWithMessage:@"请说点什么吧!"];
		return;
	}
    
    NSString *weiboTextContent=self.publishTxtView.text;
    
    if (plaformSwitch.isRenRenOpen) {
        PublishOperation *renrenOperation=[[[RenRenPublishOperation alloc]initWithOperateParams:weiboTextContent]autorelease];
        
        [((FastEasyBlogAppDelegate*)appDelegateObj).operationQueue addOperation:renrenOperation];
    }
    
    if (plaformSwitch.isTencentWeiboOpen) {
        PublishOperation *tencentOperation=[[[TencentWeiboPublishOperation alloc]initWithOperateParams:weiboTextContent]autorelease];
        
        [((FastEasyBlogAppDelegate*)appDelegateObj).operationQueue addOperation:tencentOperation];
    }
	
	//清空內容
    [self clearInputFromTextView];
    
    UIButton *imgBtn=(UIButton*)[self.view viewWithTag:4321];
    [imgBtn removeFromSuperview];
    
    [self dismissModalViewControllerAnimated:YES];
}

最后,为自己打个广告:


这是本人的第一个“个人iOS应用”,它是一款能够帮助你同时在多个社交平台和好友沟通交流以及分享的应用。支持一键发表状态/心情/照片到所有绑定平台,快速浏览/回复/分享,以及多终端同时在线浏览好友微博与好友互动,为你提供更为便捷的社交体验。




原文发布时间为:2012-11-18


本文作者:vinoYang


本文来自云栖社区合作伙伴CSDN博客,了解相关信息可以关注CSDN博客。

目录
相关文章
|
1天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
4天前
|
数据采集 存储 数据处理
Python中的多线程编程及其在数据处理中的应用
本文深入探讨了Python中多线程编程的概念、原理和实现方法,并详细介绍了其在数据处理领域的应用。通过对比单线程与多线程的性能差异,展示了多线程编程在提升程序运行效率方面的显著优势。文章还提供了实际案例,帮助读者更好地理解和掌握多线程编程技术。
|
3天前
|
API Android开发 iOS开发
深入探索Android与iOS的多线程编程差异
在移动应用开发领域,多线程编程是提高应用性能和响应性的关键。本文将对比分析Android和iOS两大平台在多线程处理上的不同实现机制,探讨它们各自的优势与局限性,并通过实例展示如何在这两个平台上进行有效的多线程编程。通过深入了解这些差异,开发者可以更好地选择适合自己项目需求的技术和策略,从而优化应用的性能和用户体验。
|
8天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
17天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
14天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
17天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
18天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
44 1
|
21天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
22天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
44 4
下一篇
无影云桌面