阿里云和腾讯云对比云服务器哪家比较好一点?

因工作关系,两家都有接触,也推荐过客户用过两家的服务器及其他云资源,今天从市场、性能、服务、价格等几个方面简单聊聊:

%title插图%num

阿里云:成立于2009年,国内*家云计算平台,也是目前国内*大、全球第三的云计算品牌;阿里云*早虽然独立立项,但阿里云的建立初期已经明确目标,那就是为阿里巴巴集团内部及服务于阿里体系的用户提供服务,包括淘系众多的第三方isv提供方,所以阿里云*早、*成熟的解决方案是基于电商行业的解决方案;2013年阿里云开启全面商业化,重点围绕B端客户进行解决方案建设;目前阿里已经形成了包含200多套解决方案的综合性云平台;国内市场占有率大约在43%,2020财年前三季度营收270亿人民币左右。

腾讯云:腾讯云开始没有独立立项,*早孵化于QQ所在的事业群,是的你没看错,就是汤道生所在的事业群。2013年腾讯云也开启了商业化之路,但腾讯云的商业化很保守,基本都是为自身的部分业务及中小型isv提供服务,并未开始全面推广;腾讯云真正开始推广是在2016年,那一年马化腾为腾讯云站台,集团也开始推广腾讯云;腾讯云进入全面发展是在2018年930之后,汤道生掌舵腾讯云,腾讯云进入飞速发展阶段。腾讯凭借多年在科技及文娱行业积累的技术优势,为云平台的快速发展提供了支撑。腾讯云的优势解决方案以音视频、游戏、金融、政务等行业解决方案为主;腾讯云目前国内市场占有率约为18.5%,2019年腾讯云营收超过170亿元。

性能:之前我同事用过两家1核2G的服务器做过压测,在该配置下腾讯云性能略优于阿里云,特别是1核2G运行windows server2012,腾讯云更加流畅;我们公司业务以Saas化应用为主,从阿里云和腾讯云*新一代的服务器使用情况来看,两家性能相差不大,腾讯云在windows操作系统优化上略好于阿里云。

服务:云服务商的服务方式以智能客服、工单、客服电话和生态服务为主,智能客服两家半斤八两,谁都不说谁,答非所问,超级难用;工单这块阿里云工单回复速度略快于腾讯云;客服电话这块其实也是扯淡,阿里云的客服电话直接问你要账号,向你推荐产品;生态服务大客户专享,小客户都得不到太多支持。

价格:这块腾讯云在价格方面占优,服务器价格整体比阿里云略低5-10%,并且腾讯云的新用户活动和企业活动较多,推出的配置也比较高,满足了大部分中小企业的上云需求;阿里云虽然也有活动,但阿里云活动已服务中小用户为主,配置相对较低。

腾讯云*近推出的“助力企业复工”活动,活动力度巨大,部分服务器低至1折,*高可选16核32G的服务器,3年不足一万元,对于常用的机型如1核2G1M带宽仅99元/年,此次活动也推出了针对企业级用户的活动,例如4核8G5M带宽仅需1099元/年,这两种机型可以满足个人学习或中小企业部署小型业务系统使用。

活动链接可参考:精选秒杀 – 腾讯云1核2G1M低至99元/年

作为国内云计算行业的老大,阿里云也推出了一系列的优惠活动,阿里云年中特惠活动力度还蛮大。

活动链接可参考:上云必备—新用户专享优惠 ECS102元/年​

特别提醒:阿里云的活动不建议购买突发性能实例,该类型实例为cpu受限实例,仅限于测试环境,用于生产环境会影响用户体验。

sql server 2008登陆服务器名称是什么

说明:这是我遇到的一个问题,折磨了很久。

我在网上搜索了半天,大部分人都说是local,localhost,单独的一个点,127.0.0.1等,当时我尝试了都不行,*后再百度知道里找到,用.\myexpress试试,我一试,果然成功了,非常感谢。于是写出来分享给大家

通常情况下,有两种情况,
单独一个小数点
小数点\sqlexpress
不管是什么名称,下面都选择Windows认证

服务器名称查找,具体请在开始-运行那里,输入services.msc回车,在服务里面查找sql server开头的服务,看到底是什么名称

防止SQL注入攻击

using System;
using System.Text;
using System.Runtime.InteropServices;

namespace DBGrep
{
public class SqlLocator
{
[DllImport(“odbc32.dll”)]
private static extern short SQLAllocHandle(short hType, IntPtr inputHandle, out IntPtr outputHandle);
[DllImport(“odbc32.dll”)]
private static extern short SQLSetEnvAttr(IntPtr henv, int attribute, IntPtr valuePtr, int strLength);
[DllImport(“odbc32.dll”)]
private static extern short SQLFreeHandle(short hType, IntPtr handle); [DllImport(“odbc32.dll”, CharSet = CharSet.Ansi)]
private static extern short SQLBrowseConnect(IntPtr hconn, StringBuilder inString, short inStringLength, StringBuilder outString, short outStringLength, out short outLengthNeeded);
private const short SQL_HANDLE_ENV = 1;
private const short SQL_HANDLE_DBC = 2;
private const int SQL_ATTR_ODBC_VERSION = 200;
private const int SQL_OV_ODBC3 = 3;
private const short SQL_SUCCESS = 0;
private const short SQL_NEED_DATA = 99;
private const short DEFAULT_RESULT_SIZE = 1024;
private const string SQL_DRIVER_STR = “DRIVER=SQL SERVER”;

public static string[] GetServers()
{
string[] retval = null;
string txt = string.Empty;
IntPtr henv = IntPtr.Zero;
IntPtr hconn = IntPtr.Zero;
StringBuilder inString = new StringBuilder(SQL_DRIVER_STR);
StringBuilder outString = new StringBuilder(DEFAULT_RESULT_SIZE);
short inStringLength = (short)inString.Length;
short lenNeeded = 0;
try
{
if (SQL_SUCCESS == SQLAllocHandle(SQL_HANDLE_ENV, henv, out henv))
{
if (SQL_SUCCESS == SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (IntPtr)SQL_OV_ODBC3, 0))
{
if (SQL_SUCCESS == SQLAllocHandle(SQL_HANDLE_DBC, henv, out hconn))
{
if (SQL_NEED_DATA == SQLBrowseConnect(hconn, inString, inStringLength, outString, DEFAULT_RESULT_SIZE, out lenNeeded))
{
if (DEFAULT_RESULT_SIZE < lenNeeded)
{
outString.Capacity = lenNeeded;
if (SQL_NEED_DATA != SQLBrowseConnect(hconn, inString, inStringLength, outString, lenNeeded, out lenNeeded))
{
throw new ApplicationException(“Unabled to aquire SQL Servers from ODBC driver.”);
}
}
txt = outString.ToString();
int start = txt.IndexOf(“{“) + 1;
int len = txt.IndexOf(“}”) – start;
if ((start > 0) && (len > 0))
{
txt = txt.Substring(start, len);
}
else
{
txt = string.Empty;
}
}
}
}
}
}
catch (Exception ex)
{
#if(DEBUG)
Console.WriteLine(“Acquire SQL Servier List Error” + (ex.Message).ToString());
#endif
txt = string.Empty;
}
finally
{
if (hconn != IntPtr.Zero)
{
SQLFreeHandle(SQL_HANDLE_DBC, hconn);
}
if (henv != IntPtr.Zero)
{
SQLFreeHandle(SQL_HANDLE_ENV, hconn);
}
}
if (txt.Length > 0)
{
retval = txt.Split(“,”.ToCharArray());
}
return retval;
}
}
}

2、利用SQLDMO(SQL Distributed Management Objects)
SQLDMO来自SQL Server自带的SQLDMO.dll,其本身是一个COM对象,可以在.net工程中添加对它的应用。然后可以得到以下对象:
SQLDMO.Application
SQLDMO.SQLServer
SQLDMO.Database
SQLDMO.NameList

得到SQL服务器的列表
SQLDMO.Application sqlApp = new SQLDMO.ApplicationClass();
SQLDMO.NameList sqlServers = sqlApp.ListAvailableSQLServers();
for(int i=0;i<sqlServers.Count;i++)
{
object srv = sqlServers.Item(i + 1);
if(srv != null)
{
this.cboServers.Items.Add(srv);
}
}
if(this.cboServers.Items.Count > 0)
this.cboServers.SelectedIndex = 0;
else
this.cboServers.Text = “<No available SQL Servers>”;
记住COM的集合从指针1开始,不是0

得到数据库列表
SQLDMO.Application sqlApp = new SQLDMO.ApplicationClass();
SQLDMO.SQLServer srv = new SQLDMO.SQLServerClass();
srv.Connect(this.cboServers.SelectedItem.ToString(),this.txtUser.Text,this.txtPassword.Text);
foreach(SQLDMO.Database db in srv.Databases)
{
if(db.Name!=null)
this.cboDatabase.Items.Add(db.Name);
}

利用c#实现对sql server的信息探测

1、原理简述
对于SQL Server2000来说,打开SQL Server客户端准备连接,当拉开服务器列表的时候,整个局域网所有的SQL Server服务器都被列出来了。这是为什么呢?
原理如下:
从我自己的机器(192.168.0.1)上从1434端口广播(192.168.0.255)了这个UDP包,然后,整个局域网中的SQL Server服务器都开始响应这个UDP数据包,所有这些都是明文传输的,我们可以很容易探测一个IP地址的1434端口,获得该IP地址上运行的SQL Server的相关信息。
这些信息包括:主机名称、实例名称、版本、管道名称以及使用的端口等。这个端口是微软自己使用,而且不象默认的1433端口那样可以改变,1434是不能改变的。
2、程序实现
下面是一个利用1434进行探测的c#程序,核心代码如下(很简单,呵呵) :
using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
namespace ConsoleApplication3
{
class Class1
{
//创建一个UDPCLIENT实例
private static UdpClient m_Client;
//LISTEN用来获取返回的信息
public static string Listen(string hostip)
{
string HostIP = hostip;
IPAddress thisIP = IPAddress.Parse(HostIP);
IPEndPoint host = new IPEndPoint(thisIP,1434);
byte [] data = m_Client.Receive(ref host);
Encoding ASCII = Encoding.ASCII;
String strData = ASCII.GetString(data);
return strData;
}
//SEND
public static void Send(string hostip)
{
string HostIP = hostip;
byte [] buffer = {02};
//02为要发送的数据,只有02、03、04有回应
int ecode = m_Client.Send(buffer,1,HostIP,1434);
//ecode用来返回是否成功发送
if(ecode <= 0)
{
Console.WriteLine(“发送时出错:” + ecode);
}
}
//对返回的信息的简单的处理
public static void OutputInfo(string strdata)
{
string str = strdata;
//str.le
char [] that = {‘;‘,‘;‘};
string [] strofthis =str.Split(that);
//int i= 0 ;
for(int i=0;i
{
Console.Write(strofthis[i]);
Console.Write(‘/n‘);
}

}
//输入IP
public static string InputHostIP()
{
Console.Write(“enter the ip you want to scan:/n/n”);
string hostip =Console.ReadLine();
Console.Write(‘/n‘);
return hostip;
}
//EXIT
public static void Exit()
{
Console.WriteLine(“if you want to exit ,just input 1/n”);
int a = Console.Read();
if(a!= 1)
{
Console.WriteLine(“if you want to exit ,just input 1/n”);
Console.Read();
}
else
{
}
}
[STAThread]

static void Main(string[] args)
{
string HostIP;
HostIP = InputHostIP();
Console.WriteLine(“Begin to send udp to the host”);
m_Client = new UdpClient();
Send(HostIP);
string strData=Listen(HostIP);
OutputInfo(strData);
Exit();
}
}
}
3一个典型的返回的信息
ServerName;AWEN;
InstanceName;AWEN;
IsClustered;No;
Version;8.00.194;
tcp;1044; (TCP的端口,可见就算改了端口也是很容易找到的)
np;//AWEN/pipe/MSSQL$XHT310/sql/query;

sqlserver 查看服务器名称以及sql语句跨服务器查询设置

print ‘服务器的名称:’+@@SERVERNAME

print ‘SQL Server的版本’ + @@VERSION

SELECT @@SERVERNAME as ‘服务器名称’

select @@VERSION as ‘SQL Server的版本’

数据库跨服务器设置:

EXEC  sp_addlinkedserver
@server=’userdb’,   –链接服务器别名
@srvproduct=”,
@provider=’SQLOLEDB’,
@datasrc=’127.0.0.1′  –要访问的的数据库所在的服务器的ip
GO
EXEC sp_addlinkedsrvlogin
‘userdb’,                  –链接服务器别名
‘false’,
NULL,
‘sa’,                     –要访问的数据库的用户
‘xxxx’                    –要访问的数据库,用户的密码

GO

查询的时候:

select * from 服务器名称.数据库.dbo.表名

web服务器和应用服务器的区别与分析

web服务器和应用服务器
前导:要想了解web服务器和应用服务器,必须要了解什么是web服务器、什么是应用服务器,以及二者之间的区别和联系

web服务器(Web Server)
理解WEB服务器,首先你要理解什么是WEB?
WEB你可以简单理解为你所看到的HTML页面就是WEB的数据元素,处理这些数据元素的应用软件就叫WEB服务器,如apache、IIS。 WEB服务器与客户端打交道,它要处理的主要信息有:session、request、response、HTML等
Web服务器可以解析(handles)HTTP协议。当Web服务器接收到一个HTTP请求(request),会返回一个HTTP响应(response),例如送回一个HTML页面。为了处理一个请求(request),Web服务器可以响应(response)一个静态页面或图片,进行页面跳转(redirect),或者把动态响应(dynamic response)的产生委托(delegate)给一些其它的程序例如CGI脚本,JSP(JavaServer Pages)脚本,servlets,ASP(Active Server Pages)脚本,服务器端(server-side)JavaScript,或者一些其它的服务器端(server-side)技术。无论它们(译者注:脚本)的目的如何,这些服务器端(server-side)的程序通常产生一个HTML的响应(response)来让浏览器可以浏览。
要知道,Web服务器的代理模型(delegation model)非常简单。当一个请求(request)被送到Web服务器里来时,它只单纯的把请求(request)传递给可以很好的处理请求(request)的程序(译者注:服务器端脚本)。Web服务器仅仅提供一个可以执行服务器端(server-side)程序和返回(程序所产生的)响应(response)的环境,而不会超出职能范围。服务器端(server-side)程序通常具有事务处理(transaction processing),数据库连接(database connectivity)和消息(messaging)等功能
虽然Web服务器不支持事务处理或数据库连接池,但它可以配置(employ)各种策略(strategies)来实现容错性(fault tolerance)和可扩展性(scalability),例如负载平衡(load balancing),缓冲(caching)。集群特征(clustering—features)经常被误认为仅仅是应用程序服务器专有的特征。

应用服务器:
应用服务器,如JSP,处理的是非常规性WEB页面(JSP文件),它动态生成WEB页面,生成的WEB页面在发送给客户端(实际上当应用服务器处理完一个JSP请求并完成JSP生成HTML后它的任务就结束了,其余的就是WEB处理的过程了)
根据定义,作为应用程序服务器,它通过各种协议,可以包括HTTP,把商业逻辑暴露给(expose)客户端应用程序。Web服务器主要是处理向 浏览器发送HTML以供浏览,而应用程序服务器提供访问商业逻辑的途径以供客户端应用程序使用。应用程序使用此商业逻辑就像你调用对象的一个方法(或过程 语言中的一个函数)一样。
应用程序服务器的客户端(包含有图形用户界面(GUI)的)可能会运行在一台PC、一个Web服务器或者甚至 是其它的应用程序服务器上。在应用程序服务器与其客户端之间来回穿梭(traveling)的信息不仅仅局限于简单的显示标记。相反,这种信息就是程序逻 辑(program logic)。 正是由于这种逻辑取得了(takes)数据和方法调用(calls)的形式而不是静态HTML,所以客户端才可以随心所欲的使用这种被暴露的商业逻辑。
在大多数情形下,应用程序服务器是通过组件(component)的应用程序接口(API)把商业逻辑暴露(expose)(给客户端应用程序)的,例 如基于J2EE(Java 2 Platform, Enterprise Edition)应用程序服务器的EJB(Enterprise JavaBean)组件模型。此外,应用程序服务器可以管理自己的资源,例如看大门的工作(gate-keeping duties)包括安全(security),事务处理(transaction processing),资源池(resource pooling), 和消息(messaging)。就象Web服务器一样,应用程序服务器配置了多种可扩展(scalability)和容错(fault tolerance)技术。

简而言之,应用服务器处理业务逻辑,web服务器则主要是让客户可以通过浏览器进行访问,处理HTML文件,web服务器通常比应用服务器简单。
WEB服务器:Apache、IIS、Nginx(也是反向代理服务器)
应用服务器:Tomcat、Weblogic、Jboss

一 web服务器
1.Apache服务器
Apache是世界使用排名*的Web服务器。它可以运行在几乎所有广泛使用的计算机平台上。源于NCSAhttpd服务器,经过多次修改,成为世界上*流行的Web服务器软件之一。Apache取自“a patchy server”的读音,意思是充满补丁的服务器,Apache有多种产品,可以支持SSL技术,支持多个虚拟主机。Apache是以进程为基础的结构,进程要比线程消耗更多的系统开支,不太适合于多处理器环境,因此,在一个Apache Web站点扩容时,通常是增加服务器或扩充群集节点而不是增加处理器。
Apache web服务器软件拥有以下特性:
1、支持*新的HTTP/1.1通信协议
2、拥有简单而强有力的基于文件的配置过程
3、支持通用网关接口
4、支持基于IP和基于域名的虚拟主机
5、支持多种方式的HTTP认证
6、集成Perl处理模块
7、集成代理服务器模块
8、支持实时监视服务器状态和定制服务器日志
9、支持服务器端包含指令(SSI)
10、支持安全Socket层(SSL)
11、提供用户会话过程的跟踪
12、支持FastCGI
更多Apache相关信息请参考Apache入门篇1、Apache入门篇3

2.IIS web服务器
IIS是Internet Information Services的缩写,是一个World Wide Web server。Gopher server和FTP server全部包容在里面。 IIS意味着你能发布网页,并且有ASP(Active Server Pages)、JAVA、VBscript产生页面,有着一些扩展功能。IIS支持一些有趣的东西,像有编辑环境的界面(FRONTPAGE)、有 全文检索功能的(INDEX SERVER)、有多媒体功能的(NET SHOW) 其次,IIS是随Windows NT Server 4.0一起提供的文件和应用程序服务器,是在Windows NT Server上建立Internet服务器的基本组件。它与Windows NT Server完全集成,允许使用Windows NT Server内置的安全性以及NTFS文件系统建立强大灵活的Internet/Intranet站点。IIS(Internet Information Server,互联网信息服务)是一种Web(网页)服务组件,其中包括Web服务器、FTP服务器、NNTP服务器和SMTP服务器,分别用于网页浏览、文件传输、新闻服务和邮件发送等方面,它使得在网络(包括互联网和局域网)上发布信息成了一件很容易的事。
IIS应用服务器有以下特性:
IIS 6.0 与Windows Server 2003为网络应用服务器的管理提供了许多新的特性,包括实用性、可靠性、安全性与可扩展性。IIS 6.0也增强了开发和国际化支持,Windows Server 2003和IIS 6.0为您提供了一整套*可靠、高效、连接的一体化网络应用解决方案。微软自带的产品,操作简单,下一步下一步就可以完成架设了。

二 应用服务器
1.Tomcat应用服务器
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,它运行时占用的系统资源小,扩展性好,支持负载平衡与邮件服务等开发应用系统常用的功能;Tomcat 部分是Apache 服务器的扩展,但它是独立运行的,所以当你 运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。
更多Tomcat相关信息请参考Tomcat快速入门

2.Weblogic应用服务器
Web Logic是美国bea公司出品的一个application server确切的说是一个基于Javaee架构的中间件,BEA Web Logic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。将Java的动态功能和Java Enterprise标准的安全性引入大型网络应用的开发、集成、部署和管理之中,是用来构建网站的必要软件,拥有解析发布网页等功能,它是用纯java开发的。
Web logic应用服务器有以下优点:
1对业内多种标准的全面支持,包括EJB、JSB、JMS、JDBC、XML和WML,使Web应用系统的实施更为简单,并且保护了投资,同时也使基于标准的解决方案的开发更加简便。
2无限的可扩展性BEA Web Logic Server以其高扩展的架构体系闻名于业内,包括客户机连接的共享、资源pooling以及动态网页和EJB组件群集。
3、凭借对EJB和JSP的支持,以及BEA Web Logic Server 的Servlet组件架 构体系,可加速投放市场速度。这些开放性标准与Web Gain Studio配合时,可简化开发,并可发挥已有的技能,迅速部署应用系统。
4、BEA Web Logic Server的特点是与*数据库、操作系统和Web服务器紧密集成。
5、关键任务可靠性其容错、系统管理和安全性能已经在全球数以千记的关键任务环境中得以验证。
6、BEA Web Logic Server简化了可移植及可扩展的应用系统的开发,并为其它应用系统和系统提供了丰富的互操作性。凭借其出色的群集技术,BEA Web Logic Server拥有*高水平的可扩展 性和可用性。BEA Web Logic Server既实现了网页群集,也实现了EJB组件 群集,而且不需要任何专门的硬件或操作系统支持。网页群集可以实现透明的复制、负载平衡以及表示内容容错,如Web购物车;组件群集则处理复杂的复制、负载平衡和EJB组件容错,以及状态对象(如EJB实体)的恢复。 无论是网页群集,还是组件群集,对于电子商务解决方案所要求的可扩展性和可用性都是至关重要的。共享的客户机/服务器和数据库连接以及数据缓存和EJB都增强了性能表现。这是其它Web应用系统所不具备的。

3.JBoss应用服务器
简介:
JBoss 是一套开源的企业级 Java 中间件系统,用于实现基于 SOA 的企业应用和服务。是一个运行EJB的J2EE应用服务器。它是开放源代码的项目,遵循*新的J2EE规范。从JBoss项目开始至今,它已经从一个EJB容器发展成为一个基于的J2EE的一个web操作系统(operating systemfor web),它体现了J2EE规范中*新的技术,含有Jsp和Servlet容器,也就可以做web容器,也包含 EJB容器,是完整的J2EE应用服务器。JBoss 是*受欢迎而且功能*为强大的应用服务器。*新版本的JBoss AS 7.0.2已经发布。
JBoss应用服务器有以下优点:
1、JBoss是免费的,开放源代码J2EE的实现,通过LGPL许可证进行发布。但同时也有闭源的,开源和闭源流入流出的不是同一途径。
2、JBoss需要的内存和硬盘空间比较小。
3、安装便捷:解压后,只需配置一些环境变量即可。
4、JBoss支持”热部署”,部署BEAN时,只拷贝BEAN的JAR文件到部署路径下即可自动加载它;如果有改动,也会自动更新。
5、JBoss与Web服务器在同一个Java虚拟机中运行 ,Servlet调用EJB不经过网络,从而大大提高运行效率,提升安全性能。
6、用户可以直接实施J2EE-EAR,而不是以前分别实施EJB-JAR和Web-WAR,非常方便。
7、Jboss支持集群。

对以上几种服务器的对比和分析
Apache是一个静态页面,而Tomcat是一个动态页面。如果想要在Apache环境下运行jsp动态网页就需要一个jsp解释器,而tomcat就是作为一个解释器来帮助在Apache的环境下执行动态页面,Tomcat是先将静态页面转换成java.类型文件,然后再以jsp.的文件形式运行。但是在处理静态页面时,tomcat就不如Apache那样迅速,也不能对其进行配置。因此在现实的使用中,Tomcat是做为一Apache的一个插件来显示网站的动态页面的。这样可以使网站具有更好的扩展性和安全性。
JBoss Web将Tomcat内核作为其Servlet容器引擎,Tomcat,在很活动连接支持、静态内容、大文件和HTTPS处理上表象不如JBoss那样优越,同时Tomcat只能运行Java应用程序,企业在使用时Tomcat,这样在使用时需要用Apache Web Server进行整合。
Apache支持所有的操作系统,可以跨平台使用,而IIS只能基于windows操作系统下使用,在开源代码方面Apache是完全开放免费的,而IIS是部分开源代码。在安装上IIS使用的是傻瓜式图形化安装,使用者只要按照提示安装即可,但是Apache在安装上相对复杂困难,在语言的运行方面Apache支持多种运行语言例如ASP,PHP,JSP,但是IIS如果要运行PHP需要反复的配置才能运行。

iOS – 底层知识学习之路-Mach-o 、lldb、 dyld

MACHO与重定位符号表认识。
命令回顾

1. 将源代码编译成可执行文件命令

Clang 文件名 -o 输出的可执行文件名称

如:
Clang test.m – o test
2. 查看可执行文件的代码段命令

objdump –macho -d 可执行文件名称

如:
objdump –macho -d test
3. 将文件编译生.o文件命令

clang -c 文件名 -o .o文件名
如:
clang -c test.m -o test.o
4. 分析.o文件的代码段命令

源码:

void test() {

}
void test_1() {

}
int global = 10;
int main(){
global = 21;
global = 20;
test();
test_1();
return 0;
}
执行:

objdump –macho -d test.o
结果:

(__TEXT,__text) section
_test:
0: 55 pushq %rbp
1: 48 89 e5 movq %rsp, %rbp
4: 5d popq %rbp
5: c3 retq
6: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:_test(%rax,%rax)
_test_1:
10: 55 pushq %rbp
11: 48 89 e5 movq %rsp, %rbp
14: 5d popq %rbp
15: c3 retq
16: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:_test(%rax,%rax)
_main:
20: 55 pushq %rbp
21: 48 89 e5 movq %rsp, %rbp
24: 48 83 ec 10 subq $16, %rsp
28: c7 45 fc 00 00 00 00 movl $_test, -4(%rbp)
2f: c7 05 fc ff ff ff 15 00 00 00 movl $21, _global-4(%rip)
39: c7 05 fc ff ff ff 14 00 00 00 movl $20, _global-4(%rip)
43: e8 00 00 00 00 callq _test
48: e8 00 00 00 00 callq _test_1
4d: 31 c0 xorl %eax, %eax
4f: 48 83 c4 10 addq $16, %rsp
53: 5d popq %rbp
54: c3 retq
得出结论:

1. 看汇编可以得出编译顺序与代码的书写顺序是一致的。

2. 看 43 与 48 行地址为 00 ,这是虚拟地址, 起占位作用, 通过重定位符号表来确定*终的内存地址。

3. 查重定位符号表

objdump –macho –reloc .o文件名
如:
objdump –macho –reloc test.o
结果:如下面结果 (43 , 48 的下一位)49 与 44 分别就是上面占位地址的*终内存地址 。

address pcrel length extern type scattered symbolnum/value
00000049 True long True BRANCH False _test_1
00000044 True long True BRANCH False _test
0000003b True long True SIGNED4 False _global
00000031 True long True SIGNED4 False _global
Relocation information (__LD,__compact_unwind) 3 entries
address pcrel length extern type scattered symbolnum/value
00000040 False quad False UNSIGND False 1 (__TEXT,__text)
00000020 False quad False UNSIGND False 1 (__TEXT,__text)
00000000 False quad False UNSIGND False 1 (__TEXT,__text)

%title插图%num

分析可执行文件的代码段命令

objdump –macho -d test
输出:

(__TEXT,__text) section
_test:
100003f60: 55 pushq %rbp
100003f61: 48 89 e5 movq %rsp, %rbp
100003f64: 5d popq %rbp
100003f65: c3 retq
100003f66: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:(%rax,%rax)
_test_1:
100003f70: 55 pushq %rbp
100003f71: 48 89 e5 movq %rsp, %rbp
100003f74: 5d popq %rbp
100003f75: c3 retq
100003f76: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:(%rax,%rax)
_main:
100003f80: 55 pushq %rbp
100003f81: 48 89 e5 movq %rsp, %rbp
100003f84: 48 83 ec 10 subq $16, %rsp
100003f88: c7 45 fc 00 00 00 00 movl $0, -4(%rbp)
100003f8f: c7 05 67 40 00 00 15 00 00 00 movl $21, 16487(%rip)
100003f99: c7 05 5d 40 00 00 14 00 00 00 movl $20, 16477(%rip)
100003fa3: e8 b8 ff ff ff callq _test
100003fa8: e8 c3 ff ff ff callq _test_1
100003fad: 31 c0 xorl %eax, %eax
100003faf: 48 83 c4 10 addq $16, %rsp
100003fb3: 5d popq %rbp
100003fb4: c3 retq
mac OS 是小端。  地址从右到左查看 。  右是高位

 

DWARF与DSYM
dsym 文件就是保存按照DWARF格式保存的调试信息的文件 。

dwarf 是一种被众多编译器和调试器使用的用于支持源码代码级别调试的调试文件格式。

dsym文件的生成过程:

1. 读取debug map

2. 从.o文件中加载 dwarf

3. 重新定位所有的地址

4. *后将全部的 dwarf 打包成 dsym bundle

生成调试信息命令

clang -g -c test.m -o test.o
理解第2步的过程: 终端命令 查看 test.o

otool -l test.o
结果:  下面就有dwarf 字段 。

Load command 0
cmd LC_SEGMENT_64
cmdsize 1112
segname
vmaddr 0x0000000000000000
vmsize 0x00000000000004c9
fileoff 1272
filesize 1225
maxprot 0x00000007
initprot 0x00000007
nsects 13
flags 0x0
Section
sectname __text
segname __TEXT
addr 0x0000000000000000
size 0x0000000000000055
offset 1272
align 2^4 (16)
reloff 2500
nreloc 4
flags 0x80000400
reserved1 0
reserved2 0
Section
sectname __data
segname __DATA
addr 0x0000000000000058
size 0x0000000000000004
offset 1360
align 2^2 (4)
reloff 0
nreloc 0
flags 0x00000000
reserved1 0
reserved2 0
Section
sectname __objc_imageinfo
segname __DATA
addr 0x000000000000005c
size 0x0000000000000008
offset 1364
align 2^0 (1)
reloff 0
nreloc 0
flags 0x10000000
reserved1 0
reserved2 0
Section
sectname __debug_str
segname __DWARF
addr 0x0000000000000064
size 0x0000000000000111
offset 1372
align 2^0 (1)
reloff 0
nreloc 0
flags 0x02000000
reserved1 0
reserved2 0
Section
sectname __debug_abbrev
segname __DWARF
addr 0x0000000000000175
size 0x0000000000000061
offset 1645
align 2^0 (1)
reloff 0
nreloc 0
flags 0x02000000
reserved1 0
reserved2 0
Section
sectname __debug_info
segname __DWARF
addr 0x00000000000001d6
size 0x0000000000000093
offset 1742
align 2^0 (1)
reloff 2532
nreloc 5
flags 0x02000000
reserved1 0
reserved2 0
Section
sectname __apple_names
segname __DWARF
addr 0x0000000000000269
size 0x0000000000000090
offset 1889
align 2^0 (1)
reloff 0
nreloc 0
flags 0x02000000
reserved1 0
reserved2 0
Section
sectname __apple_objc
segname __DWARF
addr 0x00000000000002f9
size 0x0000000000000024
offset 2033
align 2^0 (1)
reloff 0
nreloc 0
flags 0x02000000
reserved1 0
reserved2 0
Section
sectname __apple_namespac
segname __DWARF
addr 0x000000000000031d
size 0x0000000000000024
offset 2069
align 2^0 (1)
reloff 0
nreloc 0
flags 0x02000000
reserved1 0
reserved2 0
Section
sectname __apple_types
segname __DWARF
addr 0x0000000000000341
size 0x0000000000000047
offset 2105
align 2^0 (1)
reloff 0
nreloc 0
flags 0x02000000
reserved1 0
reserved2 0
如果是查看可执行文件,是找不到drarf字段的, 他会单独放在一个地方 ,通过命令查看:

nm -pa 可执行命令文件名
如:
nm -pa test
生成dsym文件命令:

clang -g1 test.m -o test
查看 dsym 命令:

dwarfdump test.dSYM
dsym 内文件中保存的是没有偏移的虚拟地址,所以能够进行bug的现场恢复。

 

计算虚拟地址
ViewController.m文件如下:

1. 获取 ASLR

2. 计算虚拟地址 。

#import “ViewController.h”
#import <mach-o/dyld.h>
#import <mach-o/getsect.h>
#import <objc/runtime.h>

@interface ViewController ()

@end

@implementation ViewController

// 获取ASLR
uintptr_t get_slide_address(void) {
uintptr_t vmaddr_slide = 0;
// 使用的所有的二进制文件 = ipa + 动态库
// ASLR Macho 二进制文件 image 偏移
for (uint32_t i = 0; i < _dyld_image_count(); i++) {
// 遍历的是那个image名称
const char *image_name = (char *)_dyld_get_image_name(i);
const struct mach_header *header = _dyld_get_image_header(i);
if (header->filetype == MH_EXECUTE) {
vmaddr_slide = _dyld_get_image_vmaddr_slide(i);
}
NSString *str = [NSString stringWithUTF8String:image_name];

if ([str containsString:@”TestInject”]) {

NSLog(@”Image name %s at address 0x%llx and ASLR slide 0x%lx.\n”, image_name, (mach_vm_address_t)header, vmaddr_slide);
break;
}
}

// ASLR返回出去
return (uintptr_t)vmaddr_slide;
}

– (void)viewDidLoad {
[super viewDidLoad];
[self getMethodVMA];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self test_dwarf];
});
NSLog(@”123″);
}

– (void)test_dwarf {
NSArray *array = @[];
array[1];
}

– (void)getMethodVMA {
// 运行中的地址(偏移)
IMP imp = (IMP)class_getMethodImplementation(self.class, @selector(test_dwarf));
unsigned long imppos = (unsigned long)imp;
unsigned long slide = get_slide_address();
// 运行中的地址(偏移) – ASLR = 真正的虚拟内存地址
unsigned long addr = imppos – slide;
}
@end
注意: 需要在building setting里面打开生成dysm文件的配置 。  debug默认不生成这个文件 。

xcode 终端格式输入十六进制   lldb调试输入

e -f x — 计算出的虚拟内存地址

结论:

使用的所有的二进制文件 = ipa + 动态库

ASLR Macho 二进制文件 image 偏移

 

 

dyld学习 – 后期通过源码深入学习
dyld 调试:

方式一: 如果想调试dyld的源代码,需要准备带调试信息dyld/libdyld.dylib/libclosured.dylib,与系统做替换,风险比较大。

方式二 : 通过在dyld文件上设置断点。  设置断点的方法有两种。

lldb保留了一个库列表(白名单), 避免在按名称设置短点时出现问题, 而 dyld 正好在这个名单上,所以需要强制设置短点, 方式如下两种:

-s 含义: 指定在哪个文件里面设置断点。

1. br set -n dyldbootstrap::start -s dyld

2. set set target.breakpoints-use-platform-avoid-list 0   (通过lldb)

%title插图%num

终端输入: 打印dyld所有的调用过程 、 其他配置自行查询实践。

DYLD_PRINT_APIS=1 ./ 可执行文件
如:
DYLD_PRINT_APIS=1 ./test
dyld 到底做了什么?

dyld: 动态链接程序

libdyld.dylib :给我们的程序提供在runtime期间能使用动态链接功能。

具体执行步骤如下:

%title插图%num

插入动态库与插入函数: hook

插入函数 :

__attribute__((used))  : 去除未使用的代码的警告,加在*前面 。

__attribute__((used)) static struct {
const void* replacement;
const void* replacee;

%title插图%num

iOS开发之判断用户是否打开APP通知开关,看我就够了

1.*近在做app内部的推送开关功能。 这样顾客可以自己定义推送给他的内容,屏蔽不想要的一些烦人推送。

在开发过程中, 如果顾客打开推送开关的时候,也已经向服务器发送指令,进行推送开关同步,给它说这个用户已经打开了A推送,现在服务器推送A推送给客户端, 这时候照说,客服端是可以收到通知的,但是客服端却没有收到。 这是为什么呢? 很简单的一个问题,原来是顾客没有在系统通知处打开app的通知开关,所以收不到推送是正常现象。

那现在就产生了一个需求:

用户在进行设置推送开关的时候,当用户打开推送开关为开的时候,需要去判断 系统通知处的 推送开关用户有没有进行设置?

网上这样的代码都是一大把,关于怎么去检测系统通知有没有打开,但是,发现运用到程序中,没什么鸟用, 会一直提示说,用户没有打开推送。

下面就到了贴代码的环节了,首先先看下这段代码:

if ([[UIApplication sharedApplication] enabledRemoteNotificationTypes] == UIRemoteNotificationTypeNone) { //判断用户是否打开通知开关
}
typedef NS_OPTIONS(NSUInteger, UIRemoteNotificationType) {//这个是用户当前通知的几种状态,*种就是用户没有开大通知开关
UIRemoteNotificationTypeNone = 0,
UIRemoteNotificationTypeBadge = 1 << 0,
UIRemoteNotificationTypeSound = 1 << 1,
UIRemoteNotificationTypeAlert = 1 << 2,
UIRemoteNotificationTypeNewsstandContentAvailability = 1 << 3,
} NS_ENUM_DEPRECATED_IOS(3_0, 8_0, “Use UIUserNotificationType for user notifications and registerForRemoteNotifications for receiving remote notifications instead.”) __TVOS_PROHIBITED;
我在程序中就是用到了这个方法,去进行检测,用户有没有打开推送开关, 却忽视了

NS_ENUM_DEPRECATED_IOS(3_0, 8_0, “Use UIUserNotificationType for user notifications and registerForRemoteNotifications for receiving remote notifications instead.”) __TVOS_PROHIBITED

通过上面的图,可以看到此方法在iOS8.0就废弃了,虽然说并没有被移除,但是也要用他*新建议的方法去进行版本的兼容,现在加上在iOS9.0上用这个方法进行判断没有任何作用,现在就用新版的判断方法。下面是新版的判断方法。

if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIRemoteNotificationTypeNone) {
}
注意:
currentUserNotificationSettings 是一个对象,属于UIUserNotificationSettings类
所以.types切莫忘记。

总结:

*完善的做法就是,进行两者兼容 iOS7下的也要兼容, iOS7以上的我更要兼容啦,*完善的做法

 

#define IOS8 ([[[UIDevice currentDevice] systemVersion] doubleValue] >=8.0 ? YES : NO)

if (IOS8) { //iOS8以上包含iOS8
if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIRemoteNotificationTypeNone) {
}
}else{ // ios7 一下
if ([[UIApplication sharedApplication] enabledRemoteNotificationTypes] == UIRemoteNotificationTypeNone) {
}
}

 

iOS – 静态库、动态库从浅到深学习之路 (三)

XCFramework  (framework的增强版)
说明:

1. 苹果官方推荐,支持的,可以更加方便多个平台和架构的分发二进制库的格式。

2. 需要xcode11以上支持

3. 在2019年提出的framework的另一种先进格式。

多架构合并
架构打包命令:

// 打包成模拟器架构
xcodebuild archive -project ‘SYTimer.xcodeproj’ \
-scheme ‘SYTimer’ \
-configuration Release \
-destination ‘generic/platform=iOS Simulator’ \
-archivePath ‘../archives/SYTimer.framework-iphonesimulator.xcarchive’ \
SKIP_INSTALL=NO

// 打包成iOS真机架构
xcodebuild archive -project ‘SYTimer.xcodeproj’ \
-scheme ‘SYTimer’ \
-configuration Release \
-destination ‘generic/platform=iOS’ \
-archivePath ‘../archives/SYTimer.framework-iphoneos.xcarchive’ \
SKIP_INSTALL=NO
胖二进制: 多个架构将库文件压缩放在一起,并不是合并。

通过lipo 命令进行将动态库进行合并。

格式 : lipo -output 合并后输出的名称 -create  架构路径1 架构路径2

lipo -output SYTimer -create ../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer ../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer
遇到问题;

相同的架构合并,会出现冲突

解决方法:

去除相同的架构,只留一个架构,相关命令如下:

lipo -output SYTimer -create ../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer ../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer

//提取 x86_64架构
lipo -output SYTimer-x86_64 -extract x86_64 ../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer

lipo -output SYTimer -create ../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer SYTimer-x86_64
苹果为了解决这个问题,所以引进了XCFramework ,优点相比于 lipo

1.  不用处理头文件

2. 会自动处理重复的架构

3. 会自动产生调试符号

4. 会根据运行的架构自动选择对应的架构 。

 

制作一个XCFramework:
xcodebuild -create-xcframework \
-framework ‘../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework’ \
-framework ‘../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework’ \
-output ‘SYTimer.xcframework’ 需要改xcframework放的路径
xcodebuild -create-xcframework \
-framework ‘../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework’ \
-debug-symbols ‘架构1 – bcsymbolmap – 的*对路径’ \
-debug-symbols ‘架构2 – bcsymbolmap – 的*对路径’ \
-debug-symbols ‘架构1 – dsym – 的相对路径’ \
-framework ‘架构2 -framework – 的相对路径’ \
-debug-symbols ‘架构2 – dysm – 的相对路径’ \
-output ‘SYTimer.xcframework’
weak import
1. 弱引用的作用 ,就是当动态库被引入但没有使用的话,会自动置为nil ,不会让程序崩溃,编译通过,一个符号找不到,不会强制找到为止。 在xcconfig文件中格式为:

OTHER_LDFLAGS = $(inherited) -Xlinker -weak_framework -Xlinker “第三方库或动态库名称”
相同静态库的重复引入,符号冲突的解决方法:

通过链接器的 force_load 引入其中一个静态库, 另外一个静态库的符号通过 load_hidden 去隐藏。

命令如下:

OTHER_LDFLAGS = $(inherited) -l”*个静态库名” -l”第二个静态库名” -Xlinker -force_load -Xlinker “*个静态库路径.a” -Xlinker -load_hidden -Xliner “第二个静态库路径.a”
动态库 、静态库的实战
动态库与动态库的链接实战:

思路:  自己的framework,通过pod导入一个第三方,然后自己的项目,导入自己的framework并使用 ,反向使用等。

实现步骤:

1 .创建一个自己的framework (创建方法见 : framework制作)

2. 在framework,通过cocoapods导入一个第三方库。 (pods 只会生成链接器的参数,并不会把库文件放到framework下,pods是通过脚本是复制到指定目录,方式二:再使用的工程中,通过pods的配置文件,再指定加载的第三方库。)

3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。

4. 现在的流程就是 app -> 自己的动态库  -> 第三方动态库 (AFNetworking) , 大致的动态库与动态库的模型就出来了。

后期上传demo.

主要出现的问题:

1. 运行app,找不到第三方库文件(因为这个第三方库,是动态库中所引用的第三方库),报错信息:

Undefined symbols for architecture x86_64:
“_OBJC_CLASS_$_ZGRAppObject”, referenced from:
objc-class-ref in ZGRAFNetworkingManager.o
解决方法是:在pod 文件中为这个target也加入pod 第三方库的方法,Podfile 代码如下:

platform :ios, ‘14.1’

#自己做的framework ,动态库
target :’ZGRNetworkManager’ do
use_frameworks!
pod ‘AFNetworking’
end

#比喻App
target :’ZGRNetworkManagerTests’ do
use_frameworks!
pod ‘AFNetworking’
end
2. 找不到对应的符号_OBJC_CLASS_$_ZGRAppObject ,  修改pod的配置文件,创建的target 和 动态库对应的配置文件都加上这句话。

OTHER_LDFLAGS = $(inherited) -framework “AFNetworking” -Xlinker -U -Xlinker _OBJC_CLASS_$_ZGRAppObject
动态库与静态库的链接实战:

思路:  自己的framework,通过pod导入一个第三方静态库 AFNetworking.a ,在framework 中添加一个target,模拟app来使用 。

实现步骤:

1 .创建一个自己的framework (创建方法见 : framework制作)

2. 在framework,通过cocoapods导入一个第三方库。 (pods 只会生成链接器的参数,并不会把库文件放到framework下,pods是通过脚本是复制到指定目录,方式二:再使用的工程中,通过pods的配置文件,再指定加载的第三方库。)

3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。

4. 现在的流程就是 app -> 自己的动态库  -> 第三方静态库 (AFNetworking.a) , 大致的动态库与静态库的模型就出来了。

 

注:

动态库链接静态库,会链接所有的静态库符号, 所以当app去编译动态库的时候,并不会报错。

静态库的导出符号,也会出现在动态库符号里面,所以可以在app中直接使用,导入头文件即可。   (搜索不到,需要设置 header search paths路径 ${SRCROOT}/Pods/Headers/Public)

后期上传demo.

关于静态库的符号隐藏,通过在pod的配置文件加入如下代码:

OTHER_LDFLAGS = $(inherited) -Xlinker -hidden-l “AFNetworking”
 

静态库与静态库的链接实战:

思路:  自己的framework静态库组件,通过pod导入一个第三方静态库 AFNetworking.a ,在framework 中添加一个target,模拟app来使用 。

实现步骤:

1 .创建一个自己的framework (静态库) (创建方法见 : framework制作)

2. 在framework,通过cocoapods导入一个第三方库静态库。

3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。

4. 现在的流程就是 app -> 自己的静态库组件  -> 第三方静态库 (AFNetworking.a) 。

 

操作记录:

1. 在静态库组件首先要设置 library search path ,  例如 : “${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking”

2. 设置Other Linker Flags , -lAFNetworking

 

原理就是: app去链接静态库组件, 但静态库组件所链接的静态库并没有路径链接到app,所以需要在组件中手动设置下所链接的静态库的路径与头文件。

 

静态库与动态库的链接实战:

公式:  app = app + 静态库 (所有的符号都是可见的)

思路:  自己的framework静态库,通过pod导入一个第三方动态库 AFNetworking ,在framework 中添加一个target,模拟app来使用 。

实现步骤:

1 .创建一个自己的framework ,静态库(创建方法见 : framework制作)

2. 在framework,通过cocoapods导入一个第三方库AFNetworking。

3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。

4. 现在的流程就是 app -> 自己的静态库  -> 第三方动态库 (AFNetworking) , 大致的静态库与动态库的模型就出来了。

 

Cocoapods 拷贝动态库脚本的用法。

首先,把脚本拷贝到工程根目录下,脚本文件,可以在任意cocoapods的工程中去找,  然后在项目中,在build phase 添加一个脚本,输入:

“${SRCROOT}/Pods-LGNetworkManagerTests-frameworks.sh”
这样在运行项目的时候,脚本会自动将动态库拷贝到product目录中。

 

总结:

1. XCFramework  – sdk开发,组件开发

解决了:

1.头文件的处理

2. 调试符号的保存

3. 相同架构的处理

4.2019年才出。

 

2.  实战

1. weak_import

使用场景:  动态库 运行时 -》 不能确认到指定位置,所以用到了弱引用,当找不到动态库的时候,项目不会报错的作用

2. 静态库冲突 -》 app -> all_load\-Objc

3. app – 动态库 – 动态库链接  ,app不知道所以需要以下操作

方式一: 通过cocoapod脚本复制

方式二: 通过cocoapod 加载第三方库,并不会加载两次。 不用担心

 

动态库 – 静态库

存在问题:

静态库不想暴露到处符号,需要用到 -hidden -l静态库

 

app – 静态库 – 静态库

1. 名称  知道。

2 不知道所在位置: 所以只需要告诉位置

 

app – 静态库 – 动态库

1. 编译报错:  不知道动态库的路径

2. 运行报错: 不知道动态库的rpath

iOS CPU占有率达到了100%甚至更多,然后导致App闪退情况总结及解决过程

今天在真机调试的过程中,发现了一个严重的问题,发现CPU的使用率竟然达到了100%,以至于会导致运行内存占用过高,被系统的看门狗机制给杀掉。

下面就讲一讲怎么去定位这个问题:

1.打开Xcode,把项目跑动起来,然后选择这个选项卡

%title插图%num

2.现在就可以看到这个画面

%title插图%num
3. 现在我们可以看到这个页面,发现我的CPU达到了 105%,这肯定是有问题,那现在怎么办呢,我们可以看到右边的图,点击Profile in Instruments. —》 然后点击Transfer.

%title插图%num

4. 现在就进入到Instruments中,我们看看究竟发生了什么,到底是什么情况,导致出现了这种问题。

1. 首先,我通过观察CPU占用率,各个页面进行排查,看是进行了何种操作后,才出现的这种CPU占用率居高不小。

2. 我很庆幸,我很快就定位到了原因。所以我可以知道是进入某一个页面,触发了某种操作后,然后,就会出现这种情况

3. 现在就可以通过Instruments来进行定位,来看看是执行什么代码,导致了这种非常耗时的操作,让CPU一直如此忙碌。

4.选中Xcode先把程序(command + R)运行起来

5.再选中Xcode,按快捷键(command + control + i)运行起来,此时Leaks已经跑起来了

6.由于Leaks是动态监测,所以我们需要手动操作APP,一边操作,一边观察Leaks的变化,当出现红色叉时,就监测到了内存泄露,点击右上角的第二个,进行暂停检测(也可继续检测,当多个时暂停,一次处理了多个).

扩展: 查内存泄露具体方法 点击打开链接l

5. 电脑卡爆了,哎。 回去了在截图,反正*后是跟踪到了 Runloop下。 有一个行为一直在占据着主线程,并且不释放,所以导致CPU一直在大量消耗,内存也慢慢渐长,一般能造成这种情况的就只有循环,并且一直没有释放,我利用Instruments中的leaks,然后进行了各种各样的内存泄露的检测及修复, 也正是这样,我发现了问题的所在。 原来是我写的有一个方法有问题。 我写的代码如下:

我们很清晰的看到如果条件为真,这就是一个死循环,我的PM那时候,这儿就想做一个图片一直闪烁的效果,这儿可以采用三种方案,一种是用这种循环引用来执行一套方法, 一种是通过NSTimer来定时去调用一个方法。我开始选择了前者,那时候也知道后果,也许这个死循环会一直存在下去,直到这个VC被dealloc,*后一种是通过 core animation来实现。 这种事*推荐的,具体写法,我会在后面开博客进行讲解

-(void)animationAction:(bool)isNeedbreak{
if(!isNeedbreak) {
[self performSelector:@selector(animationAction:) withObject: [NSNumber numberWithBool:YES] afterDelay:2];
}
}
2. 由于有上面这个担心所以,我在popviewcontroller, 控制器出栈的时候,我调用了如下方法,那个时候太粗心了,大概比方,是我想延迟2s执行一个方法,这个过程中,我想终止方法,那就只有通过调用下面两种随意一种,我却很天真的以为,这样就可以完美的终止死循环的调用。

//这个是取消所有的延迟执行函数。
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(animationAction:) object:[NSNumber numberWithBool:YES]];
[NSObject cancelPreviousPerformRequestsWithTarget:self];

3. 发现问题依然存在,所以只能用我的第二种解决办法, 用NSTimer来代替他。代码如下

NSTimer *animationTwoTime = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(productBrandIconAnimationWithIsBreak:) userInfo:[NSNumber numberWithBool:NO] repeats:YES];

4. 然后在vc出栈的时候,然后把NSTimer进行 invalidate下。

总结:

1.以后一定要慎用用for循环来进行实现动画的连续执行.
2. 这种动画效果尽量用 core animation来进行解决。

 

iOS – Shell 脚本学习入门

解释器与编译器
1. 编译器过程:  源代码 – 预处理器 – 编译器 – 目标代码 – 链接器 – 可执行程序

2. 解释器过程:  源代码 – 解释器  (python ,shell , js)

 

如何学习脚本: 三步骤
1. 学语法

2. 看脚本

3. 抄

 

基本语法:省略 。 自己去 w3c学习 。

常用语法记录:

#!/bin/bash

IS_ZSH=””
# bash-3.2 和 zsh

: << !
Shebang(Hashbang):一个由井号和叹号构成的字符序列#!出现在文本文件的*行的前两个字符。
操作系统的程序加载器会分析Shebang后的内容,将这些内容作为解释器指令。
并调用该指令,并将载有Shebang的文件路径作为该解释器的参数。

#!/usr/bin/python

#!/usr/bin/env pyhon

env:不同对操作系统,脚本解释器可能被安装于系统的不同的目录,设置到系统的PATH中。
env可以在系统的PATH目录中查找。
上述命令,使用在用户路径中找到的*个Python版本。但是可以通过指定版本号:
#!/usr/bin/env pythonX.x

env也可以指定搜索目录:
#!/usr/bin/env -S -P/usr/local/bin:/usr/bin:${PATH} python
会在/usr/local/bin、/usr/bin、系统PATH搜索python。
!

# echo “单行注释”

: << !
多行注释方式一:
echo “多行注释”
!

: << COMMENT
多行注释方式二:
echo “多行注释”
COMMENT

: ‘
多行注释方式三:
echo “多行注释”

if false; then
多行注释方式四:
echo “多行注释”
fi

((0)) && {
多行注释方式五:
echo “多行注释”
}

# 有空格时,将串包裹起来
VARIABLE=”Some string”
LUE=VARIABLE
# FOO=””
LONG_STRING=”I am Cat\\”
LONG_LONG_STRING=”I am Cat!CAT!Cat!Cat”

: ‘COMMENT
单引号与双引号,括号一定要成对
冒号(:)作为内建命令:占位符、参数扩展和重定向
子进程:在当前的shell下,去打开另一个新shell
env:查看环境变量与常见环境变量说明

PS1:提示符设置 [\u@\h \w \A #\#]\$
1. \h 主机名缩写
2. \u 用户账号名称
3. \w 完整工作路径
4. \A 24小时时间
5. \# 第几个命令
6. \$ 提示符,如果是root,提示符为#,否则是$
HOME:代表用户主文件夹。
SHELL:当前使用的是那个SHELL
PATH:执行文件查找路径
export: 自定义变量转环境变量
locale:显示语系变量
read [-pt] 变量 读取键盘变量
-p:提示符
-t:等待秒数

$(( 20 + 5 * 6)):返回双括号内算数运算的结果。
expr命令是一款表达式计算工具,使用它完成表达式的求值操作。
eval会对后面的cmdLine进行两遍扫描,如果*遍扫描后,cmdLine是个普通命令,则执行此命令;
如果cmdLine中含有变量的间接引用,则保证间接引用的语义。
type:显示命令属性,不加参数时,会显示该命令是内置命令还是其他。
-t:显示命令属性缩写
file:代表外部命令。
alias:代表该命令为别名。
builtin:内置命令。
-p:为外部命令时,会显示命令所在的文件
-a:将PATH中设置的所有的与命令名相关的列出来
echo [-ne][字符串]
-n 不要在*后自动换行
-e 若字符串中出现以下字符,则特别加以处理
\a 发出警告;
\b 删除前一个字符;
\c 不产生进一步输出 (\c 后面的字符不会输出);
\f 换行但光标仍旧停留在原来的位置;
\n 换行且光标移至行首;
\r 光标移至行首,但不换行;
\t 插入tab;
\v 与\f相同;
\\ 插入\字符;
\nnn 插入 nnn(八进制)所代表的ASCII字符;
用echo命令打印带有色彩的文字:
文字色:
echo -e “\e[1;31mThis is red text\e[0m”
\e[1;31m 将颜色设置为红色
\e[0m 将颜色重新置回
颜色码:重置=0,黑色=30,红色=31,绿色=32,黄色=33,蓝色=34,洋红=35,青色=36,白色=37
背景色:
echo -e “\e[1;42mGreed Background\e[0m”
颜色码:重置=0,黑色=40,红色=41,绿色=42,黄色=43,蓝色=44,洋红=45,青色=46,白色=47
文字闪动:
echo -e “\033[37;31;5mMySQL Server Stop…\033[39;49;0m”
红色数字处还有其他数字参数:0 关闭所有属性、1 设置高亮度(加粗)、4 下划线、5 闪烁、7 反显、8 消隐
COMMENT’

#
# if [ true ]; then
# :
# fi

#${VAR:=DEFAULT}
: ${VAR:=DEFAULT}
echo $VAR

: ‘COMMENT
1. Shell变量默认为字符串。shell不关心这个串是什么含义。
2. Shell默认的数值运算是整数类型。所以若要进行数学运算,必须使用一些命令例如declare、expr、双括号等。
3. Shell变量可分为两类:
i. 局部变量:只在创建它们的shell中可用。在函数内定义,函数执行后就被删除。
ii. 环境变量:可以在创建它们的shell及其派生出来的任意子进程中使用。在整个脚本执行期间,只要没有被删除就一直存在。
3. 定义规则:变量名必须以字母或下划线字符开头。其余的字符可以是字母、数字(0~9)或下划线字符。任何其他的字符都标志着变量名的终止。
小写敏感。
4. 给变量赋值时,等号周围不能有任何空白符。
5. 通常大写字符为系统默认变量。个人习惯。
6. set:查看所有变量(含环境变量与自定义变量),以及设置shell变量的新变量值。
-a:标示已修改的变量,以供输出至环境变量。
-b:使被中止的后台程序立刻回报执行状态。
-e:若指令传回值不等于0,则立即退出shell。
-f:取消使用通配符。
-h:自动记录函数的所在位置。
-H Shell:可利用”!”加<指令编号>的方式来执行history中记录的指令。
-k:指令所给的参数都会被视为此指令的环境变量。
-l:记录for循环的变量名称。
-m:使用监视模式。
-n:只读取指令,而不实际执行。
-p:启动优先顺序模式。
-P:启动-P参数后,执行指令时,会以实际的文件或目录来取代符号连接。
-t:执行完随后的指令,即退出shell。
-u:当执行时使用到未定义过的变量,则显示错误信息。
-v:显示shell所读取的输入值。
-x:执行指令后,会先显示该指令及所下的参数。
7. declare/typeset [-aixrp] 变量
-a 将变量定义成数组
-i 将变量定义成整数
-x 将变量定义成环境变量
-r 将变量定义成readonly
-p:显示变量定义的方式和值
+:取消变量属性,但是 +a 和 +r 无效,无法删除数组和只读属性,可以使用unset删除数组,但是 unset 不能删除只读变量。
8. local关键字,用来在作用域内创建变量。出来作用域被销毁。
9. export为shell变量或函数设置导出属性,成为环境变量。无法对未定义的函数添加导出属性。同时,重要的一点是,export的效力仅及于该次登陆操作。
注销或者重新开一个窗口,export命令给出的环境变量都不存在了。
-f:代表[变量名称]为函数名称。。
-n:删除变量的导出属性。变量实际上并未删除,只是不会输出到后续指令的执行环境中。
-p:显示全部拥有导出属性的变量。
-pf:显示全部拥有导出属性的函数。
-nf:删除函数的导出属性。
–:在它之后的选项无效。
10. 通配符
*:匹配任意字符串,包括空字符串,不包含对“/”字符的匹配。
?:匹配任意单个字符,不能匹配“/”字符。
[abc]:匹配“a”或者“b”或者“c”字符。
[^abc]:不匹配“a”或者“b”或者“c”字符。
[a-z]:匹配26个英文小写字符中任意一个。
用set命令可以查看所有的变量,unset var命令可以清除变量var,var相当于没有定义过。
readonly var可以把var变为只读变量,定义之后不能对var进行任何更改。

COMMENT’

if [[ -n $IS_ZSH ]]; then
# 根据变量属性强制转换值的英文大小写。
declare -u uc_var=’abc’
declare -l lc_var=’ABC’
# 显示’ABC abc’;
echo “${uc_var} ${lc_var}”
fi

# 执行后显示7,注意空格。
# expr 3 + 4
# result=`expr 2 + 3`
# echo $result

# # 没有指定整型属性,输出为字符串’a+b’。
# declare a=3 b=4 c
# c=a+b
# # a+b
# echo ${c}
# declare -p a

# # 不过可以使用以下方式赋值。
# c=$((a+b))
# # 7
# echo ${c}

# 设置了整型属性就可以直接加了。
# declare -i a=3 b=4 c
# c=a+b
# # 7
# echo ${c}
# declare -p a

# declare -p VARIABLE

# # 定义函数内的全局变量
# function test() {
# declare testA=3
# VARIABLE=”Value”
# local testB=3
# # 让我们查看它们的属性。
# declare -p testA VARIABLE testB
# }
# # 执行函数。
# test
# #Value
# echo $testA $VARIABLE $testB

# export a b=3
# # 当然也可以先定义后增加导出属性
# export VARIABLE
# 删除变量的导出属性
# export -n a b
# function func_1(){ echo ‘123’; }
# # 为已定义函数增加导出属性
# export -f func_1
# # 删除函数的导出属性
# export -fn func_1

# set 11 22 33 44
# # 44
# echo $4
# # $4
# echo “\$$#”
# # 44 *遍得到变量个数4,第二遍去第四个
# eval echo “\$$#”

# declare mylove=’Cat’ #定义新变量
# # env | grep mylove mylove=Cat
# set -a mylove #设置为环境变量

# echo “./*.sh”
# echo “./*”.sh
# MMM=`echo “./*.sh”`
# echo $MMM
# echo `echo “./*.sh”`
# echo ./*.sh
# set — “./”*.sh
# echo “$1″

: ‘COMMENT
数组:
var[1]=”var1”
var[2]=1
COMMENT’

# source Shell.sh
export VARIABLE

unset VARIABLE
echo $VARIABLE

: ‘COMMENT
运行方式:
1. sh:
使用$ sh script.sh执行脚本时,当前shell是父进程,生成一个子shell进程,在子shell中执行脚本。
脚本执行完毕,退出子shell,回到当前shell。$ ./script.sh与$ sh script.sh等效。
2. source:
使用$ source script.sh方式,在当前上下文中执行脚本,不会生成新的进程。脚本执行完毕,回到当前shell。
$ . script.sh与$ source script.sh等效。
3. exec方式:
使用exec command方式,会用command进程替换当前shell进程,并且保持PID不变。
执行完毕,直接退出,不回到之前的shell环境。
COMMENT’

: ‘COMMENT
参数扩展:通过符号$获得参数中存储的值。
1. 间接参数扩展${!parameter},,zsh不支持
i. ${parameter-string}:当parameter未设置则替换成string,不更改parameter值。否则,不做处理。
ii. ${parameter=string}:当parameter未设置则替换成string,更改parameter值。否则,不做处理。
iii. ${parameter?string}:parameter没有设置,则把string输出到标准错误中。否则,不做处理。
iiii. ${parameter+string}:当parameter为空的时替换成string。否则,不做处理。
2. 冒号后面跟 等号,加号,减号,问号(⚠不能有空格):
i. ${parameter:-string}:当parameter未设置或者为空则替换成string,不更改parameter值。
ii. ${parameter:=string}:当parameter未设置或者为空则替换成string,更改parameter值。
iii. ${parameter:?string}:若变量parameter不为空,则使用变量parameter的值。
若为空,则把string输出到标准错误中,并从脚本中退出。
iiii. ${parameter:+string}:当parameter不为空的时替换成string。若为空时则不替换或者说是替换空值。
3. 子串扩展:${parameter:offset}和${parameter:offset:length}。
从offset位置开始截取长度为length的子串,如果没有提供length,则是从offset开始到结尾。
i. offset可以是负值,且必须与冒号有间隔或者用()包裹。开始位置是从字符串末尾开始算起,然后取长度为length的子串。
例如,-1代表是从*后一个字符开始。
ii. parameter是@,也就是所有的位置参数时,offset必须从1开始。
4. 替换:${parameter/pattern/string}、${parameter//pattern/string}、${parameter/pattern}和${parameter//pattern}。
大小写敏感。string为空时,则相当于将匹配的子串删除。 parameter之后如果是/,则只匹配遇到的*个子串;
parameter之后如果是//,则匹配所有的子串。
5. 删除:${parameter#pattern}、${parameter##pattern}、${parameter%pattern}和${parameter%%pattern}。
i. # 是去掉左边,% 是去掉右边。单一符号是*小匹配﹔两个符号是*大匹配。
6. 参数长度:${#parameter}
COMMENT’

: ‘COMMENT
标准输入(stdin):代码为0,使用<或<<;
标准输出(stdout):代码为1,使用>或>>;
标准错误输出(stderr):代码为2,使用2>或2>>;
1> 以覆盖的方式将正确的数据输出到指定到文件或设备;
1>> 以累加到方法将正确到数据输出到指定到文件或者设备上;
2> 以覆盖的方式将错误的数据输出到指定到文件或设备;
2>> 以累加的方式将错误的数据输出到指定到文件或设备;
2>/dev/null 将错误到数据丢弃,只显示正确到数据
2>&1 或者 &>将正确到数据和错误到数据写入同一个文件
cmd;cmd 不考虑命令相关性,连续执行。
当前一个命令执行成功会回传一个 $?=0的值。
cmd1 && cmd2 如果*个命令的$?为0,则执行第二个命令。
cmd1 || cmd2 如果*个命令的$?为0,则不执行第二个命令。否则执行第二个命令。
|:管道仅能处理前面一个命令传来的正确信息,将正确信息作为stdin传给下一个命令
– :stdin和stdout利用减号“-“来代替
COMMENT’

: ‘COMMENT
sh [-nvx] scripts.h
-n:不执行,仅检查语法。
-v:在执行脚本之前,先将脚本内容输出。
-x:将使用到的脚本内容,输出。
COMMENT’

: ‘COMMENT
当条件成立,就进行循环:
while [ condation ] #判断条件
do #循环开始
程序
done #循环结束
当条件成立,就终止循环:
until [ condation ] #判断条件
do #循环开始
程序
done #循环结束
按照指定次数循环:
for var in con1 con2 con3 …
do
程序
done
for (( 初始值; 限制值; 执行步长 ))
do
程序
done
COMMENT’

: ‘COMMENT
多分支语句判断
除*后一个分支外(这个分支可以是普通分支,也可以是*)分支),其它的每个分支都必须以;;结尾,;;代表一个分支的结束,不写的话会有语法错误。
*后一个分支可以写;;,也可以不写,因为无论如何,执行到 esac 都会结束整个 case in 语句。
case $变量 in
“*个变量内容”)
程序
;; #结束
*) # 用来托底,没有匹配到数据
;;
esac
COMMENT’

: ‘COMMENT
一个条件判断:
if [ condation ]; then
成立
else
不成立
fi
多条件判断:
if [ condation ]; then
成立
elif [ condation ]; then
成立
else
不成立
fi
COMMENT’

: ‘COMMENT
shift:参数号码偏移。会移动变量,可以接数字,代表移动前面几个参数的意思
COMMENT’

: ‘COMMENT
[]:判断符号,两个等号和一个等号,效果类似。
1. 中括号里面的每个组件都需要空格分隔。
2. 中括号的变量,使用双引号
3. 中括号的常量,使用单引号或双引号
COMMENT’

: ‘COMMENT
test命令测试:
1. test n1 -eq n2:
-eq:相等
-ne:不等
-gt:大于
-lt:小于
-ge:大于等于
-le:小于等于
2. 字符串判断
-z string:判断string是否为0,为空,则为true。
-n string:判断string是否非0,为空,则为false。
string1 = string2:字符串是否相等,相等为true。
string1 != string2:字符串是否不等,相等为false。
3. 多重条件判断
-a:两个条件同时成立,为true。
-o:两个条件任何一个成立,为true。
!:反向。
4. 文件类型判断
-e:文件名是否存在。
-f:该文件名是否存在且是否为文件。
-d:该名称是否存在且为目录。
-L:该名称是否存在且是否为链接文件。
5. 文件权限检测
-r:是否存在是否有可读权限。
-w:是否存在是否有可写权限。
-x:是否存在是否有可执行权限。
-s:是否存在且为非空白文件。
6. 两文件比较
-nt 文件1是否比文件2新。
-ot 文件1是否比文件2旧。
-ef 文件1和文件2是否为同一个文件。
COMMENT’

# # Some string
# echo “${!VALUE}”

# #
# echo “${FOO-“Cat-“}”
# # Cat-
# echo “${FOO=”Cat-“}”
# #
# echo “${FOO+”Cat-“}”
# # Cat-
# echo “${FOO?”Cat—-“}”

# # Cat-
# echo “${FOO:-“Cat-“}”
# # Cat=
# echo “${FOO:=”Cat=”}”
# # Cat=
# echo “${FOO:?”Cat?”}”
# #Cat+
# echo “${FOO:+”Cat+”}”

# #Cat\
# echo “${LONG_STRING:5}”
# #Cat
# echo “${LONG_STRING:5:3}”
# #Cat
# echo “${LONG_STRING: -4:3}”
# #Cat
# echo “${LONG_STRING: -4:3}”
# #Cat
# echo “${LONG_STRING:(-4):3}”

# # I am Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING/cat}”
# # I am LGCat!CAT!LGCat!LGCat
# echo “${LONG_LONG_STRING//Cat/LGCat}”

# # am Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING#* }”
# # am Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING#? }”

# # at!CAT!Cat!Cat
# echo “${LONG_LONG_STRING#*[Cc]}”

# if [[ -n $IS_ZSH ]]; then
# # Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING#*(AT|m)}”
# echo “—–${LONG_LONG_STRING#*(AT|m)}”
# echo “—–${LONG_LONG_STRING#[A-z]**(AT|m)}”
# # !Cat!Cat
# echo “—–${LONG_LONG_STRING#*(AT|mm)}”
# fi

# # m Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING#*[a-t]}”
# # m Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING#*[a-t]}”
# # am Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING#*[^A-Z]}”

# echo `echo “./*.sh”`
# echo `echo “./”*.sh`

# # Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING##* }”
# # at
# echo “${LONG_LONG_STRING##*[Cc]}”
# #
# echo “${LONG_LONG_STRING##*[a-t]}”

# # I am
# echo “${LONG_LONG_STRING% *}”
# # I
# echo “${LONG_LONG_STRING%% *}”

# # 20
# echo “${#LONG_LONG_STRING}”

# # i am cat!cat!cat!cat
# echo “$(echo “${LONG_LONG_STRING}” | tr “[:upper:]” “[:lower:]”)”
# # I AM CAT!CAT!CAT!CAT
# echo “$(echo “${LONG_LONG_STRING}” | tr “[:lower:]” “[:upper:]”)”

# if [[ -n $IS_ZSH ]]; then
# # I AM CAT!CAT!CAT!CAT
# echo “${LONG_LONG_STRING:u}”
# # i am cat!cat!cat!cat
# echo “${LONG_LONG_STRING:l}”
# fi

# : ${TEM:=”./”*.sh}
: ‘COMMENT
函数的声明形式:
function 函数名 {
函数体
}
function 函数名() {
函数体
}
函数名() {
函数体
}
1. 有funtion,可以不写(),没有function,必须写()。
2. 函数名和”{“之间必须有空格。
3. 不得声明形式参数。
4. 必须在调用函数地方之前,声明函数
5. 无法重载
6. 后来的声明会覆盖之前的声明
7. 没有返回值的函数,默认返回函数内*后一条指令的返回值。有返回值的函数,只能返回整数。
8. 需要获得函数值,只能通过$?获得。通过=获得是空值。

我们可以将shell中函数,看作是定义一个新的命令,因此各个输入参数直接用空格分隔。
一次,命令里面获得参数方法可以通过:$0…$n得到。$0代表函数本身。
$#:传入的参数的个数。
$*:所有的位置参数(作为单个字符串)。
$@:所有的位置参数(每个都作为独立的字符串)。
$?:当前shell进程中,上一个命令的返回值,如果上一个命令成功执行则$?的值为0,否则为其他非零值。
$$:当前shell进程的pid。
$!:后台运行的*后一个进程的pid。
$-:显示shell使用的当前选项。
$_:之前命令的*后一个参数。
COMMENT’

# 无输出
# logic
# function DoWork {
# local LG_CAT=”LG_Cat”
# echo “logic”
# return 2
# }
# DoWork() {
# local LG_CAT=”LG_Cat”
# echo “logic”
# return 2
# }

# 无输出
# logic
# function DoWork {
# local LG_CAT=”LG_Cat”
# echo “logic”
# return 2
# }
# echo $LG_CAT
# echo `DoWork`
# 无输出
# logic
# function DoWork {
# LG_CAT=”LG_Cat”
# echo “logic”
# return 2
# }
# echo $LG_CAT
# echo `DoWork`
# logic
# 无输出
# function DoWork {
# LG_CAT=”LG_Cat”
# echo “logic”
# return 2
# }
# echo `DoWork`
# echo $LG_CAT
# logic
# logic
# LG_Cat
# function DoWork {
# LG_CAT=”LG_Cat”
# echo “logic”
# return 2
# }
# DoWork
# echo `DoWork`
# echo $LG_CAT
# logic
# 2
# logic
# 0
# function DoWork {
# LG_CAT=”LG_Cat”
# echo “logic”
# return 2
# }
# DoWork
# echo “$?”
# echo `DoWork`
# echo “$?”
# function DoWork {
# echo “特殊变量:\n
# \$#:$#\\n
# \$0:$0\\n
# \$1:$1\\n
# \$2:$2\\n
# \$*:$*\\n
# \$@:$@\\n
# \$$:$$\\n
# \$-:$-\\n
# \$_:$_
# ”
# return 2
# }
# DoWork “Cat” “LGCat”
array=($MMM 1 2)

echo $array

declare -p MMM

declare -p array
if [[ -n $IS_ZSH ]]; then
declare -A fruits=([‘apple’]=’red’ [‘banana’]=’yellow’)
# 显示所有关联数组。
declare -A
# red yellow
echo ${fruits[@]}
echo ${fruits[*]}
echo ${(@k)fruits}
fi

脚本实现:
while [[ $# -gt 0 ]]; do
case $1 in
-d|–directory)
shift
echo “$1—-”
shift
;;
-k|–keyword)
shift
echo “$1 —-”
shift
;;
-s|-source)
echo “$1 —-source”
shift
;;
-f|–framework)
shift
;;
-l|–lib)
shift
;;
-h|–help)
Show_Help
exit 0
;;
*)
echo “Unknown option: $1”
exit 1
esac
done
通过上面的脚本来学习脚本:

while do  case : 循环

[[ $# -gt 0 ]] :

$# ,获取执行脚本的时候传递的参数个数

-gt : 大于

$1: 循环的参数值

exit: 0 正常退出

其他exit: 为自定义意义。

 

自定义一个脚本说明文档

格式如下:

Show_Help() {
# 结束符
cat <<EOF

find_api.sh –directory <dir> 在指定目录指定文件内搜索指定关键字。

-d|–directory <dir> – 指定查找目录,默认当前所在目录
-k|–keyword <word> – 查找关键字
-s|–source – 指定查找源码文件
-f|–framework – 指定查找framework文件
-l|–lib – 指定查找libs文件
–help – prints help screen

EOF

}
注意EOF不要有空格。

cat <<EOF

中间写上脚本的使用文档

EOF

查找、细分查询功能
find . -name “*.framework”
find . -name “*.h” -exec echo {} \;
grep -E “some|weak” -A 1 -B 1 -i
nm -pa <mach-o>
find : 查找文件  -name ‘字串’  查找文件名匹配所给字串的所有文件,字串内可用通配符 *、?、[ ]。

grep: 搜索具体文件中的关键字   -E   –extended-regexp             # 将范本样式为延伸的普通表示法来使用,意味着使用能使用扩展正则表达式

 

数组与For循环
# <<< 表示将右侧的字符串传递到左侧命令的标准输入。
# < 将文件的内容传递到命令的标准输入
# << 将带有结束标志的文档内容传递到命令的标准输入

#test
#-e:文件名是否存在。
#-f:该文件名是否存在且是否为文件。
#-d:该名称是否存在且为目录。
#-L:该名称是否存在且是否为链接文件。

#read
#-a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
#-r 屏蔽\,如果没有该选项,则\作为一个转义字符,有的话 \就是个正常的字符了。

# read通过输入重定向,把file的*行所有的内容赋值给变量line,循环体内的命令一般包含对变量line的处理;然后循环处理file的第二行、第三行。。。一直到file的*后一行。
#for是每次读取文件中一个以空格为分割符的字符串。
#for是每次读取字符串中一个以空格为分割符的字符串。
#weak|cat|Hank
Find_Api() {
local key_word=””
if [[ -n “${KEYWORD}” ]]; then
read -a names <<< “${KEYWORD}”
for name in ${names[@]}
do
if [[ ! -n “${key_word}” ]]; then
key_word=”$name”
else
key_word=”$key_word|$name”
fi
done
else
echo “请输入查找的关键字!”
exit 1
fi
echo “$key_word —-”
}