池上金的技术博客

不忘初心,方得始终

将Swift项目中的TODO显示为Warning

我的代码很少注释1。但有一个例外:使用// TODO: and // FIXME:以高亮我不久后将要重新查看的代码段。这样的好处是在编辑框的条专栏弹窗里,点击一下粗体字就能跳转到该行代码。

icon1

这也有个问题,就是容易忘记。我曾经把它们记在我的todo管理里,Tings . 这又变成了重复工作。如何更好地标记这些错误呢?

杰弗里Sambells写了一篇如何在Objective-C工程中将这些注释变成Xcode Warning的文章。稍加改动,这是一个将Swift项目的TODO:和FIXME:显示为Warning的Script(应用于工程的Build Phases设置):

TAGS="TODO:|FIXME:"
echo "searching ${SRCROOT} for ${TAGS}"
find "${SRCROOT}" \( -name "*.swift" \) -print0 | xargs -0 egrep --with-filename --line-    number --only-matching "($TAGS).*\$" | perl -p -e "s/($TAGS)/ warning: \$1/"

运行一下,TODO:和FIXME:变成了醒目的警告。

icon2

比起记录在todo list的东西,这些黄色的警告总让我有莫大的冲动去清理他们,你觉得呢?

原文地址https://bendodson.com/weblog/2014/10/02/showing-todo-as-warning-in-swift-xcode-project/


  1. If your code needs commenting, it isn’t clear enough. Refactor until it is. If it doesn’t make sense because of semantics, rethink your naming conventions.

UINavigationController返回手势失效问题

从iOS7开始,系统为UINavigationController提供了一个interactivePopGestureRecognizer用于右滑返回(pop),但是,如果自定了back button或者隐藏了navigationBar,该手势就失效了。

image

这是为什么呢?

原因

我们知道,interactivePopGestureRecognizer从手势触发到行为发生,要经过下面的阶段:

image

interactivePopGestureRecognizer还存在,但没有起作用,可能是delegate里被阻断了没调用target/action,或者是调用了target/action没运行动画。

经过尝试(参考别人博客),发现自定义返回按钮或者隐藏navigationBar导致的该手势未起作用是因为在delegate阶段被阻断了。

如果我们知道action的名字,则可以添加一个自定义的滑动手势,直接调用该系统action。但API文档并没有提供。

结果就是要么自己实现滑动返回的动画action,要么自己重写interactivePopGestureRecognizer的delegate以让手势继续下去,触发系统的动画action。

实现方法

那就把delegate自己实现一下吧。

新建一个类BaseNavigationController,实现delegate:

1
2
3
4
- (void)viewDidLoad {
    [super viewDidLoad];
    self.interactivePopGestureRecognizer.delegate =  self;
}

让手势生效

1
2
3
4
5
6
7
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if (self.viewControllers.count <= 1 ) {
        return NO;
    }

    return YES;
}

在需要滑动返回的地方的UINavigationController换成BaseNavigationController

完成!

image

然后考虑到在push动画发生的时候,禁止滑动手势,在BaseNavigationController添加

1
2
3
4
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [super pushViewController:viewController animated:animated];
    self.interactivePopGestureRecognizer.enabled = NO;
}

在使用navigationController的viewcontroller里添加

1
2
3
4
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}

结果

当然不需要把上面的代码都抄一遍,因为这么通用的功能由一位韩国开发者做成组件,放在了github https://github.com/devxoul/SwipeBack。你只需要

1
2
platform :ios, '7.0'
pod 'SwipeBack', '~> 1.0'

该工程用了category+JRSwizzle交换了上面涉及到的UINavigationController的那些方法,还额外考虑了只自定义back button而不隐藏navigationBar的情况。无需一行代码,让系统的右滑返回动画重新回来!

如何去除UISearchBar颜色的阴影

其实是UISearchBar有一个不对外开放的“背景View”:UISearchBarBackground。如果你想让UISearchBar变得“扁平”,或者定制一些特别颜色,设置tintColor或者backgroundColor都是没有理想效果的——依然会得到一个带阴影的颜色。

那么,就让那讨厌的“背景View”透明吧:

@interface UISearchBar (CustomBackground)

@end


@implementation UISearchBar( CustomBackground )

- (void)drawRect:(CGRect)rect {
    for( UIView *subview in self.subviews ) {
        if( [subview isKindOfClass:NSClassFromString(@"UISearchBarBackground")]) {
        [subview setAlpha:0.0F];
        break;
        }
    }
}

@end

Update:在实际开发中还是用CustomBackgroundSearchBar : UISearchBar 好一点,毕竟存在有点UISearchBar需要定制而另外的又不需要的情况,用Category比较不好控制。

SenTestingKit Not Loaded

上次因为新工程的迁移,为了图方便直接把旧的引用包全选拖动到新建的工程里,结果出现了这个错误并闪退。 搜索了一下:

在发布的target的build phase的link binary删掉SenTestingKit 然后在单元测试的build phase加上它。

记得有人说过:越是看起来表现怪异的bug,其原因往往越是简单。

悲剧地验证了。

记录UIPageViewController不能正确地setNavigationBarHidden的一个坑

今天碰到一个奇怪bug:UIPageViewController的viewControllers里面做

1
[self.navigationController setNavigationBarHidden:YES animated:animated];

操作,在iOS5里面,可以成功隐藏UIPageViewController的navBar,但是在iOS6里面,却只能在view已经显示完成手动调用才可以隐藏,而写在UIPageViewController的viewcontrollers里面的如下代码

1
2
3
4
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.navigationController setNavigationBarHidden:YES animated:animated];
}

却不起作用!!!

一般来说,低版本不可用高版本可用,第一步分析是不是使用了高版本才有的api;而高版本不可用低版本却可用,第一步分析是否函数已经过期不再被调用(就像viewDidunload那样)和函数的被调用顺序是否已经改变

经过打印log分析得出结论:

在iOS5里,UIPageViewController会先调用自身的viewWillAppear,然后再调用它的viewControllers的viewWillAppear

在iOS6里,UIPageViewController会先调用它的viewControllers的viewWillAppear,然后再调用自身的viewWillAppear

so,为了在iOS5和iOS6里都能正确地在UIPageViewController一显示就隐藏navbar,只要把setNavigationBarHidden写到UIPageViewController的viewWillAppear。听起像白痴的废话,都是苹果造成的!

莫名其妙的一个坑,UIPageViewController Class Reference什么都没提起!!!

在UIWebView中调用Object-c本地代码

有些时候,HTML5可以做很酷的事情,比如绚丽的动画,或者快速开发跨平台的产品模型。于是我们希望JS代码和Native App多做些交互,比如在UIWebView的JS代码中调用本地的Object-c代码。

实际的需求是我们需要在APP里展示一个动画,动画运行结束后自己消失。 实现的思路是:让Parter做一个HTML5动画,然后用UIWebView调用本地的HTML5文件展示动画,在动画结束之后,用JS代码回调Object-c代码,dismiss掉UIWebView。我们知道UIWebView有一个@optional的Delegate:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;

从文档里可以知道每个UIWebView在load一个frame的时候(说实在的我也不懂load一个frame是什么意思!暂且理解为收到一个某种类型的url request请求吧。)都会调用这个函数,那我们就可以在这个函数里拦截url并决定是否开始load动作。我在这里是想要调用Object-c代码来dismiss掉WebView本身,当然不让load。

以下是实现的代码:

  • JS代码

      window.loaction = "action?dismiss";
    

    (我把这个理解成导致UIWebView开始load frame动作的原因,你如果熟悉JS代码的话可以告诉我原理啊。)

  • Object-c代码

    新建UIWebViewController并显示:

      //加载本地HTML5
      UIWebView *webView = [[UIWebView alloc] initWithFrame:APP_FRAME];
      NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"index" ofType:@"html" inDirectory:@"yourHTMLDirectory"]];
      webView.delegate = self;
      [webView loadRequest:[NSURLRequest requestWithURL:url]];
    
      //Pop WebViewController
      loginAdController = [[UIViewController alloc] initWithNibName:nil bundle:nil];
      loginAdController.view = webView;
      [webView release];
      [homePage presentModalViewController:loginAdController animated:YES];
      [loginAdController release];
    

    UIWebViewDelgate:

      - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
          NSString *url = [[request URL] absoluteString];
          NSArray *urlArray = [url componentsSeparatedByString:@"/"];
          NSString *actionString = [urlArray lastObject];
          if ( actionString && [actionString isEqualToString:@"action?dismiss"]) {
              [self performSelector:@selector(dismissAdWebView) withObject:nil afterDelay:2.5];
              return NO;
          }
    
          return YES;
      }
    

    dismiss掉动画:

      - (void)dismissAdWebView {
          [loginAdController dismissModalViewControllerAnimated:YES];
      }
    

至此整个功能完成。

参考文档:

http://stackoverflow.com/questions/1662473/how-to-call-objective-c-from-javascript

[翻译]给Object-c的Category新增Properties(Adding Properties to an Objective-C Category)

如果需要给一个category保存一些信息,很不幸,你不能直接给它新增实例变量,但你可以新增一个“Associated Reference”,文档在

这里

Associative references,Mac OS X 10.6以后可用,给当前类新增一个模拟的实例变量

使用Associated Reference实现Property存储

我们可以用这种技术给编译后的Class设置一个想要的Property。在这个例子里,我想给UIView指定类型,新增一个styleName。

    @interface UIView (DHStyleManager)
    @property (nonatomic, copy) NSString* styleName;
    @end

    #import "UIViewDHStyleManager.h"

    NSString * const kDHStyleKey = @"kDHStyleKey";


    @implementation UIView (DHStyleManager)

    - (void)setStyleName:(NSString *)styleName
    {
        objc_setAssociatedObject(self, kDHStyleKey, styleName, OBJC_ASSOCIATION_COPY);
    }

    - (NSString*)styleName
    {
        return objc_getAssociatedObject(self, kDHStyleKey);
    }

    @end

当UIView实例release的时候,对应的Associated Reference也会被release。

这种技术使得设置自定义Property给运行时的UIView成为可能,如果你只是想实现一些诸如增加Property之类的简单需求,这无疑是一种很好的继承替代方案。

    #import UIViewDHStyleManager.h"

    UIView* v = [[[UIView alloc] init] autorelease];
    v.styleName = @"someStyleName";
    NSLog(@"v = %@",v.styleName); //Logs 'someStyleName'

关于

你好,我是David Hamrick,我是Hamrick Software软件公司拥有人之一。我们开发了一款叫做“VueScan”的扫描仪驱动软件,它支持OS X,Windows,Linux,和iOS。


注:@property为assign的时候,如果property指向的对象已经被释放,用 ![]self styleName]貌似不能判断为空,property已经成了野指针,需谨慎使用以免带来bug。

原文地址:http://www.davidhamrick.com/2012/02/12/Adding-Properties-to-an-Objective-C-Category.html

Mac OS X 10.8.2 下尝试Go语言

安装Go

*. 安装Mercurial

    sudo easy_install mercurial

*. 获取Go源码

$ hg clone -r release https://go.googlecode.com/hg/ go

*. 源码下载完成之后,安装Go

$ cd go/src
    $ ./all.bash

*. 根据安装信息的最后一段提示,在etc/paths文件里,用管理员指令(sudo)添加Go Path。

HelloWorld

*. 编写源代码

package main

import "fmt"

func main() {
      fmt.Printf("hello, world\n")
}

保存为HelloWorld.go,cd到源码目录

go build HelloWorld.go

如果有错误,检查源代码,很可能是没有错误,并且已经生成了HelloWorld可执行文件。

./HelloWorld

至此,HelloWorld完成。

Go之初体验

  1. Go语言语法和C语言类似,很适合科班程序员学习新语言或者底层C语言开发人员学应用开发的时候使用。它没有C语言那么自由,比如{}的语句块,前一个{必须放在控制语句后面,不得另起一行,在C语言中,有一种严格的编码规范就是像Go这样,但是这一点貌似不能说明什么,Go只是少了一种无谓的选择。不过,if,for和switch语句控制快少了(),语句结尾不需要";“,甚至,少掉了else语句,干掉了异常处理。

  2. 不过,switch语句更智能了,函数支持多个返回值了,并且只要简单的"return reval1, reval2"就可以返回两个值。Go可真是该减肥的地方就减肥了,该丰满的地方就丰满到了恰到好处,我很喜欢的Object-c虽然也近乎完美地扩展了C语言,但其封闭的生态首先比不上Go了.一味简单地往C语言身上加特性,就没有Go的减肥功效啦!

  3. 其他更强大的并行特性,网络编程支持等,以后学习了再分享。

观点

  1. 我发现我花了四年时间锤炼自己用 C 语言构建系统的能力,试图找到一个规范,可以更好的编写软件。结果发现只是对 Go 的模仿。——云风《Go 语言初步》

  2. 我为什么喜欢Go语言——AllenDang

参考资料:Go语言文档

APUE源码编译方法

拿书上的第一段源码作为例子:

#include "apue.h"
#include <dirent.h>

int main(int argc, char *argv[]) {
    DIR *dp;
    struct dirent *dirp;

    if (argc != 2) {
        err_quit("usage:ls direntory_name");
    }
    if ((dp = opendir(argv[1])) == NULL)
    err_sys("can't open %s", argv[1]);
    while ((dirp =  readdir(dp)) != NULL)
    printf("%s\n", dirp->d_name);

    closedir(dp);
    exit(0);
}

源码里的apue.h并没有实现err_quit函数,因为实现在apue源码包的lib里。

先下载apue源码包:http://download.csdn.net/detail/i2c_rs485/4614061 (第二版使用,不保证更新)

然后:

  1. 解压文件到apue.2e目录

  2. 修改相应平台的文件,我使用的是mac,所以修改Make.defines.macos 你修改的只需要这一行WKDIR=/home/your_dir/apue2e_src/apue.2e,改成自己的目录路径

  3. cd到apue.2e目录的lib目录下, 执行make -f macos.mk,之后你会在lib目录下面找到libapue.a这个文件. 现在,你可以把它拷贝到任何地方,在编写例子的时候,你就可以

  4. 拷贝apue2e_src/apue.2e/include/apue.h和apue2e_src/apue.2e/lib/libapue.a 到你的源代码目录。

  5. 使用gcc -o hello hello.c libapue.a来编译你的源代码

使用上面的方法编译第一段源码:

gcc -o myls myls.c libapue.a 

运行:

unit_1 ihome$ ./myls /Volumes
.
..
BOOTCAMP
File
MacSys