2. iOS 出包流程. 新建一个 Xcode 工程为例
1
2
3
4
5
6
7
| 1. 打开 Xcode 工程. 进入工程属性配置(左上角)—>Edit Scheme——>Build Configuration 配置 Release
1. 查看 General 下,Automatically manage signning 状态取消. 另外看下应用配置 name. version 等版本以及下面的签名配置是否 ok。
1. 检查完毕之后. Commend+B 可以直接编译运行。
1. 到工程目录下 Products 目录下会生成一个 Demo.app 文件. 打开该文件目录拷贝到一个命名为 Playload 文件夹内。
1. 压缩 Playload 文件重命名其后缀名为.ipa 即可。
注意事项:出包时设备信息选择全部而不是某一台设备. 也就是 Generic iOS Device. 如果选择了某一台模拟器. 或者在 Build 之前设置工程编译属性为 Debug. 都有可能在安装时出现问题。另外不要创建多台模拟器. 会吃内存。
1. 上面这种方式是很久之前出包的方式了. 正常直接构建出 ipa 即可
|
3. iOS ipa 应用文件安装
1
| 1. Appstore 下载一个 Apple Configurator 进行安装
|
4. iOS 证书与描述文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| 1. 苹果证书分为调试证书与生产证书. 用法以及申请方式可上网查询。
2. 描述文件可以在申请的时候. 设置为包名通用的. 后面对应不同工程的包名都可以编译调试(个人或企业签皆可)。
3. P12 文件导入成功后可以在 Mac 钥匙串中找到。
4. 描述文件导入之后. 重命名描述文件中的 UUID 后并保存在~/library/MobileDevice/Provisioning Profiles 文件夹内。
5. P12 导入证书位置~/library/Keychains/
6. 打包时会遇到导入证书有问题. 可以查看一下证书是否过期。查看命令如下
```
#查看p12证书
openssl pkcs12 -legacy -in '/xxx.p12' -nodes | openssl x509 -noout -dates
openssl pkcs12 -legacy -in '/xxx.p12' -nodes -passin pass:123 | openssl x509 -noout -dates
#查看p12证书日期
openssl pkcs12 -in /xxx/xxx.p12 -nodes -passin pass:"123" | openssl x509 -noout -dates
#查看证书UUID和证书名称
security cms -D -i xxx.mobileprovision | grep -A1 "UUID"
openssl pkcs12 -legacy -in xxx.p12 -nodes -passin pass:"密码" | openssl x509 -noout -subject
#查看描述文件
openssl smime -inform der -verify -noverify -in /xxx/xxx.mobileprovision
```
|
5. Xcode 常用快捷键
1
2
3
| 1. Commend+R 工程运行
1. Commend+B 编译工程
1. Commend+/ 注释
|
6. 系统环境与框架
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
| 1. Cocoa OS X 与 IOS 操作系统的程序的运行环境。
1. Cocoa 两个基本框架:
```
1. Foundation 基础库
2. UIKit UI库. 高级对象
```
3. 面向对象 - 和 java 不一样的是没有垃圾回收机制. 除了有 C++与 java 的特点之外
优点:
```
动态类:运行时确定类的对象。
动态绑定:运行时确定需要调用的方法
动态加载:运行时为程序加载新的模块
```
4. 核心概念
|概念|C++ 写法|Objective-C 写法|备注|
|---|---|---|---|
|**头文件**|`.h`|`.h`|一样|
|**源文件**|`.cpp`|`.m` (纯 ObjC) / **`.mm`\*\*** (混合)**|**UE4 只用 .mm**|
|**类定义**|`class MyClass : public Parent`|`@interface MyClass : Parent`|ObjC 所有类继承自 `NSObject`|
|**类实现**|`MyClass::Function() { ... }`|`@implementation MyClass ... @end`||
|**实例方法**|`void MyFunc()`|`\- (void)myFunc;`|减号 `-` 代表成员函数|
|**静态方法**|`static void MyFunc()`|`\+ (void)myFunc;`|加号 `+` 代表静态函数|
|**方法调用**|`obj->MyFunc()`|`\[obj myFunc\];`|**中括号是核心**|
|**包含头文件\*\*|`#include "MyClass.h"`|`#import "MyClass.h"`|import 自动防止重复包含|
> "+" Class Method/Static
> "-" Instance Method
在阅读第三方 iOS SDK 文档时. 你经常会看到这样的描述:
**方法定义:**
```c++
+ (void)initWithAppId:(NSString *)appId;
- (void)login;
```
**开发者的解读方式:**
1. 看到 **`+`** -> 脑补 `static` -> 这是一个**初始化**或**配置**用的全局方法 -> 代码里写 `[SDKName initWithAppId:...]`。
2. 看到 **`-`** -> 这是一个**功能**方法 -> 我必须先拿到 SDK 的**单例对象**或者**实例**才能调用 -> 代码里写 `[[SDKName shared] login]`。
>实战案例A. 头文件 (DemoManager.h)
```
#import <Foundation/Foundation.h>
// @interface 类名 : 父类
@interface DemoManager : NSObject
// 声明单例类方法 (+)
(instancetype)sharedManager;
// 声明一个实例属性 (相当于 Java 的 public String token)
@property (nonatomic, copy) NSString *authToken;
// 声明一个实例方法 (-)
(void)loginWithUserId:(NSString *)uid;
@end
```
>实现文件
```
#import "DemoManager.h"
@implementation DemoManager
// 1. 声明一个静态变量持有实例
static DemoManager *_sharedInstance = nil;
// 2. 实现单例方法
+ (instancetype)sharedManager {
// dispatch_once 保证代码块在整个 App 生命周期只执行一次
// 它是绝对线程安全的. 不需要 synchronized
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[super alloc] init];
// 可以在这里做一些初始化. 比如 _sharedInstance.authToken = @"";
});
return _sharedInstance;
}
// 3. 实现功能方法
- (void)loginWithUserId:(NSString *)uid {
NSLog(@"[iOS] 正在登录用户: %@", uid);
// 模拟登录成功. 设置属性
// self.authToken 相当于 this.setAuthToken(...)
self.authToken = [NSString stringWithFormat:@"token_%@", uid];
}
@end
```
> 具体调用
```
// 引入头文件
#import "DemoManager.h"
void MyUE4Function() {
// 1. 获取单例并调用方法
// [类名 方法] -> 拿到对象
// [对象 方法] -> 执行逻辑
[[DemoManager sharedManager] loginWithUserId:@"1001"];
// 2. 分步写的形式 (更容易理解)
DemoManager *manager = [DemoManager sharedManager];
[manager loginWithUserId:@"1002"];
// 3. 获取属性 (Getter)
// 点语法 .authToken 其实是自动调用了 [manager authToken] 方法
NSString *token = manager.authToken;
NSLog(@"当前 Token 是: %@", token);
}
```
|
7. iOS 依赖库
1
2
3
4
5
6
7
| 1. 依赖库分为静态库与动态库。
2. 静态库是编译时链接到代码中. 完整的 copy 到可执行文件中去的. 多次使用就多次拷贝. 容易产生代码冗余。 动态库是运行时只加载一次. 在程序调用到的地方都可以加载。
3. iOS 中.a 库 与 .framework 库的关系
.a 纯二进制文件
.a +.h+sourcefile = .framework
4. 系统中的.framework 都是动态库. 我们自己创建的都是静态库。
5. .a 是不可直接使用的. 需要配合.h 与其他文件才能使用。framework 可以直接使用。
|
8. iOS 生命周期以及运行状态
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
| >1. 共有五种状态
```
1>Not running 程序未启动
2>Inactive 前台运行
3>Active 激活
4>Background 程序在后台. 且能执行代码. 大多数程序在进入该状态之后就直接Suspended了. 不过也有特殊程序一直处于Background状态
5>Suspended 程序在后台不能执行代码
```
> 2. AppDelegate 回调函数
```
//启动完成. 准备开始运行. 一般会在这里加载一些初始化活动
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSLog(@"AppDelegate didFinishLaunchingWithOptions");
return YES;
}
//进入到非活动状态. 比如Home键. 电话来了等
(void)applicationWillResignActive:(UIApplication *)application {
NSLog(@"AppDelegate applicationWillResignActive");
}
//进入到后台时被调用
(void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"AppDelegate applicationDidEnterBackground");
}
//从后台进入到前台调用
(void)applicationWillEnterForeground:(UIApplication *)application {
NSLog(@"AppDelegate applicationWillEnterForeground");
}
//应用程序进入到活动状态调用
(void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(@"AppDelegate applicationDidBecomeActive");
}
//应用程序将要退出. 保存数据和一些退出前的清理工作. 暂未调出. 慎用!
(void)applicationWillTerminate:(UIApplication *)application {
NSLog(@"AppDelegate applicationWillTerminate");
}
```
执行顺序是启动程序. 执行程序. 然后 Home 桌面. 再次启动程序
```
//启动应用程序
2018-03-22 16:20:05.113 Demo1[3404:103041] AppDelegate didFinishLaunchingWithOptions
2018-03-22 16:20:05.144 Demo1[3404:103041] 程序执行
2018-03-22 16:20:05.145 Demo1[3404:103041] 这里有一个log
2018-03-22 16:20:05.147 Demo1[3404:103041] AppDelegate applicationDidBecomeActive
//Home回到桌面
2018-03-22 16:22:46.211 Demo1[3404:103041] AppDelegate applicationWillResignActive
2018-03-22 16:22:46.678 Demo1[3404:103041] AppDelegate applicationDidEnterBackground
//后台重新启动
2018-03-22 16:22:55.540 Demo1[3404:103041] AppDelegate applicationWillEnterForeground
2018-03-22 16:22:56.010 Demo1[3404:103041] AppDelegate applicationDidBecomeActive
```
|
9. iOS 中 OC 中四种数据类型
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
| 1. 基本数据类型 int float double char
2. 构造类型 数组类型. 结构类型. 共用体类型
3. 指针类型 所有的系统类. 自定义类都是指针
4. 空类型 只有一个值 nil. 该类型没有名称. 没有空类型的变量不能转换成空类型. 但是空类型可以转换成任何引用类型。
5. BOOL 类型. YES. NO. 也就是通常意义上的非 0 值为 YES 的意思。注意:BOOL 类型用一个 8 位(一个字节)的整数来表示. 8 位全 0 则为 NO。
6. 接口声明与实现
```
//方法定义
// .h
@interface Foo : NSObject
- (void)doSomething;
@end
// .m
@implementation Foo
- (void)doSomething { }
@end
//符号定义
+:类方法(相当于 Java 的 static 方法). 用 [ClassName method] 调
-:实例方法. 必须先创建对象再调用 [[ClassName alloc] init] 或通过单例拿到实例后调用
```
7. 属性定义
```
@property (nonatomic, copy) NSString *token;
@property (nonatomic, strong) id service;
//常用修饰:strong/weak/copy. 以及 nonatomic(移动端常用)
```
|
10. 提交包到 Appstore 流程
1
2
3
4
5
6
7
| 1. 打开最新版本 Xcode10. 目前 Xcode9 也可以提交。
2. 点击左上角 Xcode->open Developer tools->Application Load
3. 输入账号名密码. 选择使用 dis 签名的包提交上传即可。
4. 常见审核不通过原因:
1. 2.1 苹果怀疑应用有后门开关或者已经有相似应用. 如果首次提交遇到该情况. 请逐条申明回复. 可在网上搜有正面怼的模板。
2. 4.3 苹果审核怀疑重复应用. 如果是应用首次提交可照着上面做. 否则呵呵祝好运。
3. 未审核通过的 ipa 包 180 天之后会自动删除。
|
11. 关于 ipa 逆向
1
2
3
| 1. 如果忘记了版本号需要查看当前包的版本. 可以解压缩该压缩包得到一个.app 文件. 右键查看内容即可进入该文件中. 找到 info.plist 文件查看。
2. 对于国内平台上架的越狱包. 如果需要修改版本号则无需重新出包. 参照上面再 info.plist 文件修改完版本号之后. 重新在 Payload 文件夹压缩后. 修改后缀为.ipa. 使用签名工具进行重签名即可。注意如果是替换某些文件. 在安装过该包的设备上需要卸载老包且重启才有效果(签名工具自己在 github 中找)
3. 如果 ipa 安装时一直装不上了. 很可能是签名过期了. 或者重签名失败了。
|
12. 关于 iOS 自动化
这个比较复杂. 如果经常出包的应用只有一个. 可以考虑使用自动化工具. 比如 fastline 配置好之后. 在手动导入签名文件后. 可以实现一行代码搞定自动化出包。或则配置 Jenkins 框架(android 也可以考虑使用这个框架)。如果是有多个不确定的 iOS 要出包. 请研究下 ruby. openssl. python. shell 等语言一定能有一个不错的解决方式。
1
2
3
4
5
6
7
8
9
| ```
xcodebuild -list -project XXX.xcodeproj #列出工程下所有target与Scheme
xcodebuild clean #清理
xcodebuild -scheme <Scheme> build #编译
xcodebuild -target Demo(Scheme) -xcconfig configuration.xcconfig #根据配置文件来编译
xcodebuild -scheme Demo SYMROOT="/Users/xxx/Desktop/app" #设置一个本地Debug路径
xcodebuild -scheme Demo DSTROOT="/Users/xxx/Desktop/app" archive #设置一个本地Release路径
通过xcodebuild -target #编译会输出默认的app包. 下面这里可以进行release与debug的切换。
```
|
13. iOS 混淆
混淆思路:如果是为了防止逆向开发者破解代码. 可以考虑将所有的关键字以及类方法都混淆成乱序的方式. 注意一旦混淆后就无法回滚. 可以从 github 搜索. 一般研究两到三个. 就可以找到你想要的。