LNUniversal Cocoapods私有库的使用

LNUniversal是自己业余时间整理的一个比较雏形的工具库,包含一些扩展和常用代码,以后也会不定时更新维护,所以目前只是简单的使用,昨天晚上花了几个小时研究了下 Cocoapods私有库的创建和使用,关于这方面可以具体查看我转载的一篇文字:使用Cocoapods创建私有podspec .目前测试通过,所以写一篇LNUniversal的简单使用。

LNUniversal地址:http://git.oschina.net/iliunian/LNUniversal

##LNUniversal 私有库的本地创建
1.首先本地需要安装Cocoapods,具体安装和初始化,这里不做表述。

2.进入本地Cocoapods库文件夹,PS:Specs是Cocoapods在本地的一个类似所有第三方库的一个索引目录。

liunian:~ liunian$ cd ~/.cocoapods/repos/master/Specs

3.创建LNUniversal目录,如果已经创建过的可以直接进入

liunian:Specs liunian$ mkdir LNUniversal

4.进入LNUniversal目录并创建版本目录,PS:0.0.1是LNUniversal默认的版本号,这个初始化后我也没有在修改。

liunian:LNUniversal liunian$ mkdir 0.0.1

5.下载LNUniversal.podspec文件地址:https://git.oschina.net/iliunian/LNUniversal.git

6.将LNUniversal.podspec复制到地4步创建的0.0.1里面

PS:至此本地的LNUniversal 已经创建完毕.

##LNUniversal 私有库的使用

在工程的Podfile文件导入该库,由于是私有库,所以写法跟公有库自然不同。

pod 'LNUniversal', :git=> 'https://git.oschina.net/iliunian/LNUniversal.git'

然后执行 pod install或者 update即可

Pod::Spec.new do |s|
  s.name             = "LNUniversal"    #名称
  s.version          = "0.0.1"             #版本号
  s.summary          = "Just LNUniversal."     #简短介绍,下面是详细介绍
  s.description      = <<-DESC
                       Testing Private Podspec.

                       * Markdown format.
                       * Don't worry about the indent, we strip it!
                       DESC
  s.homepage         = "http://git.oschina.net/iliunian/LN-Universal-Framework"                           #主页,这里要填写可以访问到的地址,不然验证不通过
  # s.screenshots     = "www.example.com/screenshots_1", "www.example.com/screenshots_2"           #截图
  s.license          = 'MIT'              #开源协议
  s.author           = { "liu nian" => "i19850511@gmail.com" }                   #作者信息
  s.source           = { :git => "https://git.oschina.net/iliunian/LN-Universal-Framework.git", :tag => "0.0.1" }      #项目地址,这里不支持ssh的地址,验证不通过,只支持HTTP和HTTPS,最好使用HTTPS
  # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'                       #多媒体介绍地址

  s.platform     = :ios, '7.0'            #支持的平台及版本
  s.requires_arc = true                   #是否使用ARC,如果指定具体文件,则具体的问题使用ARC

  s.subspec 'Common' do |common|
    common.source_files = 'LNUniversal/LNQuick/Common/*.{h,m}'
    common.public_header_files = 'LNUniversal/LNQuick/Common/*.h'
  end

  s.subspec 'Category' do |category|
    category.source_files = 'LNUniversal/LNQuick/Category/*.{h,m}'
    category.public_header_files = 'LNUniversal/LNQuick/Category/*.h'
  end

  s.subspec 'EnvObserver' do |envObserver|
    envObserver.source_files = 'LNUniversal/LNQuick/EnvObserver/*.{h,m}'
    envObserver.public_header_files = 'LNUniversal/LNQuick/EnvObserver/*.h'
  end

  s.subspec 'Utility' do |utility|
    utility.source_files = 'LNUniversal/LNQuick/Utility/*.{h,m}'
    utility.public_header_files = 'LNUniversal/LNQuick/Utility/*.h'
  end

  s.subspec 'Modules' do |ss|
    ss.source_files = 'LNUniversal/LNQuick/Modules/*.{h,m}'
    ss.public_header_files = 'LNUniversal/LNQuick/Modules/*.h'
  end
  #s.public_header_files = 'LNUniversal/LNUniversal.h'   #公开头文件地址
  s.frameworks = 'UIKit'                 #所需的framework,多个用逗号隔开
  s.dependency 'AFNetworking', '~> 2.3'
  s.dependency 'Base64', '~> 1.0.1'
  s.dependency 'Masonry', '~> 0.5.3'
  s.dependency 'RegexKitLite', '~> 4.0'
  s.dependency 'UIViewController+HUD'

end

PS:LNUniversal 目前是私有项目。

06/02/2015 08:45 上午 posted in  Cocoapods

[转]使用Cocoapods创建私有podspec

使用Cocoapods创建私有podspec

Cocoapods是非常好用的一个iOS依赖管理工具,使用它可以方便的管理和更新项目中所使用到的第三方库,以及将自己的项目中的公共组件交由它去管理。Cocoapods的介绍及优点本文就不在赘述,我开始使用Cocoapods还是在两年前,那个时候它刚刚出现,网上的资料还非常的少,就连他们自己的HomePage都十分的简单,我就着手尝试着使用了一下,用它管理起第三方库确实是十分的方便顺手。后来它有了更强大的功能就是自己创建podspec,更可以设置私有的库。

春节回来上班,一天的工作结束之后,需要充实下自己,正好项目中有一些公共组件需要从庞大的项目体系中剥离出来,而且年前项目终于从SVN迁移到了Git,真是喜大普奔,大快人心!这样项目使用Cocoapods就有了条件,正好学习一下创建私有的podspec并在项目中部署使用,以及pods的subspec的创建及使用。
整体先说明一下创建一个私有的podspec包括如下那么几个步骤:

  1. 创建并设置一个私有的Spec Repo。
  2. 创建Pod的所需要的项目工程文件,并且有可访问的项目版本控制地址。
  3. 创建Pod所对应的podspec文件。
  4. 本地测试配置好的podspec文件是否可用。
  5. 向私有的Spec Repo中提交podspec。
  6. 在个人项目中的Podfile中增加刚刚制作的好的Pod并使用。
  7. 更新维护podspec。

在这一系列的步骤中需要创建两个Git仓库,分别是第一步和第二步(第二步不一定非要是Git仓库,只要是可以获取到相关代码文件就可以,也可以是SVN的,也可以说zip包,区别就是在podspec中的source项填写的内容不同),并且第一步只是在初次创建私有podspec时才需要,之后在创建其他的只需要从第二步开始就可以。本文只介绍在Git环境下的操作,其他环境其他方式暂不说明。

##创建私有Spec Repo

先来说第一步,什么是Spec Repo?他是所有的Pods的一个索引,就是一个容器,所有公开的Pods都在这个里面,他实际是一个Git仓库remote端
在GitHub上,但是当你使用了Cocoapods后他会被clone到本地的~/.cocoapods/repos目录下,可以进入到这个目录看到master文件夹就是这个官方的Spec Repo了。这个master目录的结构是这个样子的

.
├── Specs
    └── [SPEC_NAME]
        └── [VERSION]
            └── [SPEC_NAME].podspec

因此我们需要创建一个类似于master的私有Spec Repo,这里我们可以fork官方的Repo,也可以自己创建,个人建议不fork,因为你只是想添加自己的Pods,没有必要把现有的公开Pods都copy一份。所以创建一个 Git仓库,这个仓库你可以创建私有的也可以创建公开的,不过既然私有的Spec Repo,还是创建私有的仓库吧,需要注意的就是如果项目中有其他同事共同开发的话,你还要给他这个Git仓库的权限。因为GitHub的私有仓库是收费的,我还不是GitHub的付费用户,所以我使用了其他Git服务,我使用的是CODING,当然还有其他的可供选择开源中国、Bitbucket以及CSDN

创建完成之后在Terminal中执行如下命令

 # pod repo add [Private Repo Name] [GitHub HTTPS clone URL]
$ pod repo add WTSpecs https://coding.net/wtlucky/WTSpecs.git

此时如果成功的话进入到~/.cocoapods/repos目录下就可以看到WTSpecs这个目录了。至此第一步创建私有Spec Repo完成。

PS:如果有其他合作人员共同使用这个私有Spec Repo的话在他有对应Git仓库的权限的前提下执行相同的命令添加这个Spec Repo即可。

##创建Pod项目工程文件

这个第二步没有什么好介绍的,如果是有现有的组件项目,并且在Git的版本管理下,那么这一步就算完成了,可以直接进行下一步了。

如果你的组件还在你冗余庞大的项目中,需要拆分出来或者需要自己从零开始创建一个组件库,那么我建议你使用Cocoapods提供的一个工具将第二步与第三步结合起来做。

现在来说一下这个工具,相关的文档介绍是Using Pod Lib Create 就拿我创建的podTestLibrary为例子具体讲一下这里是如何操作的,先cd到要创建项目的目录然后执行

$ pod lib create podTestLibrary

之后他会问你四个问题,1.是否需要一个例子工程;2.选择一个测试框架;3.是否基于View测试;4.类的前缀;4个问题的具体介绍可以去看官方文档,我这里选择的是1.yes;2.Specta/Expecta;3.yes;4.PTL。 问完这4个问题他会自动执行pod install命令创建项目并生成依赖。

$ tree PodTestLibrary -L 2
PodTestLibrary
├── Example                                  #demo APP
│   ├── PodTestLibrary
│   ├── PodTestLibrary.xcodeproj
│   ├── PodTestLibrary.xcworkspace
│   ├── Podfile                              #demo APP 的依赖描述文件
│   ├── Podfile.lock
│   ├── Pods                                  #demo APP 的依赖文件
│   └── Tests
├── LICENSE                               #开源协议 默认MIT
├── Pod                                       #组件的目录
│   ├── Assets                            #资源文件
│   └── Classes                              #类文件
├── PodTestLibrary.podspec           #第三步要创建的podspec文件
└── README.md                                #markdown格式的README

9 directories, 5 files

以上是项目生成的目录结构及相关介绍。

接下来就是向Pod文件夹中添加库文件和资源,并配置podspec文件,我把一个网络模块的共有组件放入Pod/Classes中,然后进入Example文件夹执行pod update命令,再打开项目工程可以看到,刚刚添加的组件已经在Pods子工程下Development Pods/PodTestLibrary中了,然后编辑demo工程,测试组件,我并没有使用提供的测试框架进行测试,这里就先不介绍了。

注:这里需要注意的是每当你向Pod中添加了新的文件或者以后更新了podspec的版本都需要重新执行一遍pod update命令。

测试无误后需要将该项目添加并推送到远端仓库,并编辑podspec文件。

通过Cocoapods创建出来的目录本身就在本地的Git管理下,我们需要做的就是给它添加远端仓库,同样去GitHub或其他的Git服务提供商那里创建一个私有的仓库,拿到SSH地址,然后cd到PodTestLibrary目录

$ git add .
$ git commit -s -m "Initial Commit of Library"
$ git remote add origin git@coding.net:wtlucky/podTestLibrary.git           
#添加远端仓库
$ git push origin master     #提交到远端仓库

因为podspec文件中获取Git版本控制的项目还需要tag号,所以我们要打上一个tag,

$ git tag -m "first release" 0.1.0
$ git push --tags     #推送tag到远端仓库

做完这些就可以开始编辑podspec文件了,它是一个Ruby的文件,把编辑器的格式改成Ruby就能看到语法高亮,下面我贴上我的podspec文件,并在后面以注释的形式说明每个字段的含义,没有涉及到的字段可以去官方文档查阅

Pod::Spec.new do |s|
  s.name             = "PodTestLibrary"    #名称
  s.version          = "0.1.0"             #版本号
  s.summary          = "Just Testing."     #简短介绍,下面是详细介绍
  s.description      = <<-DESC
                       Testing Private Podspec.

                       * Markdown format.
                       * Don't worry about the indent, we strip it!
                       DESC
  s.homepage         = "https://coding.net/u/wtlucky/p/podTestLibrary"                           #主页,这里要填写可以访问到的地址,不然验证不通过
  # s.screenshots     = "www.example.com/screenshots_1", "www.example.com/screenshots_2"           #截图
  s.license          = 'MIT'              #开源协议
  s.author           = { "wtlucky" => "wtlucky@foxmail.com" }                   #作者信息
  s.source           = { :git => "https://coding.net/wtlucky/podTestLibrary.git", :tag => "0.1.0" }      #项目地址,这里不支持ssh的地址,验证不通过,只支持HTTP和HTTPS,最好使用HTTPS
  # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'                       #多媒体介绍地址

  s.platform     = :ios, '7.0'            #支持的平台及版本
  s.requires_arc = true                   #是否使用ARC,如果指定具体文件,则具体的问题使用ARC

  s.source_files = 'Pod/Classes/**/*'     #代码源文件地址,**/*表示Classes目录及其子目录下所有文件,如果有多个目录下则用逗号分开,如果需要在项目中分组显示,这里也要做相应的设置
  s.resource_bundles = {
    'PodTestLibrary' => ['Pod/Assets/*.png']
  }                                       #资源文件地址

  s.public_header_files = 'Pod/Classes/**/*.h'   #公开头文件地址
  s.frameworks = 'UIKit'                  #所需的framework,多个用逗号隔开
  s.dependency 'AFNetworking', '~> 2.3'   #依赖关系,该项目所依赖的其他库,如果有多个需要填写多个s.dependency
end

编辑完podspec文件后,需要验证一下这个文件是否可用,如果有任何WARNING或者ERROR都是不可以的,它就不能被添加到Spec Repo中,不过xcode的WARNING是可以存在的,验证需要执行一下命令

$ pod lib lint

当你看到

 -> PodTestLibrary (0.1.0)

PodTestLibrary passed validation.
时,说明验证通过了,不过这只是这个podspec文件是合格的,不一定说明这个Pod是可以用的,我们需要在本地做一下验证,这就是第四步的内容了,第四步在具体说明。

##创建podspec文件

如果从第二步过来,已经有了现成的项目,那么就需要给这个项目创建一个podspec文件,创建它需要执行Cocoapods的另外一个命令,官方文档在这里

$ pod spec create PodTestLibrary git@coding.net:wtlucky/podTestLibrary.git

执行完之后,就创建了一个podspec文件,他其中会包含很多内容,可以按照我之前介绍的进行编辑,没用的删掉。编辑完成之后使用验证命令验证一下

$ pod lib lint

验证无误就可以进入下一步了。

本地测试podspec文件

我们可以创建一个新的项目,在这个项目的Podfile文件中直接指定刚才创建编辑好的podspec文件,看是否可用。 在Podfile中我们可以这样编辑,有两种方式

platform :ios, '7.0'

pod 'PodTestLibrary', :path => '~/code/Cocoapods/podTest/PodTestLibrary'      #指定路径
pod 'PodTestLibrary', :podspec => '~/code/Cocoapods/podTest/PodTestLibrary/PodTestLibrary.podspec'  #指定podspec文件

然后执行pod install命令安装依赖,打开项目工程,可以看到库文件都被加载到Pods子项目中了,不过它们并没有在Pods目录下,而是跟测试项目一样存在于Development Pods/PodTestLibrary中,这是因为我们是在本地测试,而没有把podspec文件添加到Spec Repo中的缘故。

在项目中编写代码,测试库文件无误后就可以开始下一步了,提交podspec到Spec Repo中。

##向Spec Repo提交podspec

向Spec Repo提交podspec需要完成两点一个是podspec必须通过验证无误,在一个就是删掉无用的注释(这个不是必须的,为了规范还是删掉吧)。 向我们的私有Spec Repo提交podspec只需要一个命令

$ pod repo push WTSpecs PodTestLibrary.podspec  #前面是本地Repo名字 后面是podspec名字

完成之后这个组件库就添加到我们的私有Spec Repo中了,可以进入到~/.cocoapods/repos/WTSpecs目录下查看

.
├── LICENSE
├── PodTestLibrary
│   └── 0.1.0
│       └── PodTestLibrary.podspec
└── README.md

再去看我们的Spec Repo远端仓库,也有了一次提交,这个podspec也已经被Push上去了。

至此,我们的这个组件库就已经制作添加完成了,使用pod search命令就可以查到我们自己的库了

$ pod search PodTestLibrary

-> PodTestLibrary (0.1.0)
   Just Testing.
   pod 'PodTestLibrary', '~> 0.1.0'
   - Homepage: https://coding.net/u/wtlucky/p/podTestLibrary
   - Source:   https://coding.net/wtlucky/podTestLibrary.git
   - Versions: 0.1.0 [WTSpecs repo]

这里说的是添加到私有的Repo,如果要添加到Cocoapods的官方库了,可以使用trunk工具,具体可以查看官方文档

##使用制作好的Pod

在完成这一系列步骤之后,我们就可以在正式项目中使用这个私有的Pod了只需要在项目的Podfile里增加以下一行代码即可

$ pod 'PodTestLibrary', '~> 0.1.0'

然后执行pod update,更新库依赖,然后打卡项目可以看到,我们自己的库文件已经出现在Pods子项目中的Pods子目录下了,而不再是Development Pods。

##更新维护podspec

最后再来说一下制作好的podspec文件后续的更新维护工作,比如如何添加新的版本,如何删除Pod。

我已经制作好了PodTestLibrary的0.1.0版本,现在我对他进行升级工作,这次我添加了更多的模块到PodTestLibrary之中,包括工具类,底层Model及UIKit扩展等,这里又尝试了一下subspec功能,给PodTestLibrary创建了多个子分支。

具体做法是先将源文件添加到Pod/Classes中,然后按照不同的模块对文件目录进行整理,因为我有四个模块,所以在Pod/Classes下有创建了四个子目录,完成之后继续编辑之前的PodTestLibrary.podspec,这次增加了subspec特性

Pod::Spec.new do |s|
  s.name             = "PodTestLibrary"
  s.version          = "1.0.0"
  s.summary          = "Just Testing."
  s.description      = <<-DESC
                       Testing Private Podspec.

                       * Markdown format.
                       * Don't worry about the indent, we strip it!
                       DESC
  s.homepage         = "https://coding.net/u/wtlucky/p/podTestLibrary"
  # s.screenshots     = "www.example.com/screenshots_1", "www.example.com/screenshots_2"
  s.license          = 'MIT'
  s.author           = { "wtlucky" => "wtlucky@foxmail.com" }
  s.source           = { :git => "https://coding.net/wtlucky/podTestLibrary.git", :tag => "1.0.0" }
  # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'

  s.platform     = :ios, '7.0'
  s.requires_arc = true

  #s.source_files = 'Pod/Classes/**/*'
  #s.resource_bundles = {
  #  'PodTestLibrary' => ['Pod/Assets/*.png']
  #}
  #s.public_header_files = 'Pod/Classes/**/*.h'

  s.subspec 'NetWorkEngine' do |networkEngine|
      networkEngine.source_files = 'Pod/Classes/NetworkEngine/**/*'
      networkEngine.public_header_files = 'Pod/Classes/NetworkEngine/**/*.h'
      networkEngine.dependency 'AFNetworking', '~> 2.3'
  end

  s.subspec 'DataModel' do |dataModel|
      dataModel.source_files = 'Pod/Classes/DataModel/**/*'
      dataModel.public_header_files = 'Pod/Classes/DataModel/**/*.h'
  end

  s.subspec 'CommonTools' do |commonTools|
      commonTools.source_files = 'Pod/Classes/CommonTools/**/*'
      commonTools.public_header_files = 'Pod/Classes/CommonTools/**/*.h'
      commonTools.dependency 'OpenUDID', '~> 1.0.0'
  end

  s.subspec 'UIKitAddition' do |ui|
      ui.source_files = 'Pod/Classes/UIKitAddition/**/*'
      ui.public_header_files = 'Pod/Classes/UIKitAddition/**/*.h'
      ui.resource = "Pod/Assets/MLSUIKitResource.bundle"
      ui.dependency 'PodTestLibrary/CommonTools'
  end

  s.frameworks = 'UIKit'
  #s.dependency 'AFNetworking', '~> 2.3'
  #s.dependency 'OpenUDID', '~> 1.0.0'
end

因为我们创建了subspec所以项目整体的依赖dependency,源文件source_files,头文件public_header_files,资源文件resource等都移动到了各自的subspec中,每个subspec之间也可以有相互的依赖关系,比如UIKitAddition就依赖于CommonTools。

编辑完成之后,在测试项目里pod update一下,几个子项目都被加进项目工程了,写代码验证无误之后,就可以将这个工程push到远端仓库,并打上新的tag->1.0.0。

最后再次使用pod lib lint验证编辑好的podsepc文件,没有自身的WARNING或者ERROR之后,就可以再次提交到Spec Repo中了,命令跟之前是一样的

$ pod repo push WTSpecs PodTestLibrary.podspec

之后再次到~/.cocoapods/repos/WTSpecs目录下查看

.
├── LICENSE
├── PodTestLibrary
│   ├── 0.1.0
│   │   └── PodTestLibrary.podspec
│   └── 1.0.0
│       └── PodTestLibrary.podspec
└── README.md

3 directories, 4 files

已经有两个版本了,使用pod search查找得到的结果为

$ pod search PodTestLibrary

-> PodTestLibrary (1.0.0)
   Just Testing.
   pod 'PodTestLibrary', '~> 1.0.0'
   - Homepage: https://coding.net/u/wtlucky/p/podTestLibrary
   - Source:   https://coding.net/wtlucky/podTestLibrary.git
   - Versions: 1.0.0, 0.1.0 [WTSpecs repo]
   - Sub specs:
     - PodTestLibrary/NetWorkEngine (1.0.0)
     - PodTestLibrary/DataModel (1.0.0)
     - PodTestLibrary/CommonTools (1.0.0)
     - PodTestLibrary/UIKitAddition (1.0.0)

完成这些之后,在实际项目中我们就可以选择使用整个组件库或者是组件库的某一个部分了,对应的Podfile中添加的内容为

platform :ios, '7.0'

pod 'PodTestLibrary/NetWorkEngine', '1.0.0'  #使用某一个部分
pod 'PodTestLibrary/UIKitAddition', '1.0.0'

pod 'PodTestLibrary', '1.0.0' #使用整个库
最后介绍一下如何删除一个私有Spec Repo,只需要执行一条命令即可

$ pod repo remove WTSpecs

这样这个Spec Repo就在本地删除了,我们还可以通过

$ pod repo add WTSpecs git@coding.net:wtlucky/WTSpecs.git

再把它给加回来。

如果我们要删除私有Spec Repo下的某一个podspec怎么操作呢,此时无需借助Cocoapods,只需要cd到~/.cocoapods/repos/WTSpecs目录下,删掉库目录

wtlucky@wtluckydeMacBook-Pro:~/.cocoapods/repos/WTSpecs$ rm -Rf PodTestLibrary

然后在将Git的变动push到远端仓库即可

wtlucky@wtluckydeMacBook-Pro:~/.cocoapods/repos/WTSpecs$ git add --all .
wtlucky@wtluckydeMacBook-Pro:~/.cocoapods/repos/WTSpecs$ git ci -m "remove unuseful pods"
wtlucky@wtluckydeMacBook-Pro:~/.cocoapods/repos/WTSpecs$ git push origin master

##参考资料

Private Pods
Creating a Private CocoaPod
Developing Private In-House Libraries with CocoaPods

06/02/2015 08:31 上午 posted in  Cocoapods

使用Cocoapods高版本(0.36.0以上) 来管理XMPPFramework

由于XMPPFramework 在cocoapods 0.36一上会有循环引用的错误,eg:Circular dependency issuse on cocoapods version(0.36.0)

如果不愿意降低cocoapods版本,那么可以参考国外一位开发者提供的一个修改XMPPFramework的podspec文件修复该问题。

  1. 这个修改版的文件是关键,访问如下链接,并且下载该文件。
    podspec.json修改版地址

  2. 找到本机如下路径的文件。使用Finder的go to folder

~/.cocoapods/repos/master/Specs/XMPPFramework/3.6.4/XMPPFramework.podspec.json
  1. 把第一步下载的文件,覆盖到2步的路径中。

  2. 然后执行 pod install 或者 pod update

  3. 到此,循环依赖的错误不会再出现。pod 也顺利工作。但还没有结束。后面是更重要的步骤

  4. build workspace 会发现build 失败。错误是 XMPPFramework.h文件

#ifdef HAVE_XMPP_SUBSPEC_SYSTEMINPUTACTIVITYMONITOR
#import "XMPPSystemInputActivityMonitor.h"
#endif
  1. 这时需要从官方的github上,
    XMPPFramework官网地址

找到如下两个文件

XMPPSystemInputActivityMonitor.h
XMPPSystemInputActivityMonitor.m

并拷贝到工程文件夹中的如下路径当中,如果没有文件夹SystemInputActivityMonitor,需要手动创建SystemInputActivityMonitor
/Pods/XMPPFramework/Extensions/SystemInputActivityMonitor/XMPPSystemInputActivityMonitor.h

完成上述工作之后,重新build workspace,会发现,一切正常.

以上方案虽然不算完美,但也算是兼顾了cocoapods的最新版本。至于第9步和第10步。也可以不做,只是这样的话,需要注释xmppframework.h文件中对应的import。孰轻孰重,只能自己衡量了。

Circular dependency issuse on cocoapods version(0.36.0) 完全解决方案(非降版本)

05/29/2015 09:40 上午 posted in  Cocoapods

常用代码块整理

/* 常用代码块
 
 
1.copy:
@property (nonatomic,copy) NSString *<#string#>;
 
 
2.strong:
@property (nonatomic,strong) <#Class#> *<#object#>;

 
3.weak:
@property (nonatomic,weak) <#Class#> *<#object#>;

 
4.assign:
@property (nonatomic,assign) <#Class#> <#property#>;
 
 
5.delegate:
@property (nonatomic,weak) id<<#protocol#>> <#delegate#>;
 

6.block:
@property (nonatomic,copy) <#Block#> <#block#>;
 
 
7.mark:
#pragma mark <#mark#>

 
8.gmark:
#pragma mark - <#gmark#>
 
 
9.warning:
#warning <#message#>
 


 
10.ReUseCell:
 
 static NSString *rid=<#rid#>;
 
 <#Class#> *cell=[tableView dequeueReusableCellWithIdentifier:rid];
 
 if(cell==nil){
 
 cell=[[<#Class#> alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:rid];
 
 }
 
 return cell;
 
 
11.initObj:
 
 if(self=[super init]){
    <#init#>
 }
 
 return self;
 
12.dataFill:
 -(void)dataFill:(<#ModelClass#> *)<#model#>{
 
    <#code#>

 }
 
13.MainGCD:
 dispatch_async(dispatch_get_main_queue(), ^{
    <#code#>
 });

 
14.GlobalGCD:
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    <#code#>
 });

15.AfterGCD:
 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    <#code to be executed after a specified delay#>
 });
 
16.OnceGCD:
 static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
    <#code to be executed once#>
 });
 
 
17.APPLICATION:
 [UIApplication sharedApplication]

 
18.NOTICENTER:
 [NSNotificationCenter defaultCenter]

 
17.DEFINEIOS6
#define ios6x ([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0f)
 
18.NSLOG:
NSLog(@"<#content#>");
 
 
20.VIEWDIDLOAD
 -(void)viewDidLoad{
 
     [super viewDidLoad];
     
     <#code#>
 }

 
21.VIEWWILLAPPEAR
 -(void)viewWillAppear:(BOOL)animated{
     [super viewWillAppear:animated];
     <#code#>
 }
 
 
22.VIEWDIDAPPEAR
 -(void)viewDidAppear:(BOOL)animated{
 
     [super viewDidAppear:animated];
     
     <#code#>
 }
 
 
23.VIEWWILLDISAPPEAR
 -(void)viewWillDisappear:(BOOL)animated{
 
     [super viewWillDisappear:animated];
     
     <#code#>
 }
 
 
24.VIEWDIDDISAPPEAR
 -(void)viewDidDisappear:(BOOL)animated{
 
     [super viewDidDisappear:animated];
     
     <#code#>
 }
 
 
24.LAYOUTSUBVIEWS
 -(void)layoutSubviews{
 
     [super layoutSubviews];
     
     <#code#>
 }
 
 
25.AWAKEFROMNIB
 -(void)awakeFromNib{
 
     [super awakeFromNib];
     
     <#code#>
 }
 
26.LOADVIEW
 -(void)loadView{
 
    <#code#>
 }
 
27.INITFRAME
 -(instancetype)initWithFrame:(CGRect)frame{
 
     self = [super initWithFrame:frame];
     
     if(self){
     
     <#code#>
 
     }
 
     return self;
 }
 
 
28.INITCODER
 -(id)initWithCoder:(NSCoder *)aDecoder{
 
 self=[super initWithCoder:aDecoder];
 
     if(self){
     
         <#code#>
     
     }
 
 return self;
 }
 
29.weakSelf
 __weak typeof(self) weakSelf=self;

30.DEBUGSCROLLVIEW
 NSLog(@"\n\n scrollView的Debug信息: \n\n frame=%@,\n bounds=%@,\n contentSize=%@,\n contentOffset=%@,\n contentInset=%@.\n",NSStringFromCGRect(scrollView.frame),NSStringFromCGRect(scrollView.bounds),NSStringFromCGSize(scrollView.contentSize),NSStringFromCGPoint(scrollView.contentOffset),NSStringFromUIEdgeInsets(scrollView.contentInset));

31.CONSTRAINTS
 [<#view#> mas_makeConstraints:^(MASConstraintMaker *make) {
 <#make.#>
 }];
 
 
*/
05/28/2015 09:42 上午 posted in  apple Program

iOS Crash文件的解析

iOS Crash文件的解析(一)

Andrew->China: Says Hello
Andrew->China: Says Hello1
Note right of China: China thinks\nabout it
China-->Andrew: How are you?
Andrew->>China: I am good thanks!
Title: Here is a title
A->B: Normal line
B-->C: Dashed line
C->>D: Open arrow
D-->>A: Dashed open arrow
st=>start: 开始
e=>end: 结束
op=>operation: 我的操作
op1=>operation: My Operation 
sub1=>subroutine: My Subroutine
cond=>condition: 确认?

st->op->op1->cond
cond(yes)->e
cond(no)->op
st=>start: 预约|past:>http://www.google.com[blank]
e=>end: 结束:>http://www.google.com
op0=>operation: 选择桌型和时间段|past
op1=>operation: 预约信息提交|past
op2=>operation: Stuff|current
op3=>operation: 登陆|current
sub1=>subroutine: My Subroutine|invalid
cond=>condition: 登陆|approved:>http://www.baidu.com
c3=>condition: 数据合法|rejected
c2=>condition: Good idea|rejected
io=>inputoutput: catch something...|request

st->cond
cond(yes, right)->op0(right)->c3(right)
cond(no)->op3
c2(yes)->io->e
c2(no)->op2->e
c3(yes)->op1
c3(no)->op0(right)
05/27/2015 16:59 下午 posted in  杂七杂八

VVDocumenter文档注释和appledoc生成文档

代码注释可以让代码更容易接受和使用,特别是在大型项目合作开发或模块开发中。Objective-C中也有一些很方便的注释插件和文档生成工具。本文推荐两个开源软件:VVDocumenter注释工具和appledoc文档生成工具。
###下载和安装VVDocumenter

VVDocumenter插件是github上的一个开源项目,地址是https://github.com/onevcat/VVDocumenter-Xcode。
你可以git clone或在github网站上下载工程的zip压缩文件。下载完成后,打开其中的工程,build&run一下就安装成功了。然后记得要重新启动一下Xcode应用,VVDocumenter插件就生效了。试试在方法定义前输入“///”。

VVDocumenter GitHub项目地址

###appledoc
我们开发自己的模块时,经常会将模块接口提供给别人使用,这样我们不得不写一些接口说明文档。很多语言都提供了这种工具比如javadoc,doxygen等,可以将注释代码转化成在线文档。这里推荐一个特别适合Objective-C语言的一种文档输出工具appledoc。它可以把一些格式的注释转换成Apple SDK风格的文档,同时它支持VVDocumenter的输出注释格式。

appledoc GitHub项目地址

1.安装

git clone git://github.com/tomaz/appledoc.git
cd ./appledoc
sudo sh install-appledoc.sh

安装完成后,验证一下OK了没

appledoc --version

2.使用
进入code所在目录,跑一下下面的命令,默认会编译出docset并安装进Xcode。

appledoc --project-name MyProject --project-company ibireme ./

其中 MyProject是生成的Docset文件名,执行上面的命令之后会在工程目录底下生成一个名字为docset-installed的txt文档。

docset-installed.txt:

Documentation set was installed to Xcode!

Path: /Users/liunian/Library/Developer/Shared/Documentation/DocSets/com.ibireme.lnuniversal.LNUniversal.docset
Time: 2015-05-26 10:01:00 +0000

如果想要详细的参数,可以查看帮助

appledoc --help

###如果想要集成进Xcode工程:
1.选中你的工程,点击Add Target按钮,选择 Other -> Aggregate模板新建.
2.点击Add Build Phase按钮,添加一个Run Script.
3.把下面的模板代码复制进去,把前几行参数改成你自己的.
4.在Xcode左上角选择这个新建的Target,然后点击build.
5.文档就会编译好并且自动安装进Xcode了(重启Xcode生效).

#appledoc Xcode script  
# Start constants  
company="ACME";  
companyID="com.ACME";
companyURL="http://ACME.com";
target="iphoneos";
#target="macosx";
outputPath="~/help";
# End constants
 
/usr/local/bin/appledoc \
--project-name "${PROJECT_NAME}" \
--project-company "${company}" \
--company-id "${companyID}" \
--docset-atom-filename "${company}.atom" \
--docset-feed-url "${companyURL}/${company}/%DOCSETATOMFILENAME" \
--docset-package-url "${companyURL}/${company}/%DOCSETPACKAGEFILENAME" \
--docset-fallback-url "${companyURL}/${company}" \
--output "${outputPath}" \
--publish-docset \
--docset-platform-family "${target}" \
--logformat xcode \
--keep-intermediate-files \
--no-repeat-first-par \
--no-warn-invalid-crossref \
--exit-threshold 2 \
"${PROJECT_DIR}"

PS:集成到Xcode我一直没走通,估计是代码没有设置好的原因,回头可以好好研究下

###其他
编译出的Docset默认会放在/Users/ibireme/Library/Developer/Shared/Documentation/DocSets路径下。

Docset格式,实际上是一个bundle,里面包含了一些xml和html。显示包内容后就可以查看和修改了。如果需要放到网站上,那单独将html部分取出来就行。

Docset安装后,在Xcode中就可以实时查看某个方法的说明了,体验和官方文档保持一致。(有一点,category中的注释不会出现在xcode的快速帮助中,不知道新版xcode是否会有改进..)

05/26/2015 18:04 下午 posted in  apple

解决在iOS App中集成支付宝时遇到的RSA密钥签名问题

最近在自己的iOS native App中集成支付宝功能,发现有即时到帐、手机网页、快捷支付,之间的区别说的不够清楚,接口及参数定义更是各不相同。对于Native App来说,选择快捷支付(wap)就对了。

说起集成文档及Demo,发现文档质量比起五年前没啥进步,一如既往的不知所云,甚至缺少某些关键步骤。而且Demo中的代码一直强调此代码不完全能直接使用,可是我想说,开发者看demo的意义就在于能够快速的解决问题,最好能一键集成,SDK的集成度可以做得更好。可是无论文档和代码都缺少版本控制,二者之间存在大量不一致现象,更不用说出错后的信息让人难以跟踪,跟当年Windows的出错信息有得一拼。

下面说说集成快捷支付中遇到的一些关键环节,特别是RSA加密的问题,因为快捷支付只支持RSA,不支持MD5方式。

  1. PID: 申请快捷支付服务后,会在商家服务网页上看到pid等信息
  2. RSA加密: 然后用openssl生成商户的公钥和私钥。私钥用于提交支付申请时加密,而公钥要在商家服务页面上提交,换取到支付宝的公钥。
  3. 提交公钥的时候,”合作伙伴密钥”和”无线产品密钥管理”都要提交,内容一样。公钥字符串不要包括-----Begin Public Key...字样,去掉空格换行后组装成一条字符串。这一步如果出错,就会在支付跳转时出现”ALI64错误”,文档中丝毫没提!
  4. 对于iOS来说,私钥要转为PKCS8格式,即下述代码的第三行,而Android的话则不需要第三行!在代码中使用时,同样要,去掉空格换行后组装成一条字符串
openssl genrsa -out rsa_private_key.pem 1024
openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt

  1. 链接库:按照文档所述,导入alipay.framework和alipay.bundle。此外,demo代码中的Order.m、\openssl、Util文件和目录都要导入,并在工程的build settings中修改Search Path,消除找不到头文件的编译错误。
  2. App之间跳转设置。按照在项目Info.plist中设置URL Types->URL Schemes,内容自定,比如”alisdkdemo”,后续提交订单时要带这个参数,以便能从支付宝跳回你的App.
  3. 对于快捷支付的接口,在进行参数字符串拼接时,每个参数都形为title="abc"&sign_type="RSA",注意到双引号了吗?这和PC端或手机网页接口都是不同的。
let order = Order()
order.partner = "<你的PID>"
order.seller = "<你的注册邮箱>"
order.tradeNO = "201412061500"
order.productName = "巧克力"
order.productDescription = "夹心"
order.amount = "0.01"
order.notifyURL = "http://127.0.0.1:8000/xxx"
order.service = "mobile.securitypay.pay"
order.paymentType = "1"
order.inputCharset = "utf-8"
order.itBPay = "30m"
order.showUrl = "m.alipay.com"

let privateKey = "<对于iOS App,要把生成的私钥转为PCKS8格式,比原始私钥字符串更长一些。字符串不要包括`-----Begin Public Key...`字样,去掉空格换行后组装成一条字符串。>";

let orderSpec = order.description
let signer = RSADataSigner(privateKey: privateKey)
let signedString = signer.signString(orderSpec)
let orderString = "\(orderSpec)&sign=\"\(signedString)\"&sign_type=\"RSA\""
println(orderString)
AlipaySDK.defaultService().payOrder(orderString, fromScheme: "alisdkdemo", callback: {resultDic in
    println("Alipay result = \(resultDic as Dictionary)")
})

转载:解决在iOS App中集成支付宝时遇到的RSA密钥签名问题

05/25/2015 16:22 下午 posted in  apple

Coredata开发之MagicalRecord

  1. 线程的创建
    先看两则代码:
    代码1:
    for (NSString *feedID in self.feedsToRemove) {
        [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
            [Feed MR_deleteAllMatchingPredicate:[NSPredicate predicateWithFormat:@“uid = %@“,feedID]];
        } completion:^(BOOL contextDidSave, NSError *error) {
            
        }];
    }

代码2:

    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
        for (NSString *feedID in self.feedsToRemove) {
            [Feed MR_deleteAllMatchingPredicate:[NSPredicate predicateWithFormat:@“uid = %@“,feedID]];
        }
    } completion:^(BOOL contextDidSave, NSError *error) {
        
    }];

代码1是错误的,因为会产生严重的线程问题,导致CPU居高不下,UI僵死等,
为什么呢?我们可以这样理解:MagicalRecord 的这个saveWithBlock方法,其实是创建一个子线程来处理数据事务,于是 当self.feedsToRemove有N个对象 那么就会产生N个线程,当然 对于一个设备来说线程总有一个最高数限制,直到把所有线程用光,于是乎整个世界就全被线程塞满了,卡死。。。

所以我们采用代码2中的写法,将枚举写如子线程中。

PS:这样的问题其实就是错误使用线程创建导致,还是自己马虎啊。。切记。

05/12/2015 22:50 下午 posted in  apple

SVN常用知识简介

trunk
tags
branches

##常用SVN 命令:

名词说明:
WC:Working Copy 你的工作区
Versioned:受控的;受版本控制的

  1. svn checkout URL[@REV]... [PATH]
    别名:co
    描述:检出
    访问库:否
    eg:svn checkout file:///var/svn/repos/test  file:///var/svn/repos/quiz working-copies
    eg:svn checkout -r 2 file:///var/svn/repos/test mine  ##check out 版本号为2的项目

  2. svn import [PATH] URL
    别名:无
    描述:导入本地一个目录到库中。但是导入后,本地的目录并不会处于受控状态。
    访问库:是。
    eg:svn import myproj
    http://svn.myProject.com/repos/trunk/misc -m “New import”

  3. svn export [-r REV] URL[@PEGREV] [PATH]
      svn export [-r REV] PATH1[@PEGREV] [PATH2]
    别名:无
    描述:导出一个干净的目录树,不包含所有的受控信息。可以选择从URL或WC中导出。
    访问库:如果访问的是URL则会。
    eg:svn export file:///var/svn/repos my-export   ##导出到my-export目录。

  4. svn status [PATH...]
    别名:stat, st
    描述:输出WC中文件和目录的状态。如果WC提交,这些状态就会同步到库里。
    一般状态有         ‘ ‘  没有修改
    ‘A’  新增
    ‘D’  删除
    ‘M’  修改
    ‘R’  替代
    ‘C’  冲突
    ‘I’  忽略
    ‘?’  未受控
    ‘!’  丢失,一般是将受控文件直接删除导致
    访问库:加上--show-updates参数时会 (查看WC与Versioned的状态对比)
    eg:svn status wc

  5. svn add [path]
    别名:无
    描述:添加文件或目录到你的wc,打上新增标记。这些文件会在下一次你提交wc的时候提交到svn服务器。
    在提交前,你也可以用svn revert撤销新增的文件。
    访问库:否
    eg: svn add file.cpp

svn add xxx@2x.png 文件时, 正常命令 svn add xxx@2x.png 会报 xxx not found
需用 svn add xxx@2x.png@  来添加,也就是图片名字后面再添加一个@ 符号,
这是因为 svn 命令最后需要用@符号来指定一个版本导致的
遇到 xxx@2x.png文件时,如果用svn命令行添加到 版本库的话,只能手动一个一个添加,不能批量添加

  1. svn commit [PATH...]
    别名:ci
    描述:把你WC的更改提交到仓库
    访问库:是
    eg:svn commit -m “added howto section.” ##默认情况下提交必须提供log message 

  2. svn revert PATH...
    别名:无
    描述:还原WC中所有的本地更改。
    访问库:否
    eg:svn revert --depth=infinity .   ##将整个目录所有文件还原

  3. svn update [PATH...]
    别名:up
    描述:更新WC,更新反馈有如下几种分类。
    A  新增
    B  锁破坏
    D  删除
    U  更新
    C  冲突
    G  合并
    E  存在的
    访问库:是
    eg:svn up -r22   ##更新到一个指定版本

  4. svn list [TARGET[@REV]...]
    别名:ls
    描述:显示目标下的文件和目录列表。
    访问库:如果访问的是库地址就会。
    eg:svn list --verbose file:///var/svn/repos   ##--verbose参数表示显示详细信息。

  5. svn delete PATH...
    别名:del,remove,rm
    描述:删除
    访问库:如果PATH是库地址时会,删除WC内的文件不会。
    eg:svn del localfile.cpp    ##删除WC里的文件,在下一次提交WC的时候才会真正在仓库里将对应文件删除。
    eg: svn del file:///var/svn/repos/test/yourfile  ##删除仓库里的文件

  6. svn cleanup [PATH...]
    别名:无
    描述:递归的清理WC中过期的锁和未完成的操作。
    访问库:否
    eg:svn cleanup

  7. svn diff
    别名:di
    描述:用来比较并显示修改点。
    访问库:
    eg:svn diff   ##最常用的方式,用来显示WC基于最近一次更新以后的所有的本地修改点。
    eg:svn diff -r 301 bin ## 比较WC和版本301中的bin目录的修改点
    eg:svn diff -r 3000:3500 file:///var/svn/repos/myProject/trunk   ##比较库里主干3000版和3500版的差异。
    eg:svn diff --summarize --xml http://svn.red-bean.com/repos/test@r2 http://svn.red-bean.com/repos/test  ##--summarize --xml 参数将差异情况以xml文档的方式显示出来。

eg: svn diff -r 1450:1457 ./JoggersV2/JoggersV2/AppDelegate.m

  1. svn log [PATH]
    svn log URL[@REV] [PATH...]
    别名:无
    描述:从库中显示log消息。log消息代码 A :added  D:deleted  M:modified  R:replaced
    访问库:是
    eg:svn log -v http://svn.myProject.com/repos/test/ foo.c bar.c   ##详细显示指定URL的库中foo.c和bar.c所有版本的log信息。
    eg:svn log -r 14:15    ##显示当前WC的14和15版本log信息。
    eg:##如果版本号不连续,只能采用如下方式。
    $ svn log -r 14 > mylog
    $ svn log -r 19 >> mylog
    $ svn log -r 27 >> mylog

  2. svn resolve PATH...
    别名:无
    描述:将冲突的文件标记为已解决,并且删掉冲突产生的临时文件。注意这个命令并不是能把冲突解决,解决冲突还是得靠人工。
    访问库:否
    eg:svn resolve --accept mine-full foo.c   ##1.5版本后,加上--accept参数,尝试自动处理冲突。

2)export
描述:创建一个无版本记录的拷贝
简写:ex

示例:
打开终端,cd 进入到要保存工程的目录(此处一个文件名为project)
cd /Users/hupu/Desktop/project

svn export 项目SVN地址

创建一个无版本记录的拷贝.
svn export –r rev URL path
从项目仓库的指定URL导出一个干净的目录树到path中,如果指定了rev参数,导出rev版本的,否则到处最新版本。
3)import
描述:提交一个无版本的文件或者树到项目仓库
svn import path URL

svn commit -m ‘refs# modify’

打开终端,cd 进入到需要上传的.a文件所在的文件夹。 确保 ls能看到.a文件
hupudeiMac:Support hupu$ cd /Users/hupu/Desktop/HP_Project/Joggers/tags/JoggersV2_1.6.0/Support/MTA
hupudeiMac:MTA hupu$ svn add *.a
A (bin) libmtasdk.a

提交命令:
hupudeiMac:tags hupu$ svn ci -m ‘refs# lib submited’

查看本地和远程修改文件的状态命令:svn st --show-updates

常用命令

打开终端,进到所在的目录,然后出入一下代码
find . -name “.svn” | xargs rm -Rf

  1. 将文件checkout到本地目录
svn checkout path(path是服务器上的目录)
例如:svn checkout svn://192.168.1.1/pro/domain
简写:svn co
  2. 往版本库中添加新的文件
svn add file
例如:svn add test.php(添加test.php)
svn add *.php(添加当前目录下所有的php文件)
    svn add xxx@2x.png 文件时, 正常命令 svn add xxx@2x.png 会报 xxx not found
    需用 svn add xxx@2x.png@  来添加,也就是图片名字后面再添加一个@ 符号,
    这是因为 svn 命令最后需要用@符号来指定一个版本导致的
    遇到 xxx@2x.png文件时,如果用svn命令行添加到 版本库的话,只能手动一个一个添加,不能批量添加
  3. 将改动的文件提交到版本库
svn commit -m “LogMessage“ [-N] [--no-unlock] PATH(如果选择了保持锁,就使用–no-unlock开关)
例如:svn commit -m “add test file for my test“ test.php
简写:svn ci
  4. 加锁/解锁
svn lock -m “LockMessage“ [--force] PATH
例如:svn lock -m “lock test file“ test.php
svn unlock PATH
  5. 更新到某个版本
svn update -r m path
例如:
svn update如果后面没有目录,默认将当前目录以及子目录下的所有文件都更新到最新版本。
svn update -r 200 test.php(将版本库中的文件test.php还原到版本200)
svn update test.php(更新,于版本库同步。如果在提交的时候提示过期的话,是因为冲突,需要先update,修改文件,然后清除svn resolved,最后再提交commit)
简写:svn up
  6. 查看文件或者目录状态
1)svn status path(目录下的文件和子目录的状态,正常状态不显示)
【?:不在svn的控制中;M:内容被修改;C:发生冲突;A:预定加入到版本库;K:被锁定】
2)svn status -v path(显示文件和子目录状态)
第一列保持相同,第二列显示工作版本号,第三和第四列显示最后一次修改的版本号和修改人。
注:svn status、svn diff和 svn revert这三条命令在没有网络的情况下也可以执行的,原因是svn在本地的.svn中保留了本地版本的原始拷贝。
简写:svn st
  7. 删除文件
svn delete path -m “delete test fle“
例如:svn delete svn://192.168.1.1/pro/domain/test.php -m “delete test file”
或者直接svn delete test.php 然后再svn ci -m ‘delete test file‘,推荐使用这种
简写:svn (del, remove, rm)
  8. 查看日志
svn log path
例如:svn log test.php 显示这个文件的所有修改记录,及其版本号的变化
  9. 查看文件详细信息
svn info path
例如:svn info test.php
  10. 比较差异
svn diff path(将修改的文件与基础版本比较)
例如:svn diff test.php
svn diff -r m:n path(对版本m和版本n比较差异)
例如:svn diff -r 200:201 test.php
简写:svn di
  11. 将两个版本之间的差异合并到当前文件
svn merge -r m:n path
例如:svn merge -r 200:205 test.php(将版本200与205之间的差异合并到当前文件,但是一般都会产生冲突,需要处理一下)
  12. SVN 帮助
svn help
svn help ci
  13. 版本库下的文件和目录列表
svn list path
显示path目录下的所有属于版本库的文件和目录
简写:svn ls
  14. 创建纳入版本控制下的新目录
svn mkdir: 创建纳入版本控制下的新目录。
用法: 1、mkdir PATH…
###2、mkdir URL…
创建版本控制的目录。
1、每一个以工作副本 PATH 指定的目录,都会创建在本地端,并且加入新增
调度,以待下一次的提交。
2、每个以URL指定的目录,都会透过立即提交于仓库中创建。
在这两个情况下,所有的中间目录都必须事先存在。
    15、恢复本地修改
svn revert: 恢复原始未改变的工作副本文件 (恢复大部份的本地修改)。revert:
用法: revert PATH…
注意: 本子命令不会存取网络,并且会解除冲突的状况。但是它不会恢复
被删除的目录
    16、代码库URL变更
svn switch (sw): 更新工作副本至不同的URL。
用法: 1、switch URL [PATH]
2、switch –relocate FROM TO [PATH...]
1、更新你的工作副本,映射到一个新的URL,其行为跟“svn update”很像,也会将
服务器上文件与本地文件合并。这是将工作副本对应到同一仓库中某个分支或者标记的
方法。
2、改写工作副本的URL元数据,以反映单纯的URL上的改变。当仓库的根URL变动
(比如方案名或是主机名称变动),但是工作副本仍旧对映到同一仓库的同一目录时使用
这个命令更新工作副本与仓库的对应关系。
    17、解决冲突
svn resolved: 移除工作副本的目录或文件的“冲突”状态。
用法: resolved PATH…
注意: 本子命令不会依语法来解决冲突或是移除冲突标记;它只是移除冲突的
相关文件,然后让 PATH 可以再次提交。
    18、输出指定文件或URL的内容。
svn cat 目标[@版本]…如果指定了版本,将从指定的版本开始查找。
svn cat -r PREV filename > filename (PREV 是上一版本,也可以写具体版本号,这样输出结果是可以提交的)
    19、配置忽略文件 vi ~/.subversion/config
    找到 global-ignores 一行,去掉注释,编辑成
global-ignores = build *~.nib *.so *.pbxuser *.mode *.perspective*
找到 enable-auto-props = yes 把注释去掉,在[auto-props] Section声明以下文本文件
*.mode* = svn:mime-type=text/X-xcode
*.pbxuser = svn:mime-type=text/X-xcode
*.perspective* = svn:mime-type=text/X-xcode
*.pbxproj = svn:mime-type=text/X-xcode

=========================================

##svn 出错信息总汇

-svn 出错信息总汇 . Subversion 错误信息一览表 注意: 不同的客户端(命令行,TortoiseSVN, AnkhSVN, Subclipse等)的出错信息可能稍有不同。 下面表格中的出错信息以 http://svn.moon.ossxp.com/svn/test 版本库做示例,仅供参考。 编号 出错信息 问题剖析 解决方案 1. svn: Server sent unexpected return value (500 I-
svn 出错信息总汇 . Subversion 错误信息一览表
注意:

不同的客户端(命令行,TortoiseSVN, AnkhSVN, Subclipse等)的出错信息可能稍有不同。
下面表格中的出错信息以 http://svn.moon.ossxp.com/svn/test 版本库做示例,仅供参考。
###编号出错信息,问题剖析解决方案
 
1.
svn: Server sent unexpected return value (500 Internal Server Error) in response to OPTIONS request for ‘http://svn.moon.ossxp.com/svn/test'

错误的用户名
检查登录的用户名是否输入错误

svn: 服务器发送了意外的返回值(500 Internal Server Error),在响应 “OPTIONS” 的请求 “http://svn.moon.ossxp.com/svn/test” 中

2.
svn: OPTIONS of ‘http://svn.moon.ossxp.com/svn/test': authorization failed: Could not authenticate to server: rejected Basic challenge (http://svn.moon.ossxp.com)

错误的口令
用正确的用户名/口令登录

svn: 方法 OPTIONS 失败于 “http://svn.moon.ossxp.com/svn/test”: 认证失败: Could not authenticate to server: rejected Basic challenge (http://svn.moon.ossxp.com)

3.
svn: Server sent unexpected return value (403 Forbidden) in response to OPTIONS request for ‘http://svn.moon.ossxp.com/svn/test'

用户无权限
联系管理员,为用户分配权限 
svn: 服务器发送了意外的返回值(403 Forbidden),在响应 “OPTIONS” 的请求 “http://svn.moon.ossxp.com/svn/test” 中
 
4.
svn: OPTIONS of ‘http://www.moon.ossxp.com/svn/test': 200 OK (http://www.moon.ossxp.com)

服务器地址错误,是普通Web页面,不支持SVN的 WebDAV 协议

确认输入正确的 SVN 服务地址。可以在浏览器中输入该地址进行确认
 
svn: 方法 OPTIONS 失败于 “http://www.moon.ossxp.com/svn/test”: 200 OK (http://www.moon.ossxp.com)
 
5.
The version of your subversion (client) is below 1.5.0, upgrade to 1.5.0 or above. SVN below 1.5.0 can not handle mergeinfo properly. It can mess up our automated merge tracking!

是由于客户端的软件版本低于1.5.0造成的。服务器端对客户端软件版本进行了限制,以免对合并跟踪破坏。

升级本地的Subversion客户端软件到1.5.0或以上版本。
 
6.
svn: This client is too old to work with working copy ‘.’. You need to get a newer Subversion client, or to downgrade this working copy. See http://subversion.tigris.org/faq.html#working-copy-format-change for details.

安装了多个版本的SVN客户端(TSVN,Subclipse,...),且各个客户端的版本不一致。高版本的SVN客户端会自动更新本地工作目录中的 .svn 目录下的文件格式,导致旧版本的SVN客户端不能继续访问该本地工作目录

将本机安装的所有的SVN客户端都更新到同一个大版本,以避免本地工作目录的格式不一致
-svn: 此客户端对于工作副本 . 太旧。你需要取得更新的 Subversion 客户端,或者降级工作副本。 参见 http://subversion.tigris.org/faq.html#working-copy-format-change 以获得更详细的信息。 7. svn: Working cop-

svn: 此客户端对于工作副本 “.” 太旧。你需要取得更新的 Subversion 客户端,或者降级工作副本。 参见 http://subversion.tigris.org/faq.html#working-copy-format-change 以获得更详细的信息。

7.
svn: Working copy ‘trunk/src’ locked svn: run ‘svn cleanup’ to remove locks (type ‘svn help cleanup’ for details)

异常操作导致目录没有解锁。
一个简单的重现方法:在 .svn 目录下创建空的名为 lock 的文件

使用命令行 “svn cleanup” 或者类似的“清理”动作删除锁定

svn: 工作副本“trunk/src”已经锁定 svn: 运行“svn cleanup”删除锁定 (输入“svn help cleanup”得到用法)
 
8.
日志中没有作者信息: ------------------------------------ r9 | (没有作者信息) | … ossxp.com anonymous commit test
匿名提交导致没有作者信息
检查版本库权限控制,禁止匿名提交
 
9.
正在发送 ... 传输文件数据.svn: 提交失败(细节如下): svn: Commit blocked by pre-commit hook (exit code 1) with output: 提交说明至少应包含 4 个字符, 或者太简单了。

这是由于用户提交的提交说明(commit log),太过简单了。在提交时需要输入有意义的 commit log。

写有意义的提交说明,或者请求管理员更改版本库插件
10.
增加 Logger.c 传输文件数据.svn: 提交失败(细节如下): svn: Commit blocked by pre-commit hook (exit code 1) with output: Wide character in print at /opt/svn/svnroot/myrepos/hooks/scripts/check-case-insensitive.pl line 259. 发现文件名大小写冲突: trunk/src/Logger.c 已经存在于 logger.c

管理员设置了对新增文件是否重名(只有大小写不同)的文件进行检查。文件名只有大小写不同,在Windows上进行检出会造成麻烦

不要添加重名(仅大小写不同)文件

增加 src/文件aBc.txt 传输文件数据.svn: 提交失败(细节如下): svn: Commit blocked by pre-commit hook (exit code 1) with output: Clash: ‘/trunk/src/文件aBc.txt’ ‘/trunk/src/文件abc.txt’

11.
svn: While preparing ‘/home/jiangxin/tmp/svn.test/trunk/src/README.txt’ for commit svn: Inconsistent line ending style

提交的文件已经设置了 svn:eol-style 属性,但是该文本内的换行符有DOS的换行符CRLF,也有Unix换行符LF,不一致!

统一该文本文件内的换行符。Linux 下可以用dos2unix, unix2dos, sed等命令。Windows下可用 UltraEdit 进行转换。
 
svn: 当为提交操作准备“/home/jiangxin/tmp/svn.test/trunk/src/README.txt”时 svn: 不一致的行结束样式
12.
svn: Failed to add file ‘Makefile’: an unversioned file of the same name already exists

执行更新(svn up)时报错。因为其他人新增一个文件到服务器,而本地却存在一个同名文件(未版本控制)

先将本地重名文件改名,再执行 “svn up”,之后再比较、合并文件。或者执行 “svn up --force”
-svn: 增加文件 ‘Makefile’ 失败: 同名未版本控制的文件已存在 13. Adding src/Makefile svn: Commit failed (details follow): svn: File ‘/svn/test/trunk/src/Makefile’ already exists 添加新文件,提交时报错。-
svn: 增加文件 ‘Makefile’ 失败: 同名未版本控制的文件已存在
 
13.
Adding src/Makefile svn: Commit failed (details follow): svn: File ‘/svn/test/trunk/src/Makefile’ already exists

添加新文件,提交时报错。因为其他人已经先于我增加了该文件。

先执行更新操作(”svn up”),再根据提示进行操作:合并/提交...

增加 src/Makefile svn: 提交失败(细节如下): svn: 文件“/svn/test/trunk/src/Makefile”已存在
 
14.
$ svn up Conflict discovered in ‘Makefile’. Select: (p) postpone, (df) diff-full, (e) edit, (mc) mine-conflict, (tc) theirs-conflict, (s) show all options: p C Makefile Updated to revision 5. Summary of conflicts: Text conflicts: 1
多人同时编辑同一个文件时,可能会遇到冲突。别人先于我提交,则当我提交时要先更新。更新可能遇到不能自动解决的冲突

使用工具进行冲突解决


$ svn up 在 “Makefile” 中发现冲突。 选择: (p) 推迟,(df) 显示全部差异,(e) 编辑, (mc) 我的版本, (tc) 他人的版本, (s) 显示全部选项: p C Makefile 更新到版本 5。 冲突概要: 正文冲突:1
 
15.
svn: Commit failed (details follow): svn: File ‘Makefile’ is out of date svn: File not found: transaction ‘6-d’, path ‘/trunk/src/Makefile’

提交的文件已被他人删除

先执行更新操作(”svn up”),再根据提示解决该树冲突:删除文件或继续添加...

svn: 提交失败(细节如下): svn: 文件 “Makefile” 已经过时 svn: File not found: transaction ‘6-c’, path ‘/trunk/src/Makefile’

16.
svn: Commit failed (details follow): svn: File or directory ‘/trunk/XXX’ is out of date; try updating svn: resource out of date; try updating

基于旧版本修改是不允许的
先更新(”svn update”),再提交 
svn: 提交失败(细节如下): svn: 文件或目录 “/trunk/XXX” 已经过时;请先更新 svn: resource out of date; try updating

17.
svn: DAV request failed; it’s possible that the repository’s pre-revprop-change hook either failed or is non-existent svn: At least one property change failed; repository is unchanged svn: Error setting property ‘log’: Repository has not been enabled to accept revision propchanges; ask the administrator to create a pre-revprop-change hook

修改提交说明等操作属于高风险操作,因为该操作没有被版本控制,属于不可恢复的操作。缺省禁止。

请联系管理员,启用该版本的相关钩子,允许修改“版本属性”。参见 管理员钩子设置


svn: DAV 请求失败;可能是版本库的 pre-revprop-change 钩子执行失败或者不存在 svn: 至少有一个属性变更失败;版本库未改变 svn: 设置属性 “log” 出错: Repository has not been enabled to accept revision propchanges; ask the administrator to create a pre-revprop-change hook

05/08/2015 08:39 上午 posted in  杂七杂八

NSPredicate是什么

##NSPredicate:谓词
字面翻译是这个意思,但是我觉得谓词这个词太难以理解了
NSPredicate的具体用途应该还是过滤,类似于过滤条件之类的,相当于一个主语的谓语,所以说会是谓词这个名字。(我是这么理解的)
##NSPredicate的创建
我们看到创建谓词使用类方法predicateWithFormat: (NSString*) format,format 里的东西真的
和SQL 的where 条件差不多。
另外,参数format 与NSLog 的格式化模版差不多,如果1 和
188.0 是传递过来的参数,你可以写成如下的形式:
@“pid>%d and height<%f”,1,188.0

##NSPredicate中主要的几种运算方式

  1. 比较运算符 > 、< 、== 、 >= 、<= 、 !=
    例:@“number >= 99”

  2. 逻辑运算符:AND、OR、NOT 这几个运算符计算并、或、非的结果。

  3. 范围运算符:IN 、BETWEEN
    例:@“number BETWEEN {1,5}”
    @“address IN {‘shanghai’,’nanjing’}”

  4. 字符串本身:SELF
    例:@“SELF == ‘APPLE’”

  5. 字符串相关:BEGINSWITH、ENDSWITH、CONTAINS
    例: @“name CONTAIN[cd] ‘ang’” //包含某个字符串
    @“name BEGINSWITH[c] ‘sh’” //以某个字符串开头
    @“name ENDSWITH[d] ‘ang’” //以某个字符串结束
    注:[c]不区分大小写 , [d]不区分发音符号即没有重音符号 , [cd]既不区分大小写,也不区分发音符号。

  6. 通配符:LIKE
    例:@“name LIKE[cd] ‘er’” //代表通配符,Like也接受[cd].
    @“name LIKE[cd] ‘???er
    ’”

  7. 正则表达式:MATCHES
    例:NSString *regex = @“^A.+e$”; //以A开头,e结尾
    @“name MATCHES %@“,regex

##如何使用NSPredicate对一个自定义实体进行筛选
NSPredicate我暂时都是用于过滤的,否则我们过滤就需要自己写判断 写循环,去判断输出了。
接下来就上代码和例子吧。

建立一个实体类 Dog

@property (copy   , nonatomic) NSString* name; 
@property (assign , nonatomic) int       age ;

这样我们就有一个类了
我们创建 5只Dog的实体


Dog * dog1=[Dog news];dog1.name=@“这是第一只Dog”;dog1.age=@“11”;
Dog * dog2=[Dog news];dog2.name=@“这是第二只Dog”;dog2.age=@“22”;
Dog * dog3=[Dog news];dog3.name=@“这是第三只Dog”;dog3.age=@“33”;
Dog * dog4=[Dog news];dog4.name=@“这是第四只Dog”;dog4.age=@“44”;
Dog * dog5=[Dog news];dog5.name=@“这是第五只Dog”;dog5.age=@“55”;

不要吐槽我的Dogs的年龄 → 。→
然后我们需要一个Array把他们都装进去

NSArray * dogArr=@[dog1,dog2,dog3,dog4,dog5];
酱紫所有的Dog都装到一个数组里了

使用NSPredicate的筛选
没有使用NSPredicate的筛选应该大家都会写,循环+判断。。。
使用NSPredicate的筛选
具体使用的代码
//创建谓词的对象 谓词条件是基于KVC的,所以需要说明一点,加入Dog里面有个属性是Doginfo指向Doginfo类,在使用的时候 我们的条件就要写成Doginfo.xxx了。

NSPredicate* predicate = [NSPredicate predicateWithFormat:@“name==‘这是第一只Dog’ AND age ==11”];
for(Dog * d in dogArr)
{
    if([predicate evaluateWithObject:d])
    {
        NSLog(d.name);
    }
}

以上的代码打印出来的结果就是 这是第一只Dog

NSPredicate筛选一个对象逻辑还是很清晰的,不过这里比较麻烦的就是 还有一个循环,这里我们会发现数组提供了一个方法可以更方便的检索数组对象
直接筛选出一个符合谓词的新数组。

NSPredicate *pre = [NSPredicate predicateWithFormat:@“age >11”];
NSMutableArray *arrayPre=[dogArr filteredArrayUsingPredicate: pre];

arrayPre里面的对象就会是 除了dog1之外的所有的dog。(至于为什么看我的条件...)

看到这里发现NSPredicate 可以用于自定义的实体的检索、筛选,那如果用于 NSString,NSInteger,bool呢?那我们再看一段代码吧

NSArray *arrays=[NSArray arrayWithObjects: @“Apple”, @“Google”, @“MircoSoft”, nil];
NSPredicate *pre2 = [NSPredicate predicateWithFormat:@“SELF==‘Apple’”];

上面这段代码是匹配字符串的时候 NSPredicate 需要如何声明

##使用NSPredicate在两个数组之间进行差异筛选

NSArray* array = @[@“aa”,@“bb”];
NSArray* array2 = @[@“aa”,@“bb”,@“cc”,@“dd”];
NSPredicate* thePredicate = [NSPredicate predicateWithFormat:@“NOT(SELF in %@)”,array];
NSArray* arr3 = [array2 filteredArrayUsingPredicate:thePredicate];
NSLog(@“%@“,arr3);

上面的代码输出结果 arr3={@“cc” ,@“dd”}
这样大家就很容易理解了
SELF在前面有介绍过 可以往回翻阅一下

如何使用正则筛选一个数组

NSString *regex = @“^A.+e$”;//以A 开头,以e 结尾的字符。
NSPredicate *pre= [NSPredicate predicateWithFormat:@“SELF MATCHES %@“, regex];
if([pre evaluateWithObject: @“Apple”]){
    printf(“YES\n”);
}else{
    printf(“NO\n”);
}

##关于NSPredicate的其他说明和注意事项,以及技巧

  1. 动态属性名
  2. 假如你的代码如下
NSPredicate *p = [NSPredicate predicateWithFormat:@“name = %@“, @“name1”]; 

显然代码没有任何问题,但是这个不是最好的写法我建议如下写法:

NSPredicate *preTemplate = [NSPredicate predicateWithFormat:@“name==$NAME”];
NSDictionary *dic=[NSDictionary dictionaryWithObjectsAndKeys:
@“name1”, @“NAME”,nil];
NSPredicate *pre=[preTemplate predicateWithSubstitutionVariables: dic];

这样看上去可能会让代码逻辑更清晰。

当过滤条件字段都是动态的时候

NSString *key = @“name”;     
NSString *value = @“name1”;      
NSPredicate *p = [NSPredicate predicateWithFormat:@“%@ = %@“, key, value];

然后当你执行到第三行的时候代码就会报错!
逻辑上没错误啊!!!为什么会出错呢?
NSPredicate要自动添加引号,所以最后得到的格式应该是@“’name’ = ‘name1’”。明显不对。要做的就是:

NSPredicate *p = [NSPredicate predicateWithFormat:@“%K = %@“, key, value];

##总结
NSPredicate 的简单的使用就介绍到这里,这里有些都是从网络上其他人的额博客转过来的内容,我自己加以修改和一些自己的理解。希望可以帮到大家。
转载:NSPredicate谓词的用法 数组过滤

贴一段实用代码

Cocoa用NSPredicate描述查询的方式,原理类似于在数据库中进行查询
计算谓词:
//基本的查询
NSPredicate *predicate;
predicate = [NSPredicate predicateWithFormat: @“name == ‘Herbie’”];
    BOOL match = [predicate evaluateWithObject: car];
    NSLog (@“%s”, (match) ? “YES” : “NO”);

//在整个cars里面循环比较
    predicate = [NSPredicate predicateWithFormat: @“engine.horsepower > 150”];
    NSArray *cars = [garage cars];
    for (Car *car in [garage cars]) {
        if ([predicate evaluateWithObject: car]) {
            NSLog (@“%@“, car.name);
        }
    }

//输出完整的信息
    predicate = [NSPredicate predicateWithFormat: @“engine.horsepower > 150”];
    NSArray *results;
    results = [cars filteredArrayUsingPredicate: predicate];
    NSLog (@“%@“, results);

//含有变量的谓词
    NSPredicate *predicateTemplate = [NSPredicate predicateWithFormat:@“name == $NAME”];
    NSDictionary *varDict;
    varDict = [NSDictionary dictionaryWithObjectsAndKeys:
               @“Herbie”, @“NAME”, nil];
    predicate = [predicateTemplate predicateWithSubstitutionVariables: varDict];
    NSLog(@“SNORGLE: %@“, predicate);
    match = [predicate evaluateWithObject: car];
  NSLog (@“%s”, (match) ? “YES” : “NO”);
//注意不能使用$VARIABLE作为路径名,因为它值代表值

//谓词字符窜还支持c语言中一些常用的运算符  
    predicate = [NSPredicate predicateWithFormat:
                 @“(engine.horsepower > 50) AND (engine.horsepower < 200)”];
    results = [cars filteredArrayUsingPredicate: predicate];
    NSLog (@“oop %@“, results);
   
    predicate = [NSPredicate predicateWithFormat: @“name < ‘Newton’”];
    results = [cars filteredArrayUsingPredicate: predicate];
    NSLog (@“%@“, [results valueForKey: @“name”]);

//强大的数组运算符
    predicate = [NSPredicate predicateWithFormat:
                 @“engine.horsepower BETWEEN { 50, 200 }”];
    results = [cars filteredArrayUsingPredicate: predicate];
    NSLog (@“%@“, results);
   
    NSArray *betweens = [NSArray arrayWithObjects:
                         [NSNumber numberWithInt: 50], [NSNumber numberWithInt: 200], nil];
    predicate = [NSPredicate predicateWithFormat: @“engine.horsepower BETWEEN %@“, betweens];
    results = [cars filteredArrayUsingPredicate: predicate];
    NSLog (@“%@“, results);
    predicateTemplate = [NSPredicate predicateWithFormat: @“engine.horsepower BETWEEN $POWERS”];
    varDict = [NSDictionary dictionaryWithObjectsAndKeys: betweens, @“POWERS”, nil];
    predicate = [predicateTemplate predicateWithSubstitutionVariables: varDict];
    results = [cars filteredArrayUsingPredicate: predicate];
    NSLog (@“%@“, results);

//IN运算符
    predicate = [NSPredicate predicateWithFormat: @“name IN { ‘Herbie’, ‘Snugs’, ‘Badger’, ‘Flap’ }”];
    results = [cars filteredArrayUsingPredicate: predicate];
    NSLog (@“%@“, [results valueForKey: @“name”]);
    predicate = [NSPredicate predicateWithFormat: @“SELF.name IN { ‘Herbie’, ‘Snugs’, ‘Badger’, ‘Flap’ }”];
    results = [cars filteredArrayUsingPredicate: predicate];
    NSLog (@“%@“, [results valueForKey: @“name”]);
   
    names = [cars valueForKey: @“name”];
    predicate = [NSPredicate predicateWithFormat: @“SELF IN { ‘Herbie’, ‘Snugs’, ‘Badger’, ‘Flap’ }”];
    results = [names filteredArrayUsingPredicate: predicate];//这里限制了SELF的范围
    NSLog (@“%@“, results);
//BEGINSWITH,ENDSWITH,CONTAINS

//附加符号,[c],[d],[cd],c表示不区分大小写,d表示不区分发音字符,cd表示什么都不区分
    predicate = [NSPredicate predicateWithFormat: @“name BEGINSWITH ‘Bad’”];
    results = [cars filteredArrayUsingPredicate: predicate];
    NSLog (@“%@“, results);
   
    predicate = [NSPredicate predicateWithFormat: @“name BEGINSWITH ‘HERB’”];
    results = [cars filteredArrayUsingPredicate: predicate];
    NSLog (@“%@“, results);
   
    predicate = [NSPredicate predicateWithFormat: @“name BEGINSWITH[cd] ‘HERB’”];
    results = [cars filteredArrayUsingPredicate: predicate];
    NSLog (@“%@“, results);

//LIKE运算符(通配符)
    predicate = [NSPredicate predicateWithFormat: @“name LIKE[cd] ‘*er*’”];
    results = [cars filteredArrayUsingPredicate: predicate];
    NSLog (@“%@“, results);
   
    predicate = [NSPredicate predicateWithFormat: @“name LIKE[cd] ‘???er*’”];
    results = [cars filteredArrayUsingPredicate: predicate];
    NSLog (@“%@“, results);
05/07/2015 16:14 下午 posted in  apple