性能调优的方式:

 1、通过专门的性能调优工具

 2、通过代码优化

1. 性能调优工具:

下面针对iOS的性能调优工具进行一个介绍:

1.1 静态分析工具–Analyze

相信iOS开发者在App进行Build或Archive时,会产生很多编译警告,这些警告是编译时产生的,静态分析的过程也类似,在XCode Product菜单下,点击Analyze对App进行静态分析。

Analyze主要分析以下四种问题:

   1、逻辑错误:访问空指针或未初始化的变量等;

  2、内存管理错误:如内存泄漏;

  3、声明错误:从未使用的变量;

  4、API调用错误:未包含使用的库和框架。

1.2 内存泄漏分析工具–Leaks

点击XCode的Product菜单Profile启动Instruments,使用Leaks开始动态分析。
选择Leaks,会自动启动Leaks工具和IOS模拟器,Leaks启动后会开始录制,随着对模拟器运行的App的操作,可以在Leaks中查看内存占用的情况。

  注:如果项目使用了ARC,随着操作,不断地开启或关闭视图,内存可能持续上升,但这不一定表示存在内存泄漏,ARC释放的时机是不固定的。

Leaks顶部分为两栏:All Heap & Anonymous VM和Leaks Checks,All Heap & Anonymous VM中的曲线代表内存分配和内存泄漏曲线。

点击第二栏Leaks Checks展示内存泄漏,进行内存泄漏分析,将光标放置在上图的小红叉上可看到leak数量,右下方是leaks调试的选项:

建议把Snapshot Interval间隔时间设置为10秒,勾选Automatic Snapshotting,Leaks会自动进行内存捕捉分析。

在你怀疑有内存泄漏的操作前和操作后,可以点击Snapshot Now进行手动捕捉。

Leaked Object的表格中显示了内存泄漏的类型、数量及内存空间等。

点击具体的某个内存泄漏对象,在右侧Detail窗口中会出现导致泄漏可能的位置,其中黑色头像代表了最可能的位置。

Leaks已成功找出了友盟相关的函数:

内存泄漏动态分析技巧

  熟练使用Leaks后会对内存泄漏判断更准确,在可能导致泄漏的操作里,多使用Snapshot Now手动捕捉。

  开始时如果设备性能较好,可以把自动捕捉间隔设置为5秒钟。

  使用ARC的项目,一般内存泄漏都是malloc、自定义结构、资源引起的,多注意这些地方进行分析。

开启ARC后,内存泄漏的原因

  开启了ARC并不是就不会存在内存问题,苹果有句名言:ARC is only for NSObject。

  在IOS 中使用malloc分配的内存,ARC是不会处理的,需要自己进行处理。

  例子中的 CGImageRef 也是一个Image的指针,ARC也不会进行处理。

1.3 不合理内存分析工具–Allocation

关于内存的问题,除了内存泄漏以外,还可能存在内存不合理使用的情况,也会导致IOS内存警告。

内存的不合理使用往往比内存泄漏更难发现,内存泄漏可以更多借助于工具的判断,而内存的不合理运用更多需要开发者结合代码、架构来进行分析。

明确说明一下两者的区别:

  内存泄漏:指内存被分配了,但是程序中已经没有志向该内存的指针,导致该内存无法被释放,一直占用着内存,产生内存泄漏。

   内存不合理运用:苹果官方称这种情况为abandoned memory,也就是存在已分配内存的引用,但实际上程序中并不会使用,比如图片等对象进行了缓存,但是缓存中的对象一直没有被使用。

XCode提供的Instruments中的Allocation工具可以用来帮你了解内存的分配情况,当你的App收到内存警告时,首先应该用Allocation进行内存分析,了解哪些对象占用了太多内存。

1.4 干掉僵尸对象–Zombies

僵尸对象,也就是我们会遇到的EXC_BAD_ACCESS错误,由于内存已经被释放,而这个对象仍旧保留这那个坏地址而导致的。
在MRC的开发中,这个错误比较常见,ARC下面在使用到C++的代码也会遇到。
不过这个工具比较简单,遇到这类错误,打开这个位于Instruments下的工具,直接就能帮你定位到,这里就不赘述了。

1.5 性能提升工具–Time Profile

这篇文章的重中之重!

既然是性能调优,那么怎么提升代码的运行效率其实才是我们程序员最直接的诉求,而这个工具可以辅助我们办到这件事。

Time Profiler分析原理: 它按照固定的时间间隔来跟踪每一个线程的堆栈信息,通过统计比较时间间隔之间的堆栈状态,来推算某个方法执行了多久,并获得一个近似值。它将各个方法消耗的时间统计起来,形成了我们直接定位需要进行优化的代码的好帮手。

选择Time Profiler工具开始测试,这时会自动启动模拟器和Time Profiler录制。

先进行一些App的操作,让Time Profiler收集足够的数据,尤其是你觉得那些有性能瓶颈的地方。

2是扩展面板,用来跟踪显示堆栈;

3里面有设置和详情,可以从这里对录制做些配置,detail下查看到cpu运行的时间都消耗在哪里;

通过对应用的操作,可以在详细面板中看到那些最耗时的操作是哪些,并可以逐行展开查看:

图标为黑色头像的就是Time Profiler给我们的提示,有可能存在性能瓶颈的地方,可以逐渐向下展开,找到产生的根本原因。

Time Profiler参数设置

这里边几个选项的含义如下:

  Separate by Thread: 每个线程应该分开考虑。只有这样你才能揪出那些大量占用CPU的”重”线程

  Invert Call Tree: 从上倒下跟踪堆栈,这意味着你看到的表中的方法,将已从第0帧开始取样,这通常你是想要的,只有这样你才能看到CPU中话费时间最深的方法.也就是说FuncA{FunB{FunC}} 勾选此项后堆栈以C->B-A 把调用层级最深的C显示在最外面

  Hide System Libraries: 勾选此项你会显示你app的代码,这是非常有用的. 因为通常你只关心cpu花在自己代码上的时间不是系统上的

  Flatten Recursion: 递归函数, 每个堆栈跟踪一个条目

  Top Functions: 一个函数花费的时间直接在该函数中的总和,以及在函数调用该函数所花费的时间的总时间。因此,如果函数A调用B,那么A的时间报告在A花费的时间加上B花费的时间,这非常真有用,因为它可以让你每次下到调用堆栈时挑最大的时间数字,归零在你最耗时的方法。

上面的参数在实践中合理设置,也没有什么太多技巧,就是通过数据的隐藏、显示让我们更关注于想找到的数据。

2. 性能调优的代码优化:

下面介绍一下在开发中可以直接进行的代码优化的方面。

2.1 views设置为不透明(opaque=yes)

(opaque)这个属性给渲染系统提供了一个如何处理这个view的提示。如果设为YES, 渲染系统就认为这个view是完全不透明的,这使得渲染系统优化一些渲染过程和提高性能。

如果设置为NO,渲染系统正常地和其它内容组成这个View。默认值是YES。

给你们看个图,你们就知道了,如果这个属性为NO,那么:

知道了吧,GPU会利用图层颜色合成公式去合成真正的色值,这在ScrollView或者动画中是很消耗性能的。

2.2 不要阻塞主线程

永远不要使主线程承担过多。因为UIKit在主线程上做所有工作,渲染,管理触摸反应,回应输入等都需要在它上面完成。

一直使用主线程的风险就是如果你的代码真的block了主线程,你的app会失去反应。

大部分阻碍主进程的情形是你的app在做一些牵涉到读写外部资源的I/O操作,比如存储或者网络。

通常建议这些操作都使用GCD的方式直接异步执行,并将UI相关操作在主线程进行回调,像这样:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{

    // 切换到全局队列,异步执行耗时操作
dispatch_async(dispatch_get_main_queue(), ^{
// 切换到主线程,更新你的UI。
});
});

2.3 提前调整ImageView中的图片大小(同图片和动画的渲染)

如果要在UIImageView中显示一个图片,你应保证图片的大小和UIImageView的大小相同。
因为在运行中缩放图片是很耗费资源的,特别是UIImageView嵌套在UIScrollView中的情况下。

如果图片是从远端服务加载的你不能控制图片大小,比如在下载前调整到合适大小的话,你可以在下载完成后,最好是用background thread,缩放一次,然后在UIImageView中使用缩放后的图片。

这个类比到图片和动画的渲染中,是通用的。

具体方法参考上面的GCD操作。

2.4 正确使用容器的特性

Arrays: 有序的一组值。使用index来查找很快,使用value 查找很慢, 插入/删除很慢。 Dictionaries: 存储键值对。 用键来查找比较快。 Sets: 无序的一组值。用值来查找很快,插入/删除很快。

2.5 大文件传输使用gzip

大量app依赖于远端资源和第三方API,你可能会开发一个需要从远端下载XML, JSON, HTML或者其它格式的app。

问题是我们的目标是移动设备,因此你就不能指望网络状况有多好。一个用户现在还在edge网络,下一分钟可能就切换到了3G。不论什么场景,你肯定不想让你的用户等太长时间。

减小文档的一个方式就是在服务端和你的app中打开gzip。这对于文字这种能有更高压缩率的数据来说会有更显著的效用。

当然,现在苹果已经自动支持了,你只需要告诉你们服务端的同学,传输大文件的时候记得用gzip就完了。

2.6 View的重用和懒加载

更多的view意味着更多的渲染,也就是更多的CPU和内存消耗,对于那种嵌套了很多view在UIScrollView里边的app更是如此。

重用就是模仿UITableViewUICollectionView的操作: 不要一次创建所有的subview,而是当需要时才创建,当它们完成了使命,把他们放进一个可重用的队列中。
当需要使用View的时候,去可重用队列里面找一找有没有可以被复用的View。
这里我的一份框架中曾经使用过类似的方法去创建一个图片浏览器,大家可以稍做参考。View的重用

懒加载就是在程序启动时并不进行加载,只有当用到这个对象的时候,才进行加载。
这个不仅在属性中可以进行这样的使用,在View上面也是一样,不过实现稍有不同。
懒加载会消耗更少内存,但是在View的显示上会稍有滞后。

2.7 Cache

一个极好的原则就是,缓存所需要的,也就是那些不大可能改变但是需要经常读取的东西。

我们能缓存些什么呢?一些选项是,远端服务器的响应,图片,甚至计算结果,比如UITableView的行高。

NSURLConnection默认会缓存资源在内存或者存储中根据它所加载的HTTP Headers。你甚至可以手动创建一个NSURLRequest然后使它只加载缓存的值。

下面是一个可用的代码段,你可以可以用它去为一个基本不会改变的图片创建一个NSURLRequest并缓存它:

+ (NSMutableURLRequest *)imageRequestWithURL:(NSURL *)url {

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

    request.cachePolicy = NSURLRequestReturnCacheDataElseLoad; // this will make sure the request always returns the cached image
request.HTTPShouldHandleCookies = NO;
request.HTTPShouldUsePipelining = YES;
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; return request;
}

注意你可以通过 NSURLConnection 获取一个URL request, AFNetworking也一样的。这样你就不必为采用这条tip而改变所有的networking代码了。

如果你需要缓存其它不是HTTP Request的东西,你可以用NSCache。

2.8 记得处理内存警告

一旦系统内存过低,iOS会通知所有运行中app。在官方文档中是这样记述:

  如果你的app收到了内存警告,它就需要尽可能释放更多的内存。最佳方式是移除对缓存,图片object和其他一些可以重创建的objects的strong references.

幸运的是,UIKit提供了几种收集低内存警告的方法:

  在app delegate中使用<code>applicationDidReceiveMemoryWarning:</code> 的方法 在你的自定义UIViewController的子类(subclass)中覆盖<code>didReceiveMemoryWarning</code> 注册并接收 UIApplicationDidReceiveMemoryWarningNotification 的通知

一旦收到这类通知,你就需要释放任何不必要的内存使用。

  例如,UIViewController的默认行为是移除一些不可见的view, 它的一些子类则可以补充这个方法,删掉一些额外的数据结构。一个有图片缓存的app可以移除不在屏幕上显示的图片。

这样对内存警报的处理是很必要的,若不重视,你的app就可能被系统杀掉。

然而,当你一定要确认你所选择的object是可以被重现创建的来释放内存。一定要在开发中用模拟器中的内存提醒模拟去测试一下。

2.9 重用大的开销对象

这里的大开销是指一些初始化很慢的objects,如:NSDateFormatter和NSCalendar。但是,你又不可避免地需要使用它们,比如从JSON或者XML中解析数据。

想要避免使用这个对象的瓶颈你就需要重用他们,可以通过添加属性到你的class里或者创建静态变量来实现。

注意如果你要选择第二种方法,对象会在你的app运行时一直存在于内存中,和单例(singleton)很相似。

下面的代码说明了使用一个属性来延迟加载一个date formatter. 第一次调用时它会创建一个新的实例,以后的调用则将返回已经创建的实例:

// in your .h or inside a class extension

@property (nonatomic, strong) NSDateFormatter *formatter;

// inside the implementation (.m)
// When you need, just use self.formatter
- (NSDateFormatter *)formatter {
if (! _formatter) {
_formatter = [[NSDateFormatter alloc] init];
_formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; // twitter date format
}
return _formatter;
}

还需要注意的是,其实设置一个NSDateFormatter的速度差不多是和创建新的一样慢的!所以如果你的app需要经常进行日期格式处理的话,你会从这个方法中得到不小的性能提升。

2.10 避免反复的处理数据

许多应用需要从服务器加载功能所需的常为JSON或者XML格式的数据。在服务器端和客户端使用相同的数据结构很重要。在内存中操作数据使它们满足你的数据结构是开销很大的。

比如你需要数据来展示一个table view,最好直接从服务器取array结构的数据以避免额外的中间数据结构改变。

类似的,如果需要从特定key中取数据,那么就使用键值对的dictionary。

2.11 正确设定背景图片

在View里放背景图片就像很多其它iOS编程一样有很多方法:

  使用UIColor的 colorWithPatternImage来设置背景色; 在view中添加一个UIImageView作为一个子View。

如果你使用全画幅的背景图,你就必须使用UIImageView因为UIColor的colorWithPatternImage是用来创建小的重复的图片作为背景的。这种情形下使用UIImageView可以节约不少的内存:

// You could also achieve the same result in Interface Builder
UIImageView *backgroundView = [ [UIImageView alloc] initWithImage:[UIImage imageNamed:@"background"]];
[self.view addSubview:backgroundView];

如果你用小图平铺来创建背景,你就需要用UIColor的colorWithPatternImage来做了,它会更快地渲染也不会花费很多内存:

self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background"]];

2.12 试试苹果最新的WKWebView来处理web

UIWebView很有用,用它来展示网页内容或者创建UIKit很难做到的动画效果是很简单的一件事。

但是你可能有注意到UIWebView并不像驱动Safari的那么快。这是由于以JIT compilation 为特色的Webkit的Nitro Engine的限制。

所以想要更高的性能你就要调整下你的HTML了。第一件要做的事就是尽可能移除不必要的javascript,避免使用过大的框架。能只用原生js就更好了。

另外,尽可能异步加载例如用户行为统计script这种不影响页面表达的javascript。

最后,永远要注意你使用的图片,保证图片的符合你使用的大小。使用Sprite sheet提高加载速度和节约内存。

当然,上面是针对你在使用UIWebView的情况下,需要尽量减少使用web的特性,而苹果最近刚推出的Safari的底层框架WKWebView也许能帮我们规避掉很多这样的性能问题。

2.13 优化你的TableView

Table view需要有很好的滚动性能,不然用户会在滚动过程中发现动画的瑕疵。

为了保证table view平滑滚动,确保你采取了以下的措施:

  正确使用reuseIdentifier来重用cells 尽量使所有的view opaque,包括cell自身 避免渐变,图片缩放,后台选人 缓存行高 如果cell内现实的内容来自web,使用异步加载,缓存请求结果 使用shadowPath来画阴影 减少subviews的数量 尽量不适用cellForRowAtIndexPath:,如果你需要用到它,只用一次然后缓存结果 使用正确的数据结构来存储数据 使用rowHeight, sectionFooterHeight 和 sectionHeaderHeight来设定固定的高,不要请求delegate

2.14 选择正确的数据存储方式

当存储大块数据时你会怎么做?

你有很多选择,比如:

  使用NSUerDefaults 使用XML, JSON, 或者 plist 使用NSCoding存档 使用类似SQLite的本地SQL数据库 使用 Core Data

NSUserDefaults的问题是什么?虽然它很nice也很便捷,但是它只适用于小数据,比如一些简单的布尔型的设置选项,再大点你就要考虑其它方式了

XML这种结构化档案呢?总体来说,你需要读取整个文件到内存里去解析,这样是很不经济的。使用SAX又是一个很麻烦的事情。

NSCoding?不幸的是,它也需要读写文件,所以也有以上问题。

在这种应用场景下,使用SQLite 或者 Core Data比较好。使用这些技术你用特定的查询语句就能只加载你需要的对象。

在性能层面来讲,SQLite和Core Data是很相似的。他们的不同在于具体使用方法。Core Data代表一个对象的graph model,但SQLite就是一个DBMS。Apple在一般情况下建议使用Core Data,但是如果你有理由不使用它,那么就去使用更加底层的SQLite吧。

如果你使用SQLite,你可以用FMDB(https://github.com/ccgus/fmdb)这个库来简化SQLite的操作,这样你就不用花很多经历了解SQLite的C API了。

2.15 把Xib换成Storyboard吧

当你加载一个XIB的时候所有内容都被放在了内存里,包括任何图片。如果有一个不会即刻用到的view,你这就是在浪费宝贵的内存资源了。

Storyboards就是另一码事儿了,storyboard仅在需要时实例化一个view controller.

当加载XIB时,所有图片都被缓存,如果你在做OS X开发的话,声音文件也是。Apple在相关文档中的记述是:

  当你加载一个引用了图片或者声音资源的nib时,nib加载代码会把图片和声音文件写进内存。在OS X中,图片和声音资源被缓存在named cache中以便将来用到时获取。在iOS中,仅图片资源会被存进named caches。取决于你所在的平台,使用NSImage 或UIImage 的`imageNamed:`方法来获取图片资源。

很明显,同样的事情也发生在storyboards中,但我并没有找到任何支持这个结论的文档。

另外,快速打开app是很重要的,特别是用户第一次打开它时,对app来讲,第一印象太太太重要了。

你能做的就是使它尽可能做更多的异步任务,比如加载远端或者数据库数据,解析数据。

还是那句话,避免过于庞大的XIB,因为他们是在主线程上加载的。所以尽量使用没有这个问题的Storyboards吧!

  注意,用Xcode debug时watchdog并不运行,一定要把设备从Xcode断开来测试启动速度

2.16 学会手动创建Autorelease Pool

NSAutoreleasePool负责释放block中的autoreleased objects。一般情况下它会自动被UIKit调用。但是有些状况下你也需要手动去创建它。

假如你创建很多临时对象,你会发现内存一直在减少直到这些对象被release的时候。这是因为只有当UIKit用光了autorelease pool的时候memory才会被释放。

好消息是你可以在你自己的@autoreleasepool里创建临时的对象来避免这个行为:

NSArray *urls = [@"url1",@"url2"];
for (NSURL *url in urls) {
@autoreleasepool {
NSError *error;
NSString *fileContents = [NSString stringWithContentsOfURL: url encoding: NSUTF8StringEncoding error: &error];
/* Process the string, creating and autoreleasing more objects. */
}
}

这段代码在每次遍历后释放所有autorelease对象。

链接

iOS 性能调试的更多相关文章

  1. iOS 性能优化收集

    iOS 性能调试 instrument Instrument Instrument之Core Animation工具 避免图层混合 ①.确保控件的opaque属性设置为true,确保backgroun ...

  2. IOS 支付、性能调试、IPv6兼容支持等

    微信支付 支付宝支付 性能调试 IPv6兼容支持 APP引导页框架

  3. iOS各种调试技巧豪华套餐

    转载自http://www.cnblogs.com/daiweilai/p/4421340.html 目录 前言 逼优鸡 知己知彼 百战不殆 抽刀断Bug 普通操作 全局断点(Global Break ...

  4. iOS性能优化:Instruments使用实战

    iOS性能优化:Instruments使用实战   最近采用Instruments 来分析整个应用程序的性能.发现很多有意思的点,以及性能优化和一些分析性能消耗的技巧,小结如下. Instrument ...

  5. IOS性能调优系列:使用Instruments动态分析内存泄漏

    硬广:<IOS性能调优系列>第二篇,持续更新,欢迎关注. 第一篇介绍了Analyze对App做静态分析,可以发现应用中的内存泄漏问题,对于有些内存泄漏情况通过静态分析无法解决的,可以通过动 ...

  6. IOS性能调优系列:使用Zombies动态分析内存中的僵尸对象

    硬广:<IOS性能调优系列>第四篇,预计会有二十多篇,持续更新,欢迎关注. 前两篇<IOS性能调优系列:Analyze静态分析>.<IOS性能调优系列:使用Instrum ...

  7. IOS各种调试

    IOS各种调试技巧豪华套餐   目录 前言逼优鸡知己知彼 百战不殆抽刀断Bug 普通操作 全局断点(Global BreakPoint) 条件断点(Condational Breakpoints)打印 ...

  8. 使用CoreProfiler/NanoProfiler实现跨平台&应用的整合性能调试

    摘要 NanoProfiler是一个开源.NET性能调试类库,CoreProfiler是其.NET Core版本的实现.在之前的一些文章中,我曾介绍过NanoProfiler的主要使用方式,以及如何为 ...

  9. CoreProfiler/NanoProfiler性能调试监控系列总目录

    NanoProfiler - 适合生产环境的性能监控类库 之 基本功能篇 NanoProfiler - 适合生产环境的性能监控类库 之 大数据篇 NanoProfiler - 适合生产环境的性能监控类 ...

随机推荐

  1. 您真的理解了SQLSERVER的日志链了吗?

    您真的理解了SQLSERVER的日志链了吗? 先感谢宋沄剑给本人指点迷津,还有郭忠辉童鞋今天在QQ群里抛出的问题 这个问题跟宋沄剑讨论了三天,再次感谢宋沄剑 一直以来,SQLSERVER提供了一个非常 ...

  2. Microsoft SQL Server中的事务与并发详解

    本篇索引: 1.事务 2.锁定和阻塞 3.隔离级别 4.死锁 一.事务 1.1 事务的概念 事务是作为单个工作单元而执行的一系列操作,比如查询和修改数据等. 事务是数据库并发控制的基本单位,一条或者一 ...

  3. 这些年一直记不住的 Java I/O

    参考资料 该文中的内容来源于 Oracle 的官方文档.Oracle 在 Java 方面的文档是非常完善的.对 Java 8 感兴趣的朋友,可以从这个总入口 Java SE 8 Documentati ...

  4. Concurrency != Parallelism

    前段时间在公司给大家分享GO语言的一些特性,然后讲到了并发概念,大家表示很迷茫,然后分享过程中我拿来了Rob Pike大神的Slides <Concurrency is not Parallel ...

  5. ABP框架 - 动态Web Api层

    文档目录 本节内容: 创建动态Web Api控制器 ForAll 方法 重写 ForAll ForMethods Http 动词 WithVerb 方法 HTTP 特性 命名约定 Api 浏览器 Re ...

  6. Failure to find xxx in xxx was cached in the local repository, resolution will not be reattempted until the update interval of nexus has elapsed or updates are forced @ xxx

    问题: 在linux服务器上使用maven编译war时报错: 16:41:35 [FATAL] Non-resolvable parent POM for ***: Failure to find * ...

  7. HTML中的拖拉框----在路上(29)

    拖拽框---当鼠标按在指定的区域才可进行拖拽 思想:只有当鼠标是按在一个大div里的小div才可拖拽,其他不可:拖拽框有多种方法,这里以其中一种分享:下面使我自己写的demo,简单有效. <!D ...

  8. 使用Red Gate Sql Compare 数据库同步工具进行SQL Server的两个数据库的结构比较、同步

    将测试版的项目同步(部署)到正式版的时候,两个数据库的结构比较与同步时,如果修改数据库的时候没有记录好修改了那些表,很难将两个数据库进行同步 RedGate Sql Compare使用简介说明: 1. ...

  9. ASP.NET MVC5+EF6+EasyUI 后台管理系统(45)-工作流设计-设计步骤

    系列目录 步骤设计很重要,特别是规则的选择. 我这里分为几个规则 1.按自行选择(在起草时候自行选审批人,比较灵活) 2.按上级(无需指定,当时需要知道用户的上司是谁,可以在职位管理设置,或者在用户表 ...

  10. Windows Live Writer 在线安装失败的解决方法。

    这里提供一种解决方法:下载离线安装包,我这个版本是2011的.大家有兴趣的话可以下载一下: http://wl.dlservice.microsoft.com/download/8/3/D/83D75 ...