
runtime 在iOS中是“运行时”的含义,是一套用c语言写的api,很多人会用但是也仅仅用过最最常用的几个函数,这次,我将详细的带着大家探索下 runtime 的API,这一章就说下 <objc/runtimeh> 这个文件里的 API ,并且我会把不适用于 ARC 和不支持64位的API剔除掉。
首先,我们先看一个简单的函数:
这个函数是通过传入 Class 类型的 cls 来得到 Class 的名字。那我们测试下这个函数:
其中 [Person class] OC中获得 Class 的方法,当然,你也可以用 runtime 里面的 objc_getClass 等函数,后面我也会讲到。
运行结果:
我们可以看到打印出来的结果就是类的名字。
上面既然用到了 [Person class] ,那我们就看下在 runtime 中 [Person class] 的替代函数,都是通过名字来获得 Class
那这三个有什么区别,从结论上讲, objc_getClass 和 objc_lookUpClass 的效果是一致的,在最新的源码里面,这两个方法调用的底层也是一致的,当你要找的类不存在的话,就返回nil,而 objc_getRequiredClass 里你要找的类不存在的话,就会崩溃。下面我们来测试下,我们创建一个 Person 类。
运行结果:
最后也确实崩溃了,所以大家使用 objc_getRequiredClass 这个函数时候要慎重小心。
除了用名字获得类对象以外,还可以用实例对象来获取:
我们测试下:
运行结果:
完全没问题。
Class 不仅可以代表类对象,也可以代表元类对象,下面这个函数就是通过名字获取元类对象。
如果你读过源码的话,你就会清楚元类对象储存的是类方法,类对象储存的是实例方法,在后面讲到Method相关的API的时候,我们在具体讲他们之间的区别。
讲到元类对象,我们还要关注下这个函数,
这个函数是用来判断是否是元类对象。
运行结果:
我们可以看到 objc_getMetaClass 生成才是元类对象, objc_getClass 生成的只是类对象。
那么有没有函数区分类(元类)对象和实例对象呢?当然有:
这个方法只要是类对象或者元类对象都会返回YES:
运行结果:
当然也可以获得父类对象。
我们新建一个继承 Person 的类 Student ,然后我们通过 Student 类来获得 Person 类。
运行结果:
Student 的父类确实是 Person 。
我们知道OC里面可以强转类型,当然, runtime 里面也有相关方法
这个方法的意思是给一个实例对象设置新的类,返回旧的类
运行结果:
我们可以看出开始的时候 student 的类是 Student ,用了 object_setClass 后就是 Person 类了。
runtime 的动态性还可以动态新增类,下面四个函数分别表示为一个类分配内存,注册一个类,复制一个类,销毁一个类
创建一个新类, superclass 是新类所继承的类,如果为 nil , superclass 就默认为根类,也就是 NSObject , extraBytes 是在类和元类对象的末尾为索引ivars分配的字节数。这一般是0, name 是新类的名字。
注册类,如果这个类 objc_allocateClassPair 好了,就必须 objc_registerClassPair 才能使用。
这个方法在系统KVO的底层用过,系统不推荐我们自己用。
objc_disposeClassPair 只能销毁通过 objc_allocateClassPair 创建的类。
我们写个demo来测试这些方法, objc_duplicateClass 官方不建议使用,那么我们就不测试这函数。
运行结果:
我们可以知道如果仅仅只是 objc_allocateClassPair 的话,你是找不到这个类的,必须再 objc_registerClassPair 才可以找到, objc_disposeClassPair 则是把类销毁掉,所以再实际开发中,如果我们不再使用自建类的时候,就要及时销毁,节省内存。
下面两个函数是关于整个工程的类列表的函数:
这个函数是获得所有注册类的列表,我们试用下:
运行结果:
我们看到注册的类有15765个。
objc_getClassList 也是获取注册类的方法
第一个参数 buffer 已分配好内存空间的数组指针, bufferCount 是数组的个数,如果 bufferCount 的数量小于实际的数组数量,那么 buffer 返回的是所有数组集合的任意一个子类。如果 buffer 为NULL,那么 bufferCount 为0。无论那种情况,返回结果都是当前注册类的总数。
运行结果:
返回类实例的大小。
运行结果
一个没有变量或属性的继承于NSObject的类占有8个字节。
还有个方法是:
这是一个创建实例的方法, cls 是要创建的类, extraBytes 是额外的字节内存,用来存储类定义中的实例变量之外的其他实例变量。在源码中 alloc 方法底层就是用的这个函数。那么,我们用这个函数来初始化 Person 类:
运行结果:
确实能够成功创建出来。
最后剩下两个方法:
这两个方法都和 version 有关,这个version在实际中我也没发现用处,可能是在改变类的变量或者方法时给定一个标识
运行结果
下面我们将使用runtime里面最最常用的api,也就是给分类绑定对象,这里,我们先了解下,一个枚举:
objc_AssociationPolicy 是一个枚举,里面的枚举值分别代表要添加的属性的修饰类型。
OBJC_ASSOCIATION_ASSIGN 相当于 weak
OBJC_ASSOCIATION_RETAIN_NONATOMIC 相当于 strong 和 nonatomic
OBJC_ASSOCIATION_COPY_NONATOMIC 相当于 copy 和 nonatomic
OBJC_ASSOCIATION_RETAIN 相当于 strong 和 atomic
OBJC_ASSOCIATION_COPY 相当于 copy 和 atomic
关于分类的runtime函数,主要有下面3个:
含义分别为设置关联对象,获得关联对象,删除关联对象。
我们知道如果在分类的 h 文件设置属性并没有用,调用的时候会发生闪退,这是因为系统并没有自动为属性生成 Set 和 Get 方法,所以,我们用上面三个方法来手动关联对象。
我们创建一个 Person 的分类 Person+Actorh ,在h文件里新建一个新属性 @property(nonatomic, assign)float actingSkill 而不做其他任何处理,这时候, m 文件就会有警告。
这时候就绑定好了。
在 ViewController 里面去使用下这个属性
运行结果:
说明set和get方法都成功了。
那么还有一个 objc_removeAssociatedObjects 方法还没用,这个方法是解除绑定,为了测试这个效果,我们在ViewController里面 touchesBegan 里面去调用这个方法。
运行结果:
之前绑定的结果被移除了。
今天我们这一篇就讲到这, runtime 还有很多其他的用法我们下一篇见。
对了,这个是 demo ,喜欢的可以点个星。
java枚举类型中的instance用法
用法: result = object instanceof class 参数: Result:布尔类型。 Object:必选项。任意对象表达式。 Class:必选项。任意已定义的对象类。 说明: 如果object 是 class 的一个实例,则 instanceof运算符返回 true
定义了颜色,当读取字符串时,想获得与之对应的枚举类型的值,可以用下面的代码实行
public
enum
Color
{
RED
=
1,
GREEN
=
2,
BLUE
=
3
}
static
void
Main(string[]
args)
{
string
col
=
"BLUE";
Color
getType
=
(Color)EnumParse(typeof(Color),col);
ConsoleWriteLine(getTypeGetHashCode());
ConsoleReadLine();
}
如果想得到枚举的名称的输出,则可以直接将输出语句换成getType即可
一、通讯框架类图
二、框架说明
上图是通讯框架静态类图,其抽象模型是:服务器在指定的IP和端口上进行监听,当收到一个连接请求时就会创建一个连接,然后把这个连接交给一个执行器执行处理该连接,一个连接包含一个或多个会话,每个会话在一个线程上执行,不同的会话间互相不影响,只要客户端不主动关闭连接,服务器就可以在同一连接上处理多个会话。
XServiceHost是服务主机,即监听者,它负责在指定的IP和端口上监听来自客户端的请求,它运行在一个单独的线程中;当收到来自客户端的请求时就调用XconnectionCreateor的create接口创建一个连接(XConnection的派生类,不同的服务派生不同),然后把该connection对象添加到Xconnectionmanager中,xconnectionmanager负责创建一个执行器对象即XExecuter去执行请求,XExecuter对象是一个单独的线程;XServiceHost继续监听接收来自其它客户端的请求,如此往复持续提供服务。
所有的应用服务只需派生XConnectionCreator和XConnection即可,前者负责创建具体的业务connection对象,如果逻辑在XConnection的派生类中处理较为复杂,则在connection中创建不同的XRequest派生类对象去执行具体的业务逻辑。
XHostContext是对服务框架所运行的主机对象的抽象,目前提供了一个退出方法即Exit,该接口在XServiceHost调用监听失败时调用,通知主机退出监听,结束服务。
21、XServiceHost类
1、构造函数
XServiceHost(XHostContext context,XConnectionCreator connectioncreator, string ip, int port)
说明:
Context 入参表示的是调用此方法的上下文,即XServiceHost对象的所有者;
Connectioncreator XConnectionCreator类的对象
Ip 表示要监听的IP
Port 表示要监听的端口;
2、Start() 函数功能是启动对应的监听线程;
3、Stop() 函数功能是停止对应的监听线程;
4、Listen() 表示此监听线程开始执行时要调用的方法;
此函数会调用C#封装好的Plasterer类来进行对指定端口的监听;使用此方法避免直接调用Socket底层的方法,简化代码,降低复杂度;需要使用到的函数AcceptSocket(),使用此函数接受挂起的连接请求;
22、XConnectionCreator接口
XConnectionCreator提供对连接创建的一个约束,不同的服务可以实现不同功能,从而实现封装和抽象,可以不改变XServiceHost的情况下进行良好的运行;
XConnection Create(Socket socket,string clientip)方法
声明创建连接的约定,具体的服务可以继承此接口进行相应功能的开发;并返回对应的连接实例对象;
23、XConnection类
XConnection类为对连接的一个抽象,为基类,具体的服务继承此基类进行对应的 *** 作;
1、XConnection(Socket socket, string clientip) 构造函数;
2、virtual void Process(Command cmd, MemoryStream packet)为抽象函数;具体的子类进行实现;cmd是命令的枚举对象;packet是对应需要 *** 作的数据;
3、public void Execute() 此函数会生成一个XSession类的对象,用来异步的获取各个系统的Socket连接;获取完数据后将数据传送到对应的XConnection实例对象,调用Process(Command cmd, MemoryStream packet)方法对消息进行相应的处理;
24、XconnectionManager类
XconnectionManager是一个使用单例模式设计的连接管理者,
Add(XConnection connection)方法使用XExecuter类的实例对象为每个连接生成一个执行器,该执行器会单独起一个线程对对应的连接进行处理;
25、XExecuter类
XExecuter用来执行请求,XExecuter对象是一个单独的线程;此类的对象会维护一个List<XConnection>队列;
1、public void Accept(XConnection con)方法该方法对外提供为该队列增加连接的功能;
2、拥有的独立线程会挂起,直到队列中出现连接,此时会唤起该线程进行该线程,并调用获取的连接对象的Execute()方法进行处理;
26、XSession类
XSession类使用Socket类的BeginReceive和EndReceive方法异步获取连接发送的数据,获取数据后使用XConnection类的Parse方法将数据传回连接对象,从而对数据进行相应的处理;
27、XRequest类
XRequest类主要负责不同消息的处理,此为一个基类,具体的响应需要子类完成,来负责收到不同消息的处理;
三、获取数据后的界面处理
对应界面各个模块来说,都相当于是一个单例,所以可以使用单例模式的方式对各个模块的控件进行管理,如Beam模块,可以抽象一个管理者来负责对其的控制;此单例负责所有针对此模块的逻辑;
目前有两种方案来进行界面响应:AsyncOperation和BackgroundWorker
31 AsyncOperation
AsyncOperation类通过回调函数可以将子线程处理完的数据传到UI线程,从而进行对应的UI *** 作;可以使用Post 函数来切换子线程到UI线程
具体实现:
1、当具体的消息传到具体的XRequest子类的实体对象后,XRequest子类对象可以调用对应消息的单例,在对应的单例里声明了对应的委托,并进行声明如:
public delegate void PushMessage(object msg);
PushMessagem_RouteMapSaved;
2、并且会提供具体的注册和取消注册的函数,在界面生成的时候就会调用注册函数进行注册,具体的注册和取消注册的函数如下:
public void BeamChangedMsg(PushMessage callback)
{
m_BeamChanged += callback;
if (null == m_Messager)
{
m_Messager = AsyncOperationManagerCreateOperation(null);
}
}
public void UnBeamChangedMsg(PushMessage callback)
{
m_BeamChanged -= callback;
}
注:m_BeamChanged为声明的PushMessage 委托对象;m_Messager为AsyncOperation类的对象。
1、进行完上述的步骤之后,就可以再具体的位置调用AsyncOperation类的 Post函数进行线程上下文切换,进入到UI线程进行对应的界面刷新,如:
m_MessagerPost(newSendOrPostCallback(m_BeamChanged), parameters);
2、当退出界面的时候可以调用UnBeamChangedMsg函数进行取消注册,避免发生异常;
32、BackgroundWorker类
BackgroundWorker类其实是对AsyncOperation类的使用的一次封装,更加简洁方便的使用异步回调的方式进行多线程编程,主要涉及到三个事件:
public event DoWorkEventHandlerDoWork; //实现开辟子线程完成耗时 *** 作
public event ProgressChangedEventHandlerProgressChanged;//实现返回 *** 作进度或自定义参数
public event RunWorkerCompletedEventHandlerRunWorkerCompleted;//完成后返回数据的处理;
需要注意的是DoWork事件是在子线程完成,具体函数不能包含对UI控件的 *** 作;
具体的UI *** 作可以放入到ProgressChanged和RunWorkerCompleted事件中完成。
具体实现:
与使用AsyncOperation基本一致:
首先在对应的单例中声明BackgroundWorker,在创建UI的时候对BackgroundWorker对象进行各个事件的注册,最后使用RunWorkerAsync方法进行调用;
四、通信工具
通信工具类
该工具类提供网络通信的两个接口,一个是只发送请求的SendRequest函数,另一个是先发送请求然后等待服务端返回结果的GetResponse函数;SendRequestByLongConnection用于建立长连接的通讯,ReleaseConnection函数用来释放连接;
以上就是关于iOS之runtime详解api(一)全部的内容,包括:iOS之runtime详解api(一)、java枚举类型中的instance用法、c#如何根据字符串获得枚举对象对应的值等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)