当前位置: 首页 > 编程日记 > 正文

IOS推送详解

为什么80%的码农都做不了架构师?>>>   hot3.png

IOS推送详解

一.关于推送通知

推送通知,也被叫做远程通知,是在iOS 3.0以后被引入的功能。是当程序没有启动或不在前台运行时,告诉用户有新消息的一种途径,是从外部服务器发送到应用程序上的。一般说来,当要显示消息或下载数据的时候,通知是由远程服务器(程序的提供者)发送,然后通过苹果的推送通知服务(Apple Push Notification Service,简称apns)推送到设备的程序上。

推送的新消息可能是一条信息、一项即将到期的日程或是一份远程服务器上的新数据。在系统上展现的时候,可以显示警告信息或在程序icon上显示数字,同时,也可以播放警告音。一旦用户注意到程序有新的信息、时间或是数据,他们可以运行程序并访问新的内容。也可以选择忽略通知,这时程序将不会被激活。

iPhone, iPad和iPod touch上同一时刻只有一个app在前台运行。大多数程序在后台运行的时候,可以对某些用户感兴趣的内容做出回应(定时、或数据等)。推送通知能让程序在这些事件发生的时候通知用户。

作为提供者为程序开发和部署推送通知,必须通过iOS Developer Program Portal获得SSL证书。每个证书限用于一个程序,使用程序的bundle ID作为标识。证书有两种用途的:一种是针对sandbox(用于开发和测试),另外一种针对发布产品。这两种运行环境拥有为各自指定的IP地址并且需要不同的证书。还必须为两种不同的环境获取各自的provisioning profiles。

APNS提供了两项基本的服务:消息推送和反馈服务。

消息推送:使用流式TCP套接字将推送通知作为二进制数据发送给APNs。消息推送有分别针对开发和测试用的sandbox、发布产品的两个接口,每个都有各自的地址和端口。不管用哪个接口,都需要通过TLS或SSL,使用SSL证书来建立一个安全的信道。提供者编制通知信息,然后通过这个信道将其发送给APNs。 注:sandbox:   gateway.sandbox.push.apple.com:219 产品接口:gateway.push.apple.com:2195

反馈服务:可以得到针对某个程序的发送失败记录。提供者应该使用反馈服务周期性检查哪些设备一直收不到通知,不需要重复发送通知到这些设备,降低推送服务器的负担。 注:sandbox:feedback.push.apple.com:2196 产品接口:feedback.sandbox.push.apple.com:2196

二.Apple Push Notification的工作机制

自己的客户端发送消息到自己应用软件的apns,apns发送消息到你的手机。这个是根据令牌识别的。手机在发送消息到你的应用软件。

  • 首先是应用程序注册消息推送。

  • IOS跟APNS Server要deviceToken。应用程序接受deviceToken。

  • 应用程序将deviceToken发送给PUSH服务端程序(Provider)。

  • 服务端程序向APNS服务发送消息。

  • APNS服务将消息发送给iPhone应用程序。

1. Provider是指某个iPhone软件的Push服务器,是我们将要开发的服务器。

2. APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器。

上图可以分为三个阶段:

第一阶段:推送服务器(provider)把要发送的消息、目的iPhone的标识打包,发给APNS;

第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发到iPhone;

第三阶段:iPhone把发来的消息传递给相应的应用程序,并且按照设定弹出Push通知。

三 推送之前的配置

在任何iOS应用开发里面,开发人员总是在团队里面扮演某一角色:团队经理,团队管理员,和团队成员。iPhone开发证书和配置文件由不同角色完成。通常团队经理是团队里面唯一可以创建Development SSL证书(沙箱环境)和Distribution SSL证书(产品环境)的人。而团队经理和团队管理员都可以创建Development和Distribution的配置文件。团队成员只能下载并安装证书和配置文件(!这里要区别好证书和配置文件的概念:证书,即Certificates,它主要是使用Access Key Chain来生成的一个签名密钥。而配置文件,即Provisioning profiles,它主要是根据不同的App来生成的针对性的代码签名文件。二者区别是一般一个开发者账户只能有一个Certificates(而Development一个,Distribtuion一个),但是可以有多个Provisioning profiles文件,且这些Provisioning profiles通常创建的时候都使用同一个Certrificates)。以下部分详细介绍该过程。
注意:iOS Provisioning Portal给所有iOS开发者。

创建SSL证书和密钥
在iOS Dev Center的iOS Provisioning Portal里面,团队经理选中相应APNs的AppID。他需要完成以下步骤来创建SSL证书:
1.
点击窗口左边侧边栏的App IDs。
将会跳转到一个显示当前合法应用IDs的页面。每个应用的ID前面包含了它的Bundle ID,而在Bundle ID之前包含了苹果生成的十位字符串。团队管理员必须输入Bundle ID。对于每个证书,它必须匹配某一特定的Bundle ID,你不能使用“wildcart”的应用ID。即要使用推送通知的应用的Bundle ID不能使用“*”来创建。
2.
找到需要创建SSL证书的应用ID(和Development Provisioning Profile相关),并单击Configure按钮。
你必须确保该App ID下面的苹果推送通知服务栏下面显示“可用”状态才能为该应用配置APNs证书。

3.

在App ID配置页面,检查已经勾选了“Enable for Apple Push Notification service”复选框,并点击下面的“Configure”按钮。
单击该按钮将会启动APNs助理,它会通过一系列步骤来引导完成操作。

4.
第一步要求你启动Keychain Access应用并生成一个Certificate Signing Request(CSR)文件。
下面的介绍来自证书助理。当你完成生成CSR后,点击Keychain Access上面Continue来返回到APNs Assistant。
当你创建一个CSR时,Keychain Access生成一个私有和公有的加密密钥对。而私有的密钥会默认被放入你的Login keychain里面。而公有的密钥包含CSR会发送到配置服务器。当配置服务器把证书发回给你的时候,证书里面的其中一个条目将是公有密钥。
5.
在Submit Certificate Signing Request面板上,单击Choose File。导航到你之前创建的CSR文件的地方并选中它。
6.
单击Generate按钮。
在显示Generate Your Certificate面板的同时,Assistant会配置并生成你的客户端SSL证书。如果生成成功,它将会显示信息“Your APNs Certificate has been generated.”。单击Continue来进入下一步操作。
7.
在下一个面板,单击Download Now按钮来下载证书文件到本地文件夹。找到文件下载的地方并双击证书文件(该证书文件包含一个.cer的扩展名)来把它安装到你的keychain里面。当完成后,单击APNs Assistant上面的Done按钮。
双击加载启动Keychain Access。确保你已经把刚才的证书安装到了你用于开发的电脑的login keychain里面。在Keychain Access里面,确保你的证书使用的ID匹配你应用的Bundle ID。APNs的SSL证书应该安装到你的通知服务上面。
当你返回到iOS Dev Center Portal的Configure App ID页面完成这些步骤后,你的证书应该会变成绿色,并且显示“Enabled”。
为了给产品环境创建一个证书,重复上述步骤,但记住选中产品证书的应用ID。

团队经理或团队管理器接下来必须创建在服务器用于远程通知开发的配置证书(Development 或 Distribution)。配置文件就是一个集合,它囊括了和应用相关的开发者和开发团队验证过的设备并使用这些设备来测试应用程序。配置文件包含了证书、设备标示符、应用的Bundle ID、和所有权利,包括<aps-environment>。所有团队程序需要在运行并测试他们应用代码的设备上面安装该配置文件。
注意:关于创建配置文件的详细解析参考编程用户指南。
为了下载并安装配置文件,团队程序必须完成以下步骤:
1.
进入iOS Dev Center的Provisioning portal页面。
2.
创建一个新的配置文件,并包含你注册用于APNs的App ID。
3.
在你下载这个新的配置文件之前修改任何已存在的配置文件。
你必须修改配置文件的一些细微部分(比如切换选项)来生成一个新的配置文件。如果配置文件并没有“受损(dirtied)”,你不应该给以该原始配置文件任何推送的权利。
4.
找到文件的下载目录,把该配置文件(通常是一个.mobileprovision扩展文件)拖拉到Xcode或iTunes应用程序的图标上面。
可选的,你也可以把配置文件复制到~/Library/MobileDevice/Provisioning Profiles目录。如果当前目录不存在则生成一个新目录。
5.
验证该配置文件的权利是否正确。为此,使用文本编辑器打开.mobileprovision文件。该文件的内容是一个XML的文本。查看在aps-environment键的位置的字典值。对于一个开发模式的配置文件,该值应该是development;而对应发布模式的配置文件,该字符值应该是production。
6.
在Xcode的Organizer窗口,查看Provisioning Profiles部分,并确认证书已经安装到你的设备上面。
当你编译该工程的时候,二进制文件现在使用证书的私有密钥签名。
4.2.3
安装SSL证书和密钥到你的服务器上面
你必须安装SSL发布证书和你之前获取的私有加密密钥到需要运行提供者代码的服务器上,从该服务器连接到APNs的沙箱或产品环境。为此,需要完成以下步骤:

1.
打开Keychain Access实体并单击左边面板的My Certificates类别。
2.
找到你将要安装的证书,并打开相应内容。
你将会看到证书和私有密钥。
3.
选中证书和密钥,选中File > Export Items,并把它们导出个人信息交换文件(.p12)。
4.
如果服务器采用Buby或Perl语言来实现的话,那么它们更容易处理个人信息交换格式的文件。为了把证书转换为该格式,需要完成以下步骤:
a)
在Keychain Access里面,选中相应证书并选择File > Export Items。选择个人信息交换格式选项(.p12)。选择一个保存地址,并单击Save按钮。
b)
加载终端应用,并输入如下的命令:
openssl pkcs12 -in CertificateName.p12 -out CertificateName.pem -nodes
5.
拷贝.pem证书文件到新的电脑并安装它到合适的地方。

这次准备工作已经完成了。

下面是客户端的步骤了

一个应用程序必须在设备(iOS设备或Mac电脑)上面注册了苹果推送通知服务才能接收来自程序提供者的远程通知。
注册过程包含以下三个步骤:

一个应用程序必须在设备(iOS设备或Mac电脑)上面注册了苹果推送通知服务才能接收来自程序提供者的远程通知。
注册过程包含以下三个步骤:
1.
程序调用registerForRemoteNotificationTypes:方法。
2.
委托实现application:didRegisterForRemoteNotificationsWithDeviceToken:方法来接收设备令牌。
3.
把设备令牌作为非对象(二进制值)传递给程序提供者。


1.
程序调用registerForRemoteNotificationTypes:方法。
2.
委托实现application:didRegisterForRemoteNotificationsWithDeviceToken:方法来接收设备令牌。
3.
把设备令牌作为非对象(二进制值)传递给程序提供者。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions   

  • {   

  •     self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];   

  •     // Override point for customization after application launch.   

  •     self.viewController = [[[ViewController alloc] init] autorelease];   

  •     self.window.rootViewController = self.viewController;   

  •     [self.window setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"background.png"]]];   

  •     [self.window makeKeyAndVisible];   

  •     /** 注册推送通知功能, */  

  •     [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];   

  •        

  •     //判断程序是不是由推送服务完成的   

  •     if (launchOptions) {   

  •         NSDictionary* pushNotificationKey = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];   

  •         if (pushNotificationKey) {   

  •             UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"推送通知"    

  •                                                            message:@"这是通过推送窗口启动的程序,你可以在这里处理推送内容"  

  •                                                           delegate:nil    

  •                                                  cancelButtonTitle:@"知道了"    

  •                                                  otherButtonTitles:nil, nil];   

  •             [alert show];   

  •             [alert release];   

  •         }   

  •     }   

  •     return YES;   

  • }   

- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {   

  •     NSString* token = [NSString stringWithFormat:@"%@",deviceToken];   

  •     NSLog(@"apns -> 生成的devToken:%@", token);   

  •     //把deviceToken发送到我们的推送服务器   

  •     DeviceSender* sender = [[[DeviceSender alloc]initWithDelegate:self ]autorelease];   

  •     [sender sendDeviceToPushServer:token ];       

  • }   

- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {   

  •     NSLog(@"apns -> 注册推送功能时发生错误, 错误信息:\n %@", err);   

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo   

  • {   

  •     NSLog(@"\napns -> didReceiveRemoteNotification,Receive Data:\n%@", userInfo);   

  •     //把icon上的标记数字设置为0,   

  •     application.applicationIconBadgeNumber = 0;   

  •     if ([[userInfo objectForKey:@"aps"] objectForKey:@"alert"]!=NULL) {   

  •         UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"**推送消息**"  

  •                                                         message:[[userInfo objectForKey:@"aps"] objectForKey:@"alert"]   

  •                                                        delegate:self   

  •                                               cancelButtonTitle:@"关闭"  

  •                                               otherButtonTitles:@"处理推送内容",nil];   

  •         alert.tag = alert_tag_push;   

  •         [alert show];   

  •     }   

  • }   

至此已经可以了,如果没有服务器端可以参考pushMebaby,本博客也有介绍


转载于:https://my.oschina.net/iWage/blog/538425

相关文章:

redis(4)

redis-cli -p 6380redis-cli -p 6379 info server | grep run_idpsync &#xff1f; -1

PHP也玩并发,巧用curl 并发减少后端访问时间

说明&#xff1a;本人源自3篇博文 http://blog.csdn.net/zuiaituantuan/article/details/7048782首先&#xff0c;先了解下 php中的curl多线程函数&#xff1a;# curl_multi_add_handle# curl_multi_close# curl_multi_exec# curl_multi_getcontent# curl_multi_info_read# cur…

ADSL自动更换IP地址源代码

有些网站限制IP地址&#xff0c;什么一个IP地址只能一次之类的。特别是投票网址&#xff0c;为了防止刷票&#xff0c;限制1个IP只允许投票一次&#xff01; 此程序采用Vs2010C#开发&#xff0c;提供全部源代码&#xff01;方便程序猿朋友二次开发&#xff01; 可以后台运行&am…

安全隐患:神经网络可以隐藏恶意软件

编译 | 禾木木 出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09; 凭借数百万和数十亿的数值参数&#xff0c;深度学习模型可以做到很多的事情&#xff0c;例如&#xff0c;检测照片中的对象、识别语音、生成文本以及隐藏恶意软件。加州大学圣地亚哥分校和伊利诺伊大学…

实现一个完美符合Promise/A+规范的Promise

原文在我的博客中&#xff1a;原文地址 如果文章对您有帮助&#xff0c;您的star是对我最好的鼓励&#xff5e; 简要介绍&#xff1a;Promise允许我们通过链式调用的方式来解决“回调地狱”的问题&#xff0c;特别是在异步过程中&#xff0c;通过Promise可以保证代码的整洁性和…

用递归法计算斐波那契数列的第n项

斐波纳契数列&#xff08;Fibonacci Sequence&#xff09;又称黄金分割数列&#xff0c;指的是这样一个数列&#xff1a;1、1、2、3、5、8、13、21、……在数学上&#xff0c;斐波纳契数列以如下被以递归的方法定义&#xff1a;F00&#xff0c;F11&#xff0c;FnF(n-1)F(n-2)&a…

ArrayList的内存泄露

2019独角兽企业重金招聘Python工程师标准>>> 大家先运行下下面这段代码&#xff0c;看看结果 public class MemoryLeak {public static void main(String[] args) throws InterruptedException {new Thread(new Runnable() {Overridepublic void run() {for (int i …

给 Python 初学者推荐的 IDE 哦!

作者 | 黄伟呢来源 | 数据分析与统计学之美总有一些Python初学者&#xff0c;会问到&#xff1a;学习Python&#xff0c;应该用什么Python IDE&#xff1f;了解到他们使用Python做什么之后&#xff0c;我总结了这篇文章。IDE是集成开发环境的缩写&#xff0c;通俗地说&#xff…

django 2.0路由配置变化

urlpatterns变量​​的语法 urlpatterns应该是path()和/或re_path()实例的Python列表。 首先&#xff0c;Django会使用根路由解析模块(root URLconf)来解析路由。 通常&#xff0c;这是ROOT_URLCONF设置的值&#xff0c;但是如果传入的HttpRequest对象具有urlconf属性&#xff…

用ext_skel,实现一个PHP扩展,添加到PHP并调用

http://www.shinrun.com/PHP 一、开始之前 1. 系统环境&#xff1a;FreeBSD 8.22. AP环境&#xff1a;即已经装好的Apache2.2.17、PHP5.3.8环境3. PHP源码&#xff1a;下载稳定版本源码到当前用户的目录&#xff0c;如&#xff0c;下载PHP 5.3.8到/usr/home/abc下。4. 其它要求…

关于第三方IOS的checkBox框架的使用

关于第三方IOS的checkBox框架的使用 这个框架是从github上下载获取的&#xff1a;M13Checkbox。 只是github的源码项目工程比较久远&#xff0c;所以我把代码部分拷贝到XCode 7.1.0新建的项目里。 下面是展示效果 客户端源码使用参考&#xff1a; 1 #import "ViewControll…

20 个 Pandas 数据实战案例,干货多多

作者 | 俊欣来源 | 关于数据分析与可视化今天我们讲一下pandas当中的数据过滤内容&#xff0c;小编之前也写过也一篇相类似的文章&#xff0c;但是是基于文本数据的过滤&#xff0c;大家有兴趣也可以去查阅一下。下面小编会给出大概20个案例来详细说明数据过滤的方法&#xff0…

Python创建和访问字典

>>> dict1 {a:1,b:2,c:3,d:4}>>> print(a的值是:,dict1[a])a的值是: 1>>> dict4 dict(我 快乐, 你 伤悲)SyntaxError: keyword cant be an expression>>> dict4[你] 改变悲伤>>> dict4{我: 快乐, 你: 改变悲伤}>>>…

C语言九阴真经

发现记忆力越来越差&#xff0c;所以干脆搞这么一个东西&#xff0c;就是把C语言的最常用的语法汇编在一起&#xff0c;不断完善。这样以后只要经常把这个回顾一下就可以了。不然去翻书太多了。。。f.h#define Area 1000 struct student{char *last_name;int student_id;char …

听障人士的“有声桥梁”:百度智能云曦灵-AI手语平台发布

在刚刚落幕的冰雪赛事中&#xff0c;百度智能云曦灵为央视新闻打造的AI手语主播正式上岗&#xff0c;她以流畅、专业的手语服务实时传递冰雪运动的激情。然而在日常生活中&#xff0c;听障人士想要方便地获取信息仍面临着众多困难&#xff0c;无障碍窗口稀缺的问题亟待解决。 …

模拟实现: strstr strcpy strlen strcat strcmp memcpy memmove

模拟实现&#xff1a;strstrstrcpystrlenstrcatstrcmpmemcpymemmove1 strstr 字符串中查找子字符串char * my_strstr(const char *dest, const char *src) {const char *ret dest;const char *p dest;const char *q src;assert(dest ! NULL && src ! NULL); while(r…

【Spring Security】五、自定义过滤器

在之前的几篇security教程中&#xff0c;资源和所对应的权限都是在xml中进行配置的&#xff0c;也就在http标签中配置intercept-url&#xff0c;试想要是配置的对象不多&#xff0c;那还好&#xff0c;但是平常实际开发中都往往是非常多的资源和权限对应&#xff0c;而且写在配…

一星期没完成Ansible任务

这个星期&#xff0c;前4天&#xff0c;我在看Nginx&#xff0c;没有深入Ansible。 这几天我有思考做Ansible的哪个方面&#xff0c;现在我用Ansible可以用&#xff0c;但是没有生产环境&#xff0c;我对基础部分热情不是特别大&#xff0c;应该是基础部分大家在弄&#xff0c;…

Python 批量处理 Excel 数据后,导入 SQL Server

作者 | 老表来源 | 简说Python1、前言2、开始动手动脑2.1 拆解明确需求2.2 安装第三方包2.3 读取excel数据2.4 特殊数据数据处理2.5 其他需求2.6 完整调用代码1、前言今天教大家一个需求&#xff1a;有很多Excel&#xff0c;需要批量处理&#xff0c;然后存入不同的数据表中。2…

最经典的计算机网络新书推荐--计算机网络(第5版)Tanenbaum著中文版上市

作者&#xff1a;Tanenbaum是全球最著名的计算机科学家。linux之父Linus当年就是参考Tanenbaum写的MINIX&#xff01; Tanenbaum《计算机网络(第5版) 》《现代操作系统(第3版) 》《操作系统设计与实现(第3版) 》《分布式系统原理与范型(第2版) 》《计算机组成结构化方法&#x…

elasticsearch简单操作(二)

让我们建立一个员工目录&#xff0c;假设我们刚好在Megacorp工作&#xff0c;这时人力资源部门出于某种目的需要让我们创建一个员工目录&#xff0c;这个目录用于促进人文关怀和用于实时协同工作&#xff0c;所以它有以下不同的需求&#xff1a;1、数据能够包含多个值的标签、数…

苹果放大招?「廉价版」5G iPhone 将揭晓,M2芯片来袭?

整理 | 张洁来源 | CSDN3 月 2 日&#xff0c;苹果公司正式宣布将于北京时间 3 月 9 日凌晨 2 点举办 2022 年的首场活动&#xff0c;主题为“peek performance&#xff08;高能传送&#xff09;”。与去年一样&#xff0c;苹果 2022 年的第一场活动将继续以线上的方式进行。活…

PHP 预编译加速: eAccelerator的安装和性能比较

eAccelerator已经是很常用的PHP平台预编译加速的手段了。今天在自己机器上尝试安装了一下&#xff0c;备忘如下&#xff1a; 获得源代码&#xff1a;http://bart.eaccelerator.net/source/编译&#xff1a;需要有autoconf支持&#xff0c;解包后在源程序目录下&#xff1a;/usr…

合并区间(LintCode)

合并区间 给出若干闭合区间&#xff0c;合并所有重叠的部分。 样例 给出的区间列表 > 合并后的区间列表&#xff1a; [ [[1, 3], [1, 6],[2, 6], > [8, 10],[8, 10], [15, 18][15, 18] ] ]挑战 O(…

Kylin集群部署和cube使用

Kylin集群部署和cube使用 安装集群环境节点 Kylin节点模式 Ip 内存 磁盘Node1 All 192.167.71.11 2G 80GNode2 query 192.168.71.12 1.5G 80GNode3 query 192.168.71.13 1.5G 80GKylin工作原理如下&#xff1a; 集群时间同步Ntp服务自行设置安装kylin之前所需要的环境Hadoop-2.…

就是个控制结构,Scala 能有什么新花样呢?

作者 | luanhz来源 | 小数志导读编程语言中最为基础的一个概念是控制结构&#xff0c;几乎任何代码都无时无刻不涉及到&#xff0c;其实也就无外乎3种&#xff1a;顺序、分支和循环。本文就来介绍Scala中控制结构&#xff0c;主要是分支和循环。Scala中的控制结构实质上与其他编…

快速开发一个PHP扩展

快速开发一个PHP扩展 作者&#xff1a;heiyeluren时间&#xff1a;2008-12-5博客&#xff1a;http://blog.csdn.net/heiyeshuwu 本文通过非常快速的方式讲解了如何制作一个PHP 5.2 环境的扩展&#xff08;PHP Extension&#xff09;&#xff0c;希望能够在图文的方式下让想快速…

oracle11g的安装

目录层次&#xff1a;linux->oracle软件->dbca数据库安装过程&#xff1a;虚拟机->linux->VMtools->拷贝数据库软件->创建一个目录mkdir->创建组.用户->修改根目录->设置参数->解压 >安装->oracle完成参考&#xff1a;安装oracle软件linu…

python 100例(10)

2019独角兽企业重金招聘Python工程师标准>>> 题目&#xff1a;古典问题&#xff1a;有一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子&#xff0c;小兔子长到第三个月后每个月又生一对兔子&#xff0c;假如兔子都不死&#xff0c;问每个月的兔子总数为多…

cocos2dx-3.9 集成admob

Part 1: 安装GoogleMobileAds framework &#xff08;即admob&#xff09; 1. 安装Cocoapods&#xff0c;否则解决依赖关系和配置真的会把人不累死也得烦死 sudo gem install cocoapods 国内用户安装过程中可能遇到SSL连接出错的问题&#xff0c;请参考 Cocoapod安装过程中的幺…