Alex Lin's Notes

Strategy, Execution, Communication.

LLVM Module

A module is a single unit of code distribution—a framework or application that is built and shipped as a single unit and that can be imported by another module with Swift’s import keyword.

Each build target (such as an app bundle or framework) in Xcode is treated as a separate module in Swift. If you group together aspects of your app’s code as a stand-alone framework—perhaps to encapsulate and reuse that code across multiple applications—then everything you define within that framework will be part of a separate module when it’s imported and used within an app, or when it’s used within another framework.

As the docs indicate, the module is an application or a framework (library). If you create a project with classes A and B, they are part of the same module. Any other class in the same project can inherit from those classes. If you however import that project to another project, classes from that another project won’t be able to subclass A nor B. For that you would have to add open indicator before their declarations.

Basically, if you work on a single app then you are working in one single module and unless declared as private or fileprivate, the classes can subclass each other.

Module

Module 是一种集成库的方式,在 Module 出现之前,开发者需要在引入库文件的同时引入需要使用的头文件,以保证编译的正常进行。但是每次引入库的时候都要导入一堆文件,看起来并不优雅。Module 和 Framework 的出现让开发者极大程度上告别了这些不优雅的工作。简单说就是用树形的结构化描述来取代以往的平坦式 #include, 例如传统的 #include <stdio.h> 现在变成了 import std.io

主要好处有:

  1. 语义上完整描述了一个框架的作用
  2. 提高编译时可扩展性,只编译或 include 一次。避免头文件多次引用,只解析一次头文件甚至不需要解析(类似预编译头文件)
  3. 减少碎片化,每个 module 只处理一次,环境的变化不会导致不一致
  4. 对工具友好,工具(语言编译器)可以获取更多关于 module 的信息,比如链接库,比如语言是 C++ 还是 C

modulemap 文件

module.map 文件就是对一个框架,一个库的所有头文件的结构化描述。通过这个描述,桥接了新语言特性和老的头文件。默认文件名是 module.modulemap,modulemap 其实是为了兼容老标准,不过现在 Xcode 里的还都是这个文件名,相信以后会改成新名字。

文件的内容以 Module Map Language 描述,大概语法如下:

1
2
3
4
5
6
7
8
9
10
11
module MyLib {
explicit module A {
header "A.h"
export *
}

explicit module B {
header "B.h"
export *
}
}

类似上面的语法,描述了 MyLib、MyLib.A、MyLib.B 这样的模块结构。

官方文档中有更多相关内容,可以描述框架,描述系统头文件,控制导出的范围,描述依赖关系,链接参数等等。这里不多叙述,举个 libcurl 的例子:

1
2
3
4
5
module curl [system] [extern_c] {
header "/usr/include/curl/curl.h"
link "curl"
export *
}

将此 modulemap 文件放入任意文件夹,通过 Xcode 选项或者命令行参数,添加路径到 import search path (swift 的 -I 参数)。 然后就可以在 Swift 代码里直接通过 import curl 导入所有的接口函数、结构体、常量等。

Xcode 选项位于 Build Settings 下面的 Swift Compiler - Search Paths 。添加路劲即可。

每个Module中必须包涵一个umbrella头文件,这个文件用来import所有这个Module下的文件。

大致关系为:import module -> import umbrella header -> other header

使用 Module 库的调用方式:

项目类型 OC库(GDTPackage) Swift库(GDTPackage)
OC 项目 #import <GDTPackage/GDTPackage.h> #import <GDTPackage-Swift.h>
Swift 项目 import GDTPackage import GDTPackage

GDTPackage.h 其实就是 umbrella header/master header

CocoaPods 自定义 Module

我们以桥接 GDTMobSDK 为例。

创建 GDTPackage 库

通过 CocoaPods 提供的命令行创建库:

1
$ pod lib create GDTPackage

创建 module.modulemap 和 BridgeHeader.h

在项目中新建 module.modulemapBridgeHeader.h,将它们放在同一个文件夹下 GDTPackage/Module

module.modulemap 代码如下:

1
2
3
4
module GDTPackageBridge {
header "BridgeHeader.h"
export *
}

BridgeHeader.h 代码如下:

1
2
3
4
5
6
#import <GDTMobSDK/GDTMobBannerView.h>
#import <GDTMobSDK/GDTRewardVideoAd.h>
#import <GDTMobSDK/GDTNativeExpressAd.h>
#import <GDTMobSDK/GDTNativeExpressAdView.h>
#import <GDTMobSDK/GDTMobInterstitial.h>
#import <GDTMobSDK/GDTSplashAd.h>

GDTPackage.podspec 部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
...
s.static_framework = true
s.source_files = 'GDTPackage/Classes/**/*'

s.preserve_paths = ['GDTPackage/Module/module.modulemap', 'GDTPackage/Module/BridgeHeader.h']
s.pod_target_xcconfig = {
# 路径根据实际情况进行引用,必须保证路径是正确的
'SWIFT_INCLUDE_PATHS' => ['$(PODS_ROOT)/GDTPackage/Module', '$(PODS_TARGET_SRCROOT)/GDTPackage/Module']
}

s.dependency 'GDTMobSDK'
...

代码中引用 GDTPackageBridge

1
2
3
4
5
6
7
import GDTPackageBridge

class GDTPackage {
func test() {
GDTSplashAd.init()
}
}

注意事项

  1. 如果已经在 preserve_paths 添加了 modulemapheader,可以不用在 source_files 里再加一遍,如果要在 source_files 里加也可以,记得指定 public_header_files。如果没有指定,你自己创建的 modulemap 也会当做 public 处理。这样 lint 的时候会报 Include of non-modular header inside framework module

  2. lint 时遇到 Include of non-modular header inside framework module 错误,可以在后面添加 --use-libraries。虽然能验证和上传通过,但是其他项目引用的时候还是会有问题。

  3. user_target_xcconfig 是针对所有 Pod 的,可能和其他 Pod 存在冲突。pod_target_xcconfig 是针对当前 Pod 的。

参考链接

  1. Modules - Clang 12 documentation

想在项目中使用静态库功能,需要在 Podspec 显示指定 s.static_framework = true,对于多个 Pod 的项目来说,一个个改起来太麻烦了,也不现实。但是 CocoaPods 是 Ruby 写的,我们可以通过 patch CocoaPods 来实现在只写几行代码的情况下,把所有 pod 变成 Static Framework。

通过分析 CocoaPods 的源代码发现,CocoaPods 会通过 Pod -> Installer -> Analyzer -> determine_build_type 这个方法来决定每个 podspec 的 build type,我们可以通过 patch 这个方法来改写。

在 Podfile 的同级目录创建 patch_static_framework.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module Pod
class Installer
class Analyzer
def determine_build_type(spec, target_definition_build_type)
if target_definition_build_type.framework?
# 过滤掉只能动态库方式的framework,或者不确定的framework
dynamic_frameworks = ['xxxxx']
if !dynamic_frameworks.include?(spec.root.name)
return BuildType.static_framework
end
root_spec = spec.root
root_spec.static_framework ? BuildType.static_framework : target_definition_build_type
else
BuildType.static_library
end
end
end
end
end

在 Podfile 的最上面,引入该文件

1
require_relative 'patch_static_framework'

这样 patch 就会在 pod install 的时候生效,我们就不需要改每个 Pod 的 Podspec 就可以实现每个 Pod 都是 static_framework。

越狱软件

越狱软件统计(更新日期:2020-05-24)

越狱软件 最新版本 支持设备 支持版本 源码
unc0ver 5.0.0 A7 ~ A13 iOS 11.0 ~ 13.5 https://github.com/pwn20wndstuff/Undecimus/
checkra1n 0.10.2 beta iPhone 5s ~ iPhone X iOS 12.3 ~ 未释放
Chimera 1.4.0 所有设备 iOS 12 ~ 12.2 and 12.4,tvOS 12 ~ 12.2 and 12.4 https://github.com/coolstar/Chimera13
Electra 1.3.2 所有设备 iOS 11.0 – 11.4.1 https://github.com/coolstar/electra
Meridian v0.9-007 Pre-Release 所有64位设备 iOS 10 ~ 10.3.3 https://github.com/PsychoTea/MeridianJB
Doubleh3lix RC8 所有64位A7 ~ A9 iOS 10.x
yalu102 beta7 所有64位设备除了iPhone7 iOS 10.x https://github.com/kpwn/yalu102

越狱工具

  1. Note: This method requires an Apple developer account.
  2. Download Cydia Impactor for the applicable OS.
  3. Extract the application file, and open it.
  4. Connect your iOS device.
  5. Download the latest version of unc0ver from above.
  6. Drag the IPA file into the Impactor window.
  7. Enter your Apple ID and password (requires developer account). (Note: If you are using two factor authentication, generate an app specific password, and use that here.)
  8. On your iOS device, open Settings → General → Device Management and tap on your Apple ID.
  9. Trust unc0ver.
  10. Open unc0ver and jailbreak!
  • AltStore
    1. Download AltStore. Use the link for your operating system.
    2. Unzip and move AltStore to your Applications folder.
    3. Launch the AltStore application.
    4. Click on the AltStore icon in the Menu Bar, and then click on the Install Mail Plug-in option.
    5. Open the Mail app, and click on Mail → Preferences in the menu bar.
    6. Open the General tab in mail preferences, click Manage Plug-ins, check AltPlugin, and apply and restart Mail.
    7. Connect your iOS device via USB.
    8. Click AltStore in the menu bar, then go to Install AltStore → (Your iOS Device)
    9. Login with your Apple ID when prompted and click install.
    10. On your iOS device, open Settings → General → Device Management and tap on your Apple ID.
    11. Trust AltStore.
    12. Tap the “Open in AltStore” button located above.
    13. AltStore will now install the app. Wait until it finishes.
    14. Open unc0ver and jailbreak!
  • iOS App Signer
    1. Install Xcode, open it, and agree to the license agreement.
    2. Plug in your iOS device and select it as the build target.
    3. Open Xcode and create a new iOS Application.
    4. Type a name and identifier.
    5. Xcode will complain about the lack of a provisioning profile. Click fix issue.
    6. Sign into an Apple ID when prompted.
    7. Download iOS App Signer
    8. Download the latest version of unc0ver from above.
    9. Open iOS App Signer.
    10. Select the ipa you just downloaded as an input file.
    11. Click start.
    12. Return to Xcode. Go to the menu bar. Click Window → Devices.
    13. Find your device, click the plus, and select the file created by iOS App Signer.
    14. Open unc0ver on your device and jailbreak!

Cydia源&常用软件

BigBoss

源:http://apt.thebigboss.org/repofiles/cydia/

  • OpenSSH
  • FLEXing
  • LookinLoader
  • LocationFakerX
  • AnyWhere!–虚拟定位

Binger

源:https://apt.bingner.com/

  • Class Dump

Frida

源:https://build.frida.re/

  • Frida

Chariz

源:https://repo.chariz.com/

  • Cephei
  • NewTerm
  • QuitAll

TIGI Software

源:https://tigisoftware.com/cydia/

  • Apps Manager
  • Filza File Manager

Matchstic

源:https://repo.incendo.ws/

  • ReProvision

雷锋源

源:https://apt.abcydia.com/

  • AppStore++ 应用降级
  • iCleaner Pro
  • NetControl 联网控制
  • Shadow 屏蔽越狱检测
  • eSim+ 双卡增强
  • FlyJB 屏蔽越狱检测
  • NtSpeed 悬浮网速
  • Filza File 文件管理器
  • CacheClearerXI 缓存清理
  • Snapper 2 智能截图
  • CarBridge 汽车互联
  • AudioRecorder XS 通话录音

fastlane is the easiest way to automate beta deployments and releases for your iOS and Android apps. 🚀 It handles all tedious tasks, like generating screenshots, dealing with code signing, and releasing your application.

fastlane 是自动化Beta部署和发布iOS和Android应用程序最简单方法。它可以处理所有繁琐的任务,例如生成屏幕截图,处理代码签名以及发布应用程序。

Fastlane 安装

安装 Xcode command line tools

1
$ xcode-select --install

安装 Homebrew

1
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安装 RVM

1
2
$ curl -sSL https://get.rvm.io | bash -s stable --auto-dotfiles
$ source ~/.rvm/scripts/rvm

修改 RVM 的 Ruby 安装源到 Ruby China 的 Ruby 镜像服务器,这样能提高安装速度。

1
$ echo "ruby_url=https://cache.ruby-china.org/pub/ruby" > ~/.rvm/user/db

安装Ruby 2.6.5

1
2
$ rvm install 2.6.5
$ rvm use 2.6.5 --default

更新 RubyGems 镜像

1
2
3
4
5
$ gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/
$ gem sources -l
https://gems.ruby-china.org
# 确保只有 gems.ruby-china.org
bundle config mirror.https://rubygems.org https://gems.ruby-china.org

安装 CocoaPods 和 Fastlane

1
2
3
$ gem install cocoapods
$ gem install fastlane -NV
$ gem install bundle
阅读全文 »

Swift 的函数作为一等公民,可以赋值给变量,柯里化,也可以作为参数传递(如果将函数作为参数传递给闭包,只要类型匹配,就可以将函数引用代替内联闭包)。我们可以将函数当作带有名称的特殊闭包,但是使用的时候需要当心。

0x01 问题

最近遇到一个在 Swift 中将函数作为参数传递给闭包时,导致循环引用的场景。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class ClassA {

var commandHandler: () -> Void = { }

init() {
print("init ClassA")
}

deinit {
print("deinit ClassA")
}

func handle(commandHandler: @escaping () -> Void) {
self.commandHandler = commandHandler
}
}

class ClassB {
let a: ClassA
init(a: ClassA) {
print("init ClassB")
self.a = a
a.handle(commandHandler: self.commandAction)
}

deinit {
print("deinit ClassB")
}

func commandAction() {

}
}

实例化ClassB,这个时候就会产生循环引用导致内存泄漏。

0x02 实例函数是柯里化类函数

在Swift中,实例函数只是柯里化类函数,该类函数将实例作为第一个参数,并隐式地使第一个参数作为self可供函数体使用。 因此,以下两个是等价的:

1
2
3
let numbers = [1, 2, 3, 4, 5, 6, 7, 8]
numbers.contains(3) //true
Array.contains(numbers)(3) //true

而且,这些也是等价的:

1
2
3
let handler1 = self.commandAction
let handler2 = self.dynamicType.commandAction(self)
let handler3 = { [unowned self] in self.commandAction() }

0x03 可以通过泛型函数来管理内存

如果我们要从上面的 handler2 中获取 self.dynamicType.commandAction,但是没有参数 (self)作为参数传递给了包装函数以便引用 self,我们改怎么办呢? 我们可以通过unowend来引用,并将 unowned 实例引用传递给类函数获取一个实例函数,而且不会导致循环引用。

1
2
3
4
5
6
7
func unown<T: AnyObject, V>(_ instance: T, _ classFunction: @escaping (T) -> (() -> V)) -> () -> V {
return { [unowned instance] in classFunction(instance)() }
}

func unown<T: AnyObject, U, V>(_ instance: T, _ classFunction: @escaping (T) -> ((U) -> V)) -> (U) -> V {
return { [unowned instance] in classFunction(instance)($0) }
}

这样的话,我们就可以通过以下方式来获取实例方法的引用,而且我们不会强引用self

1
let handler4 = unown(self, self.dynamicType.commandAction)

缺点是,函数每增加一个参数,我们就需要写一个泛型函数来管理内存。而且,由于使用的是unowned管理内存,如果使用不当会导致野指针访问导致崩溃。

参考链接

接触新项目后,发现没有改代码的情况下,每次编译基本上编译时间都在一分钟左右。就有了一个想法去解决这个问题,断断续续花了三天时间解决,解决过程中,学习到很多,记录下来。

0x01 发现问题

开启编译耗时显示

打开终端执行以下命令并重启Xcode:

1
$ defaults write com.apple.dt.Xcode ShowBuildOperationDuration -bool YES

编译 Build

编译时长 56.3 s,其中耗时比较长的过程为以下:

  • Compile asset catalogs:23.5 s
  • [CP]Embed Pods Frameworks:7.4 s
  • [CP] Copy Pods Resources:17.6 s

0x02 分析&解决问题

开始尝试优化 Xcode 编译速度

发现编译耗时集中在上面三个过程中,一开始主要关注于 Xcode 本身编译提升,看了很多关于提升 Xcode 编译速度的文章,比如这篇文章:https://elliotsomething.github.io/2018/05/23/XCodeBuild/

编译时长优化 Find Implicit Dependencies

对所编译项目的Scheme进行配置 Product > Scheme > Edit Scheme > Build Build Opitions选项中,去掉Find Implicit Dependencies。

编译线程数优化

1
2
3
4
$ defaults write com.apple.dt.xcodebuild PBXNumberOfParallelBuildSubtasks `sysctl -n hw.ncpu`
$ defaults write com.apple.dt.xcodebuild IDEBuildOperationMaxNumberOfConcurrentCompileTasks `sysctl -n hw.ncpu`
$ defaults write com.apple.dt.Xcode PBXNumberOfParallelBuildSubtasks `sysctl -n hw.ncpu`
$ defaults write com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks `sysctl -n hw.ncpu`

其后的数字为指定的编译线程数。Xcode默认使用与CPU核数相同的线程来进行编译,但由于编译过程中的IO操作往往比CPU运算要多,因此适当的提升线程数可以在一定程度上加快编译速度。

然后做完以上尝试后,优化了4s。😭

远远没有达到优化的目的。

寻找另外的解决方向

从 Xcode 的本身优化不能有任何的提升后,那问题只能出在工程本身,再次分析编译过程的时长发现和 Assets.xcassets 和 Pods 关系很大。先从 CocoaPods 开始分析 Podfile,发现工程的 Podfile 有如下代码:

install! ‘cocoapods’, disable_input_output_paths: true

去掉以后运行 pod install,出现编译出现错误:

error: Multiple commands produce ‘/xxxxx/xxxxx/Assets.car’:

  1. Target ‘xxxx’ (project ‘xxx’) has compile command with input ‘/xxxx/xxxx/Assets.xcassets’

  2. That command depends on command in Target ‘xxx’ (project ‘xxx’): script phase “[CP] Copy Pods Resources”

在 CocoaPods 上找到了这样一个 issue https://github.com/CocoaPods/CocoaPods/issues/8122,里面提到主工程里 Assets.xcassets 和 Pods 里有同名的 Assets.xcassets,在 Xcode 10 之前进行编译是不会有问题的,Xcode 只是生成 Warning,但是在 Xcode 10 之后使用了 New Build System 会生成 Errror,提示重复生成 Assets.car。

issue 里提到了4种解决方案:

方案1:https://github.com/CocoaPods/CocoaPods/issues/8122#issuecomment-424169508

1
install! 'cocoapods', :disable_input_output_paths => true

这个方案会导致每次编译时长增加3x倍多。这也刚好是我们工程采用的方式。

方案2:https://github.com/CocoaPods/CocoaPods/issues/8122#issuecomment-424265887

使用 Legacy Build System 而不是 Xcode 11 的 New Build System

方案3:在 Podfile 中添加如下代码

1
2
3
4
5
6
7
8
9
10
project_path = '[YOUR_PROJ_NAME].xcodeproj'
project = Xcodeproj::Project.open(project_path)
project.targets.each do |target|
build_phase = target.build_phases.find { |bp| bp.display_name == '[CP] Copy Pods Resources' }

assets_path = '${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Assets.car'
if build_phase.present? && build_phase.input_paths.include?(assets_path) == false
build_phase.input_paths.push(assets_path)
end
end

这种方案在 CocoaPods 1.8.0 之前可以的,但是在 1.8.0 之后 Input Files 变成了 xcfilelist,就无法直接使用了。

方案4:https://github.com/CocoaPods/CocoaPods/issues/8122#issuecomment-531726302

主要代码是在 [CP] Copy Pods ResourcesInput Files 或者 Input File Lists 中添加。

1
$ {TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Assets.car

尝试了以上4种解决方案,只有方案4 符合预期。

0x03 解决方案

使用这个 cocoapods 插件:https://github.com/dreampiggy/cocoapods-xcode-patch

使用 BundlerGemfile 添加这个插件:

1
2
3
4
source "https://rubygems.org"

gem 'cocoapods'
gem 'cocoapods-xcode-patch', :git => 'https://github.com/dreampiggy/cocoapods-xcode-patch.git'

使用 bundle exec pod install 替代 pod install 来加载这个插件。

0x04 原因分析

出现这个问题根本原因是因为 CocoaPods 有两种资源管理方式 resource_bundlesresources

以下简单介绍下这两种资源管理方式:

resource_bundles(官方推荐)

This attribute allows to define the name and the file of the resource bundles which should be built for the Pod. They are specified as a hash where the keys represent the name of the bundles and the values the file patterns that they should include.

For building the Pod as a static library, we strongly recommend library developers to adopt resource bundles as there can be name collisions using the resources attribute.

The names of the bundles should at least include the name of the Pod to minimise the chance of name collisions.

To provide different resources per platform namespaced bundles must be used.

Examples:

1
spec.ios.resource_bundle = { 'MapBox' => 'MapView/Map/Resources/*.png' }
1
2
3
4
spec.resource_bundles = {
'MapBox' => ['MapView/Map/Resources/*.png'],
'MapBoxOtherResources' => ['MapView/Map/OtherResources/*.png']
}

resources

A list of resources that should be copied into the target bundle.

For building the Pod as a static library, we strongly recommend library developers to adopt resource bundles as there can be name collisions using the resources attribute. Moreover, resources specified with this attribute are copied directly to the client target and therefore they are not optimised by Xcode.

Examples:

1
spec.resource = 'Resources/HockeySDK.bundle'
1
spec.resources = ['Images/*.png', 'Sounds/*']

由于组件化的原因,我们的某个组件采用了Assets.xcassets 和 Storyboard 需要拷贝到主工程中进行引用,Pod 库只能以 resources 的方式引用资源。经过这次优化编译速度有了很大提升。

0x05 后续:Pods 文件更改没有更新

优化了 Xcode 编译后,出现另外一个问题:更改 Pods 库后,Pods 库已编译但主工程没有使用最新的frameworks,导致动态链接的时候找不到对应的符号而产生崩溃。

导致这个问题的原因是 Build Phases 中的 [CP] Embed Pods Frameworks 不是每次都执行,猜测可能是 Xcode 11 的 New Build System 做了优化,导致脚本没有执行。最终想了个办法来解决这个问题,追加命令来执行脚本 find "${PODS_ROOT}" -type f -name *frameworks.sh -exec bash -c "touch \"{}\"" \;,使得脚本每次能执行更新frameworks。

因为 [CP] Embed Pods Frameworks的脚本是由 CocoaPods 进行修改的,所有我将上面的命令通过hook的方式来追加,具体使用方法可以查看 https://github.com/xwal/cocoapods-xcode-patch

编译时间也有所增加,在工程中测试大概增加了20s左右,还有优化的空间,后续如果想到更好的解决办法再更新。

0x06 参考链接

尝试支付

使用国际信用卡(Master或Visa)务必保证账单地址正确,否则会导致支付失败。

我使用的是招行的Visa信用卡,账单地址可以从招行的掌上生活 App 搜索账单地址可以找到。

无法支付或支付失败

  • 周一到周五,早上9点到下午5点。

  • 登录开发者中心,左侧,Contact Us,选择会员资格与帐户—>计划购买和续订。

  • 填电话号码,苹果客服会打电话给你。

  • 接通10秒左右按1与技术顾问交谈,你就跟客服说,无法支付年费,让他们给解决的方法。

  • 他们一般会让你先多尝试,你要先做一些支付失败的记录。

  • 然后跟他们说,确实无法支付,他们就会给你两种方案其中一种,不一定是哪一种。

  • 第一种是直接信用卡划扣,他们会问你有没有国际信用卡,Master或Visa的。

    然后要你给卡号,过期时间,还有账单地址,给你尝试手动划扣99美金,扣成功的话,就直接成功了。

  • 第二种是让你电汇到苹果公司的账号。
    他们会手动帮你生成一个订单,然后要转账99美金到他们苹果美国的银行账号里。
    然后转账附言上订单号。
    可以使用建行,我是用这个解决的,要有U盾。
    先购汇99美金,大概690+人民币,然后电汇需要手续费20+80,就是100块手续费。
    大概就是800左右了。
    电汇成功后回复邮件告诉他们已经电汇了,就行了,一般隔日账号就通过了。

  • 以上。

链接

  1. 苹果开发者中心联系电话:https://developer.apple.com/contact/phone/cn/

分析工具:clang

1
2
3
4
clang -rewrite-objc test.m

// UIKit
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk xxxxx.m

block 的数据结构定义

对应的结构体定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};

struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};

block 的三种类型

  1. _NSConcreteGlobalBlock 全局的静态 block,不会访问任何外部变量。
  2. _NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销毁。
  3. _NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0 时会被销毁。

block对变量的捕获规则:

  1. 静态存储区的变量:例如全局变量、方法中的static变量
    引用,可修改。

  2. block接受的参数
    传值,可修改,和一般函数的参数相同。

  3. 栈变量 (被捕获的上下文变量)
    const,不可修改。 当block被copy后,block会对 id类型的变量产生强引用。
    每次执行block时,捕获到的变量都是最初的值。

  4. 栈变量 (有__block前缀)
    引用,可以修改。如果时id类型则不会被block retain,必须手动处理其内存管理。
    如果该类型是C类型变量,block被copy到heap后,该值也会被挪动到heap

变量的复制

对于 block 外的变量引用,block 默认是将其复制到其数据结构中来实现访问的。

对于用 __block 修饰的外部变量引用,block 是复制其引用地址来实现访问的。

嵌套block

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)setUpModel{
XYModel *model = [XYModel new];

__weak typeof(self) weakSelf = self;
model.dataChanged = ^(NSString *title) {
__strong typeof(self) strongSelf = weakSelf;
strongSelf.titleLabel.text = title;

__weak typeof(self) weakSelf2 = strongSelf;
strongSelf.model.dataChanged = ^(NSString *title2) {
__strong typeof(self) strongSelf2 = weakSelf2;
strongSelf2.titleLabel.text = title2;
};
};

self.model = model;
}

这样,就避免的引用循环,总结一下,不管都多少个block嵌套,皆按此法

@weakify, @strongify 使用

  1. weakify(self)展开后是: **weak typeof(self) **weak_self = self;

  2. strongify(self)展开后是:**strong typeof(self) self = **weak_self;

  3. 在block中使用strongify(self);的目的是确保在block作用域内self不会被其它线程释放掉

  4. 以前我们在block中直接使用__weak_self来解除循环引用。这本身没有问题,之所以还要加strongify(self)就是为了避免block中代码执行过程中由于其它线程释放了self导致block内执行的逻辑出现问题。例如:会出现执行前几句代码时访问self还是存在的,但后面的self调用已经变为nil了

  5. 如果是在block外部定义strongify(self)虽然在block中的self还是指向(跳转到定义)这个strongify(self)。但因为方法调用结束后strongify(self)定义的局部self变量被释放了,所以这种做法就回退到了[4]

  6. 由5可知,如果block中有多个嵌套的block异步调用,那么每一个block中都要再定义一个strongify(self);

  7. 虽然在多层嵌套的block中,定义weakify(self)也是可行的。但是不推荐这么做

  8. swift中使用unowned和weak来解决循环引用问题,基本原理同OC。但unowned本质上是__unsafe_unretained即assign,所以使用起来要小心野指针。还是推荐无脑用weak

  9. 不过要达到[3]中的效果,就要在当前closure的作用域内retain下self,只不过有个小麻烦是没法像OC中写的那么自然——不能使用self了。例子如下:

    1
    2
    3
    4
    5
    obj.doSomething {[weak self] in
    if let strong_self = self {
    strong_self.Member_XXX
    }
    }

总结:多层嵌套的block,只需要对最外层block传入的self进行weak化即可。

参考文章

  1. 谈Objective-C block的实现
  2. block没那么难(一):block的实现
  3. block没那么难(二):block和变量的内存管理
  4. block没那么难(三):block和对象的内存管理
  5. 深入研究Block捕获外部变量和__block实现原理
  6. 深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用
  7. iOS 中的 block 是如何持有对象的
  8. objc 中的 block
  9. iOS开发之block终极篇
  10. iOS Block用法和实现原理
  11. OC高级编程——深入block,如何捕获变量,如何存储在堆上
  12. A look inside blocks: Episode 1
  13. A look inside blocks: Episode 2
  14. A look inside blocks: Episode 3
  15. 对 Objective-C 中 Block 的追探
  16. LLVM 中 block 实现源码
  17. objective-c-blocks-quiz
  18. Which Clang Warning Is Generating This Message?
  19. iOS 内存泄漏分析

Cocos2d-x 引擎

Cocos2d-x引擎可在 Cocos官网下载,其下载地址为:http://www.cocos.com/download/。当然,亦可从Cocos2d-x的 GitHub 仓库拉取,仓库地址:https://github.com/cocos2d/cocos2d-x。下载完成后,引擎包的主要内容下。
framework_architecture

  • AUTHORS:作者目录,包含所有给Cocos2d-x引擎贡献代码的开发者
  • build:包含测试例子、cocos2d_lib的Xcode以及Visual Studio工程
  • CHANGELOG:所有历史版本详细改动列表
  • CMakeLists.txt:cmake配置文件
  • cocos:Cocos2d-x引擎源代码
  • CONTRIBUTING.md:贡献代码指南
  • docs:包含JavaScript代码风格规范、当前发布说明和当前版本升级指南
  • download-deps.py:下载第三方库的脚本
  • extensions:第三方扩展
  • external:存放第三方库的文件夹
  • licenses:所有许可协议
  • plugin:插件
  • README.cmake:针对cmake用法的说明文件
  • README.md:Cocos2d-x引擎简介
  • setup.py:Cocos Console的安装脚本
  • templates:Cocos Console创建项目时使用的模板
  • tests:各分支的测试项目
  • tools:工具文件夹
  • —bindings-generator:脚本绑定工具
  • —cocos2d-console:Cocos Console工具
  • —tojs:JSB自动绑定配置文件以及生成脚本
  • —tolua:Lua绑定配置文件以及生成脚本
  • web:Cocos2d-JS游戏引擎
阅读全文 »

软件团队的模式

主治医师模式(Chief Programmer Team,Surgical Team)

就像在手术台上那样,有一个主刀医师,其他人(麻醉,护士,器械)各司其职,为主刀医师服务。

这样的软件团队中,有首席程序员(Chief Programmer),他/她负责处理主要模块的设计和编码,其他成员从各种角度支持他/她的工作(后备程序员、系统管理员、工具开发、编程语言专家、业务专家)。

在一些学校里,软件工程的团队模式往往从这一模式退化为『一个学生干活,其余学生跟着打酱油』。

明星模式(Super-star Model)

主治医师模式运用到极点,可以蜕化为明星模式,在这里,明星的光芒盖过了团队其他人的总和。明星也是人,也会受伤,犯错误,如何让团队的利益最大化,而不是明星的利益最大化?如何让团队的价值在明星陨落之后任然能够保持?是这个模式要解决的问题。

社区模式(Community Model)

社区由很多志愿者参与,每个人参与自己感兴趣的项目,贡献力量,大部分人不拿报酬。这种模式的好处是『众人拾柴火焰高』,但是如果大家都只来烤火,不去拾柴;或者捡到的柴火质量太差,最后火也就熄灭了。『社区』并不意味着『随意』,一些成功社区项目(例如开发和维护Linux操作系统的社区),都有很严格的代码复审和签入的质量控制。

业余剧团模式(Amateur Theater Team)

这样的团队在每一个项目中,不同的人会挑选不同的角色。在下一个项目中,这些人也许会换一个完全不同的角色类型。各人在团队中听从一个中央指挥(导演)的指导和安排。在学生实践项目或培训项目中,这样的事情经常发生。

秘密团队(Skunk Work Team)

一些软件项目在秘密状态下进行,别人不知道他们具体在做什么。这种模式的好处是:团队内部有极大的自由,没有外界的干扰(不用每周给别人介绍项目进展,听领导的最新指示,等等),团队成员有极大的投入。

特工团队(SWAT)

软件行业的一些团队由一些有特殊技能的专业人士组成,负责解决一些棘手而有紧迫性的问题。

交响乐团模式(Orchestra)

想象一下交响乐团的演奏,有下面的特点。

  • 家伙多,门类齐全。
  • 各司其职,各自有专门场地,演奏期间没有聊天、走动等现象。
  • 演奏都靠谱,同时看指挥的。
  • 演奏的都是练习过多次的曲目,重在执行。

当某个软件领域处于稳定成长阶段的时候,众多大型软件公司的开发团队就会才去这种模式。

爵士乐模式(Jazz Band)

和交响乐团相比,这种模式有以下特点。

  • 不靠谱。他们演奏时都没有谱子。
  • 没有现场指挥,平时有编曲起到协调和指导作用。
  • 也有模式,架构师先吹出主题,然后他走到一旁抽烟去了,其余人员根据这个主题各自即兴发挥,最后迈尔斯加入,回应主题,像是对曲子的总结。
  • 人数较少。

功能团队模式(Feature Team)

很多软件公式的团队最后都演变成功能团队,简而言之,就是具备不同能力的同事们平等协作,共同完成一个功能。

在这个功能完成之后,这些人又重新组织,和别的角色一起去完成下一个功能。他们之间没有管理和被管理的关系。大型软件公司里的不少团队都是采用这种模式。这些功能小组也称为Feature Crew,小组内的交流比较频繁。

每个小组都由一到三个人组成,每个小组都是一个有自主权的单元,可以自由选用最有利于他们完成工作的任何技术。但是,每个小组必须与其他小组就编码规范达成一致。

官僚模式(Bureaucratic Model)

这种模式脱胎于大机构的组织架构,几个人报告给一个小头目,几个小头目报告给中头目,依次而上。这种模式在软件开发中会出问题。因为成员之间不光有技术方面的合作和领导,同时还混进了组织上的领导和被领导关系。跨组织的合作变得比较困难,因为各自头顶上都有不同的老板。

这种模式如果应用不好,最后会变成『老板驱动』的开发流程。

阅读全文 »
0%