Senior Mobile Developer - Vale

Contact me via @cwtututu.


  • Home

  • Categories

  • Archives

  • Tags

  • About

管理iOS代码的一些规范

Posted on Mar 19 2013   |   In Code-Standard   |  
  1. 做任何事前先git init。
  2. 使用CocoaPods管理第三方库。
  3. 使用ARC。如果第三方库没有使用ARC,可以在他们的.m文件中加上-fno-objc-arc关键字。
  4. 清除@synthesize。当你为成员变量增加@property时,Xcode会在编译时候默认为你增加@synthesize关键字。
  5. 移除注释掉的代码或工程中未使用的文件。
  6. 不要在AppDelete中写代码。
  7. 增加#pragram mark –在你需要注释的地方
  8. 管理好Xcode工程中的文件夹结构。一般使用Model,View,Controller,Helper/Manager和Images文件夹。如果有第三方库,则用External文件夹。
  9. 代码格式,空格。
  10. 将__mycompany改成有意义的。
  11. 时常NSLog,形成日志系统。

—————–神奇的分割线——————-

又找到了一些规范:

Under the Warnings header, also set the Other Linker Flags option to -Wall. The compiler will now check for all possible situations that can cause problems. By default many of these warnings are turned off but I find it useful to always have all warnings on and to treat them as fatal errors. In other words, if the compiler gives a warning I will first fix it before continuing. Whether that is something you may want to do on your own projects is up to you, but during the conversion to ARC I recommend that you take a good look at any issues the compiler may complain about.
For the exact same reason, also enable the Run Static Analyzer option under the Build Options header, Xcode will now run the analyzer every time we build the app. That makes the builds a bit slower but for an app of this size that’s barely noticeable.

如果大家有什么好的代码规范,欢迎补充。O(∩_∩)O~。

Blocks的申明调用与Queue当做锁的用法

Posted on Mar 10 2013   |   In GCD   |  

###Blocks的申明与调用
话说Blocks在方法内使用还是挺方便的,之前都是把相同的代码封装成外部函数,然后在一个方法里需要的时候调用,这样挺麻烦的。使用Blocks之后,我们可以把相同代码在这个方法里封装起来,然后再在这个方法中需要的地方直接调用,逻辑清晰,操作也不会那么繁琐。

上代码:

//在方法体内
//block申明与初始化
 void(^removePicViewBlock)(int,int) = ^(int start,int stop){

    while (start <= stop)
    {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        SWDPictureView *view = (SWDPictureView *)[self.scroller viewWithTag:start + 1];
        view.imageView.image = nil;
        view.imageView = nil;
        view.delegate2 = nil;
        [view removeFromSuperview];
        start ++;
        [pool drain];
    }


};
if (page < self.currentPage)
{
    //block的调用
    removePicViewBlock(0,page);
}
else if(page > self.currentPage)
{
    //block的调用
    removePicViewBlock(page,[self.picList count]-1);
}

###Queue作为锁的用法

说到多线程就必须要提到锁了,话说用锁也蛮麻烦,需要在初始化的时候申明一个锁,然后每个方法的开头加一把锁,结尾再把锁去掉。如果用queue就不必那么麻烦了。根据task在queue中FIFO的特性就可以防止多线程中资源被同时访问。

上代码。

//初始化时初始化一个queue
UserQueue = dispatch_queue_create("use for storing image to disk", nil);

//多线程调用时,将任务放在queue中执行。此时queue充当锁的角色。
//存储图片缓冲到硬盘
-(void)storeImage:(UIImage *)image key:(NSString *)key
{
    dispatch_async(UserQueue, ^{

        NSFileManager *fileManager = [[NSFileManager alloc] init];
        BOOL succ = [fileManager createFileAtPath:[self cachePathForKey:key] contents:UIImageJPEGRepresentation(image, (CGFloat)0.6) attributes:nil];
        NSLog(@"store successfully!!! %d",succ);
        [fileManager release];
        fileManager = nil;
    });

}

好了,就这些。

说说翻墙软件 GoAgentX

Posted on Mar 10 2013   |   In Wall   |  

翻墙软件对程序猿来说应该都是必备的吧,最近翻墙又出现了问题,所以又捣鼓了一遍。现在记下来,免得以后出问题又要翻半天。

  1. 申请Google Appengine。这是需要注意的是:申请时填写的app id不要包含移动平台的字段,比如iphone或者android。不然进入facebook,twitter网站会默认进入移动版。
  2. 下载GoAgentX。最新的版本里已经包含了goagent,所以不再需要下载goagent。
  3. 安装GoAgentX并配置。这里要注意的是:服务配置选项卡里面的服务密码要填写。其他设置中的“导入goagent根证书”要点击导入。一个是为了防止出现url/2的错误,一个是为了防止无法登陆https网站。
  4. chrome安装SwitchySharp插件,并在插件中导入http://goagent.googlecode.com/files/SwitchyOptions.bak

那么,希望各位能在一个开放自由的互联网环境中学习,成长。

Crashlytics 调试利器

Posted on Mar 9 2013   |   In Debug   |  

今天收到Crashlytics员工发来的邮件,说他收到很多我发的重复请求,如果已收到邀请,请及时回复。话说他这个注册还挺麻烦,先发一个邮件地址过去,要等一天的样子,然后会收到一个邮件说你是否通过验证,通过验证才能注册。不知道要等那么久,以为是邮箱的关系,就不断的请求请求。哈。不管怎样,总算可以注册了。

说一下Crashlytics吧。

了解的不多,大概是将它的sdk加入到工程中可以随时监控iOS的崩溃情况,甚至可以定位到代码行级别。这个比iOS的崩溃日志强多了。

更多优点使用之后再和大家分享。

DEE758DA-B46C-477A-B790-13C9B14DE43F

GCD和信号量

Posted on Mar 5 2013   |   In iOS GCD   |  

#GCD
概念不多说,直接上代码。话说也不是什么高深的东东,不过极大简化了代码,一目了然。后面对信号量的记录也采用了相同的原理。

//抛出线程
dispatch_async(dispatch_get_global_queue(0, 0), ^{

    NSURL * url = [NSURL URLWithString:@"http://www.google.com"];
    NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
    if (data != nil) {
        //抛出的线程执行完后,回到主线程处理界面逻辑。
        dispatch_async(dispatch_get_main_queue(), ^{

          });
    } else {

    }
});

#semaphore(信号量)
信号量主要是用在传说中的生产者消费者模式里面,当信号量为0时线程挂起,当信号量大于0时继续向下执行。以前研究过一次,不过没怎么搞懂,反而把界面给搞死了。这次,取相册里的图片,GCD加Semaphore,又折腾了一次,还好。不多说,继续上代码。

//这几段代码的大概意思是:抛一个线程出来,取相册里的group,再在取group中的线程中抛出线程,去取group中的result,最终到界面线程中去操作。
//这里需要两个信号量,一个用来阻塞取group的线程,避免界面线程先于取group的线程运行完毕。另外一个用来阻塞取result中的线程,避免取result的
//线程先于取group执行完毕。

//抛出线程
dispatch_async(dispatch_get_global_queue(0, 0), ^{

    //创建信号量
    dispatch_semaphore_t semaphore1 = dispatch_semaphore_create(0);
    dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0);
    ALAssetsGroupEnumerationResultsBlock groupEnumerAtion = ^(ALAsset *result, NSUInteger index, BOOL *stop){

        if (result == nil) {
            //当某个group取完毕后,信号量加1,dispatch_semaphore_wait方法执行,信号量为0,程序循环,去取下一个group中的result
            dispatch_semaphore_signal(semaphore2);

        }else if ([[result valueForProperty:ALAssetPropertyType]isEqualToString:ALAssetTypePhoto]||[[result valueForProperty:ALAssetPropertyType]
 isEqualToString:ALAssetTypeVideo]) {

            FileNode *node = [[FileNode alloc] init];
            node.m_name =  result.defaultRepresentation.filename;
            node.m_path = [result.defaultRepresentation.url absoluteString];
            [self.picList addObject:node];
            NSLog(@"%@",node.m_name);
            NSLog(@"%@",node.m_path);
            [node release];

        }
    };


    ALAssetsLibraryGroupsEnumerationResultsBlock libraryGroupsEnumeration = ^(ALAssetsGroup* group, BOOL* stop){

        if (group!=nil)
        {

            [group enumerateAssetsUsingBlock:groupEnumerAtion];
            dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);

        }else{
            //当所有group取完后,信号量加1,程序不再阻塞,进入界面线程。
            dispatch_semaphore_signal(semaphore1);
        }

    };

    ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *error){


        NSLog(@"failureblock:%@",error);
    };


    ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
    [library enumerateGroupsWithTypes:ALAssetsGroupAll
                           usingBlock:libraryGroupsEnumeration
                         failureBlock:failureblock];

    dispatch_semaphore_wait(semaphore1, DISPATCH_TIME_FOREVER);
    dispatch_release(semaphore1);
    dispatch_release(semaphore2);
    dispatch_async(dispatch_get_main_queue(), ^{

        [self loadPicAtPage:self.currentPage];
        [self loadPicAtPage:self.currentPage + 1];
    });
});

Git 简易指南

Posted on Mar 3 2013   |   In git   |  

以前看git文档,看的云里雾里的,没怎么看懂,后来在公司用了svn,对版本控制了解更深了些。最近看到了这篇《Git简易指南》彻底明白git是怎么回事了。以防此好文丢失,现转载如下:

创建新仓库

创建新文件夹,打开,然后执行

git init

以创建新的 git 仓库。

#检出仓库
执行如下命令以创建一个本地仓库的克隆版本:

git clone /path/to/repository

如果是远端服务器上的仓库,你的命令会是这个样子:

git clone username@host:/path/to/repository

#工作流
你的本地仓库由 git 维护的三棵“树”组成。第一个是你的 工作目录,它持有实际文件;第二个是 缓存区(Index),它像个缓存区域,临时保存你的改动;最后是 HEAD,指向你最近一次提交后的结果。

git-trees

#添加与提交
你可以计划改动(把它们添加到缓存区),使用如下命令:

git add <filename>
git add *

这是 git 基本工作流程的第一步;使用如下命令以实际提交改动:

git commit -m "代码提交信息"

现在,你的改动已经提交到了 HEAD,但是还没到你的远端仓库。

#推送改动
你的改动现在已经在本地仓库的 HEAD 中了。执行如下命令以将这些改动提交到远端仓库:

git push origin master

可以把 master 换成你想要推送的任何分支。
如果你还没有克隆现有仓库,并欲将你的仓库连接到某个远程服务器,你可以使用如下命令添加:

git remote add origin <server>

如此你就能够将你的改动推送到所添加的服务器上去了。

#分支
分支是用来将特性开发绝缘开来的。在你创建仓库的时候,master 是“默认的”。在其他分支上进行开发,完成后再将它们合并到主分支上。

branches

创建一个叫做“feature_x”的分支,并切换过去:

git checkout -b feature_x

切换回主分支:

git checkout master

再把新建的分支删掉:

git branch -d feature_x

除非你将分支推送到远端仓库,不然该分支就是 不为他人所见的:

git push origin <branch>

#更新与合并
要更新你的本地仓库至最新改动,执行:

git pull

以在你的工作目录中 获取(fetch) 并 合并(merge) 远端的改动。
要合并其他分支到你的当前分支(例如 master),执行:

git merge <branch>

两种情况下,git 都会尝试去自动合并改动。不幸的是,自动合并并非次次都能成功,并可能导致 冲突(conflicts)。 这时候就需要你修改这些文件来人肉合并这些 冲突(conflicts) 了。改完之后,你需要执行如下命令以将它们标记为合并成功:

git add <filename>

在合并改动之前,也可以使用如下命令查看:

git diff <source_branch> <target_branch>

#标签
在软件发布时创建标签,是被推荐的。这是个旧有概念,在 SVN 中也有。可以执行如下命令以创建一个叫做 1.0.0 的标签:

git tag 1.0.0 1b2e1d63ff

1b2e1d63ff 是你想要标记的提交 ID 的前 10 位字符。使用如下命令获取提交 ID:

git log

你也可以用该提交 ID 的少一些的前几位,只要它是唯一的。

#替换本地改动
假如你做错事(自然,这是不可能的),你可以使用如下命令替换掉本地改动:

git checkout -- <filename>

此命令会使用 HEAD 中的最新内容替换掉你的工作目录中的文件。已添加到缓存区的改动,以及新文件,都不受影响。

假如你想要丢弃你所有的本地改动与提交,可以到服务器上获取最新的版本并将你本地主分支指向到它:

git fetch origin
git reset --hard origin/master

#有用的贴士
内建的图形化 git:gitk

彩色的 git 输出:

git config color.ui true

显示历史记录时,只显示一行注释信息:

git config format.pretty oneline

交互地添加文件至缓存区:

git add -i

#有用的图片
1352126739_7909

转自:http://rogerdudler.github.com/git-guide/index.zh.html

Read more »

2012年总结,2013年计划

Posted on Feb 16 2013   |   In Life   |  

2012年经历了很多,辞职,回校答辩,找工作,结婚,拿毕业证,还有将要出生的涂这这同学,这一年过的好赶啊。

2012年最想做的事就是辞职呆在家里做freelancer,结果因对自己信心不足而放弃。虽然如此,做一名优秀的freelancer还是我的梦想,2013年将继续努力,希望离这个梦想更近一点。2012年学习的时间少了很多,在新公司一大半的时间都在做维护工作,因为是oem的原因,所以很繁琐很累,每晚都九点十点才回家,周末也时常加班,没有时间搞学习,也没时间陪家人,这也是我最痛心的事。

说说2013年的计划吧。

  • 锻炼身体。

    坚持每晚下班回家跑步,做仰卧起坐。身体很重要很重要!!!

  • 研究fmpeg,并做出全能播放器。

    术业有专攻,就从视频编解码开始。

  • 学习c++,研究boost库。

  • 熟练运用blocks。

  • 熟悉android开发平台。

  • 学会弹几首吉他曲。


一些补充(2013.3.30):

  • 熟悉ARC
  • OpenGL
  • 学习英语
  • 熟悉Ruby on Rails

话说想到在公司工作我又想吐槽,本来想将多抽出一点时间陪家人作为新一年的计划,想想实际情况还是算了。公司的情况是无论你多么的高效,永远有做不完的事,就算没事早点回家领导也会有意见。不知道是公司特例还是普遍现象。也欢迎大家吐槽。

Octopress怎么增加导航栏标签

Posted on Feb 16 2013   |   In octopress   |  

多年前学习的html知识都又还回去了,再加上不懂ruby,对博客样式的修改觉得还很吃力。几经摸索,找到增加导航栏标签的方法如下:

###用命令行增加新页面
tumatoMac-mini:octopress tuchangwei$ rake new_page[你要添加的标签]
mkdir -p source/你要添加的标签
Creating new page: source/你要添加的标签/index.markdown

###在source/_includes/custom/navigation.html中添加如下代码

<li><a href="{{ root_url }}/你要添加的标签">你要添加的标签名</a></li>

那么剩下的操作就是在新的标签页的index.markdown文件中写博文了。

Octopress 怎么添加分类

Posted on Feb 16 2013   |   In octopress   |  

大致步骤为:

###增加category_list插件
保存以下代码到plugins/category_list_tag.rb:

module Jekyll
  class CategoryListTag < Liquid::Tag
    def render(context)
      html = ""
      categories = context.registers[:site].categories.keys
      categories.sort.each do |category|
        posts_in_category = context.registers[:site].categories[category].size
        category_dir = context.registers[:site].config['category_dir']
        category_url = File.join(category_dir, category.gsub(/_|\P{Word}/, '-').gsub(/-{2,}/, '-').downcase)
        html << "<li class='category'><a href='/#{category_url}/'>#{category} (#{posts_in_category})</a></li>\n"
      end
      html
    end
  end
end

Liquid::Template.register_tag('category_list', Jekyll::CategoryListTag)

这个插件会向liquid注册一个名为category_list的tag,该tag就是以li的形式将站点所有的category组织起来。如果要将category加入到侧边导航栏,需要增加一个aside。

###增加aside
复制以下代码到source/_includes/asides/category_list.html。(请将下面的$号换成%号)

<section>
  <h1>Categories</h1>
  <ul id="categories">      
      {$ category_list $}        
  </ul>
</section>

配置侧边栏需要修改_config.yml文件,修改其default_asides项:

default_asides: [asides/category_list.html, asides/recent_posts.html]

###使用
当新建markdown文件时,顶部会自动显示:“categories:”,在其后写上自己的分类名即可。

###参考
http://codemacro.com/2012/07/18/add-category-list-to-octopress/

Read more »

终于有自己的博客了

Posted on Feb 15 2013   |   In octopress   |  

将octopress搭建在github上,我终于有了自己的第一个博客。
昨天在网上瞎逛,看到唐巧的博客,说自己的博客是建立在github上,前段时间因为12306的关系,github无法访问。于是搜索资料,开始搭建自己的第一个博客,由于中间安装ruby以及配置SSH key的关系,到目前为止才搭建完毕。

####参考

  • http://blog.devtang.com/blog/2012/02/10/setup-blog-based-on-github/
  • http://easypi.github.com/blog/2013/01/05/using-octopress-to-setup-blog-on-github/
  • http://sharecore.info/blog/2012/12/25/octopress-plus-github/

####博文发布工具

  • http://mouapp.com/
1…78
Changwei

Changwei

I develop iOS/Android apps with Swift/Kotlin language.

80 posts
33 categories
34 tags
GitHub Twitter Weibo Linkedin Upwork peopleperhour
Creative Commons
© 2011 - 2020 Changwei
Powered by Hexo
Theme - NexT.Muse