`
zjjzmw1
  • 浏览: 1366194 次
  • 性别: Icon_minigender_1
  • 来自: 开封
社区版块
存档分类
最新评论

TCP与UDP区别和oc中消息传递机制-附:对performSelector方法的扩充

    博客分类:
  • iOS
阅读更多
TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快
Overview
TCP (Transmission Control Protocol) is the most commonly used protocol on the Internet. The reason for this is because TCP offers error correction. When the TCP protocol is used there is a "guaranteed delivery." This is due largely in part to a method called "flow control." Flow control determines when data needs to be re-sent, and stops the flow of data until previous packets are successfully transferred. This works because if a packet of data is sent, a collision may occur. When this happens, the client re-requests the packet from the server until the whole packet is complete and is identical to its original.

UDP (User Datagram Protocol) is anther commonly used protocol on the Internet. However, UDP is never used to send important data such as webpages, database information, etc; UDP is commonly used for streaming audio and video. Streaming media such as Windows Media audio files (.WMA) , Real Player (.RM), and others use UDP because it offers speed! The reason UDP is faster than TCP is because there is no form of flow control or error correction. The data sent over the Internet is affected by collisions, and errors will be present. Remember that UDP is only concerned with speed. This is the main reason why streaming media is not high quality.

oc中消息传递机制-附:对performSelector方法的扩充

各种语言都有些传递函数的方法:C语言中可以使用函数指针,C++中有函数引用、仿函数和lambda,Objective-C里也有选择器(selector)和block。
不过由于iOS SDK中的大部分API都是selector的方式,所以本文就重点讲述selector了。

Objective-C和我接触过的其他面向对象的语言不同,它强调消息传递,而非方法调用。因此你可以对一个对象传递任何消息,而不需要在编译期声名这些消息的处理方法。
很显然,既然编译期并不能确定方法的地址,那么运行期就需要自行定位了。而Objective-C runtime就是通过“id objc_msgSend(id theReceiver, SEL theSelector, ...)”这个函数来调用方法的。其中theReceiver是调用对象,theSelector则是消息名,省略号就是C语言的不定参数了。
这里的消息名是SEL类型,它被定义为struct objc_selector *。不过文档中并没有透露objc_selector是什么东西,但提供了@selector指令来生成:
SEL selector = @selector(message);
@selector是在编译期计算的,所以并不是函数调用。更进一步的测试表明,它在Mac OS X 10.6和iOS下都是一个C风格的字符串(char*):
NSLog (@"%s", (char *)selector);
你会发现结果是“message”这个消息名。

下面就写个测试类:

@interface Test : NSObject
@end

@implementation Test

- (NSString *)intToString:(NSInteger)number {
return [NSString stringWithFormat:@"%d", number];
}

- (NSString *)doubleToString:(double *)number {
return [NSString stringWithFormat:@"%f", *number];
}

- (NSString *)pointToString:(CGPoint)point {
return [NSString stringWithFormat:@"{%f, %f}", point.x, point.y];
}

- (NSString *)intsToString:(NSInteger)number1 second:(NSInteger)number2 third:(NSInteger)number3 {
return [NSString stringWithFormat:@"%d, %d, %d", number1, number2, number3];
}

- (NSString *)doublesToString:(double)number1 second:(double)number2 third:(double)number3 {
return [NSString stringWithFormat:@"%f, %f, %f", number1, number2, number3];
}

- (NSString *)combineString:(NSString *)string1 withSecond:string2 withThird:string3 {
return [NSString stringWithFormat:@"%@, %@, %@", string1, string2, string3];
}

@end
再来测试下objc_msgSend:

#import <objc/message.h>
//要使用objc_msgSend的话,就要引入这个头文件

Test *test = [[Test alloc] init];
CGPoint point = {123, 456};
NSLog(@"%@", objc_msgSend(test, @selector(pointToString:), point));
[test release];
结果是“{123.000000, 456.000000}”。而且与之前猜想的一样,下面这样调用也是可以的:
NSLog(@"%@", objc_msgSend(test, (SEL)"pointToString:", point));
看到这里你应该发现了,这种实现方式只能确定消息名和参数数目,而参数类型和返回类型就给抹杀了。所以编译器只能在编译期警告你参数类型不对,而无法阻止你传递类型错误的参数。

接下来再看看NSObject协议提供的一些传递消息的方法:
- (id)performSelector:(SEL)aSelector
- (id)performSelector:(SEL)aSelector withObject:(id)anObject
- (id)performSelector:(SEL)aSelector withObject:(id)anObject withObject:(id)anotherObject
也没有觉得很无语?为什么参数必须是对象?为什么最多只支持2个参数?

好在selector本身也不在乎参数类型,所以传个不是对象的玩意也行:
NSLog(@"%@", [test performSelector:@selector(intToString:) withObject:(id)123]);
可是double和struct就不能这样传递了,因为它们占的字节数和指针不一样。如果非要用performSelector的话,就只能修改参数类型为指针了:
- (NSString *)doubleToString:(double *)number {
return [NSString stringWithFormat:@"%f", *number];
}

double number = 123.456;
NSLog(@"%@", [test performSelector:@selector(doubleToString:) withObject:(id)(&number)]);
参数类型算是搞定了,可是要支持多个参数,还得费番气力。理想状态下,我们应该可以实现这2个方法:
@interface NSObject (extend)

- (id)performSelector:(SEL)aSelector withObjects:(NSArray *)objects;
- (id)performSelector:(SEL)aSelector withParameters:(void *)firstParameter, ...;

@end
先看看前者,NSArray要求所有的元素都必须是对象,并且不能为nil,所以适用的范围仍然有限。不过你可别小看它,因为你会发现根本没法用objc_msgSend来实现,因为你在写代码时没法预知参数个数。
这时候就轮到NSInvocation登场了:
@implementation NSObject (extend)

- (id)performSelector:(SEL)aSelector withObjects:(NSArray *)objects {
NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self];
[invocation setSelector:aSelector];

NSUInteger i = 1;
for (id object in objects) {
[invocation setArgument:&object atIndex:++i];
}
[invocation invoke];

if ([signature methodReturnLength]) {
id data;
[invocation getReturnValue:&data];
return data;
}
return nil;
}

@end


NSLog(@"%@", [test performSelector:@selector(combineString:withSecond:withThird:) withObjects:[NSArray arrayWithObjects:@"1", @"2", @"3", nil]]);
这里有3点要注意的:
因为方法调用有self(调用对象)和_cmd(选择器)这2个隐含参数,因此设置参数时,索引应该从2开始。
因为参数是对象,所以必须传递指针,即&object。
methodReturnLength为0时,表明返回类型是void,因此不需要获取返回值。返回值是对象的情况下,不需要我们来创建buffer。但如果是C风格的字符串、数组等类型,就需要自行malloc,并释放内存了。

再来实现第2个方法:
- (id)performSelector:(SEL)aSelector withParameters:(void *)firstParameter, ... {
NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];
NSUInteger length = [signature numberOfArguments];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self];
[invocation setSelector:aSelector];

[invocation setArgument:&firstParameter atIndex:2];
va_list arg_ptr;
va_start(arg_ptr, firstParameter);
for (NSUInteger i = 3; i < length; ++i) {
void *parameter = va_arg(arg_ptr, void *);
[invocation setArgument:&parameter atIndex:i];
}
va_end(arg_ptr);

[invocation invoke];

if ([signature methodReturnLength]) {
id data;
[invocation getReturnValue:&data];
return data;
}
return nil;
}

NSLog(@"%@", [test performSelector:@selector(combineString:withSecond:withThird:) withParameters:@"1", @"2", @"3"]);

NSInteger number1 = 1, number2 = 2, number3 = 3;
NSLog(@"%@", [test performSelector:@selector(intsToString:second:third:) withParameters:number1, number2, number3]);
和前面的实现差不多,不过由于参数长度是未知的,所以用到了[signature numberOfArguments]。当然也可以把SEL转成字符串(可用NSStringFromSelector()),然后查找:的数量。
处理可变参数时用到了va_start、va_arg和va_end,熟悉C语言的一看就明白了。
不过由于不知道参数的类型,所以只能设为void *。而这个程序也报出了警告,说void *和NSInteger类型不兼容。而如果把参数换成double,那就直接报错了。遗憾的是我也不知道怎么判别一个void *指针究竟是指向C数据类型,还是指向一个Objective-C对象,所以最好是封装成Objective-C对象。如果只需要兼容C类型的话,倒是可以将setArgument的参数的&去掉,然后直接传指针进去:
NSInteger number1 = 1, number2 = 2, number3 = 3;
NSLog(@"%@", [test performSelector:@selector(intsToString:second:third:) withParameters:&number1, &number2, &number3]);

double number4 = 1.0, number5 = 2.0, number6 = 3.0;
NSLog(@"%@", [test performSelector:@selector(doublesToString:second:third:) withParameters:&number4, &number5, &number6]);
[test release];
至于NSObject类添加的performSelector:withObject:afterDelay:等方法,也可以用这种方式来支持多个参数。

接下来再说说刚才略过的_cmd,它还可以用来实现递归调用。下面就以斐波那契数列为例:
- (NSInteger)fibonacci:(NSInteger)n {
if (n > 2) {
return [self fibonacci:n - 1] + [self fibonacci:n - 2];
}
return n > 0 ? 1 : 0;
}
改成用_cmd实现就变成了这样:
return (NSInteger)[self performSelector:_cmd withObject:(id)(n - 1)] + (NSInteger)[self performSelector:_cmd withObject:(id)(n - 2)];
或者直接用objc_msgSend:
return (NSInteger)objc_msgSend(self, _cmd, n - 1) + (NSInteger)objc_msgSend(self, _cmd, n - 2);
但是每次都通过objc_msgSend来调用显得很费劲,有没有办法直接进行方法调用呢?答案是有的,这就需要用到IMP了。IMP的定义为“id (*IMP) (id, SEL, …)”,也就是一个指向方法的函数指针。
NSObject提供methodForSelector:方法来获取IMP,因此只需稍作修改就行了:
- (NSInteger)fibonacci:(NSInteger)n {
static IMP func;
if (!func) {
func = [self methodForSelector:_cmd];
}

if (n > 2) {
return (NSInteger)func(self, _cmd, n - 1) + (NSInteger)func(self, _cmd, n - 2);
}
return n > 0 ? 1 : 0;
}
现在运行时间比刚才减少了1/4,还算不错。

顺便再展现一下Objective-C强大的动态性,给Test类添加一个sum:and:方法:
NSInteger sum(id self, SEL _cmd, NSInteger number1, NSInteger number2) {
return number1 + number2;
}

class_addMethod([Test class], @selector(sum:and:), (IMP)sum, "i@:ii");
NSLog(@"%d", [test sum:1 and:2]);
class_addMethod的最后那个参数是函数的返回值和参数类型,详细内容可以参考Type Encodings文档。



分享到:
评论

相关推荐

    OC-performSelector

    在Objective-C(简称OC)中,`performSelector`方法是基于消息传递机制的关键特性之一,它允许我们在运行时动态地调用对象的方法。这个特性使得OC具有高度的灵活性和动态性,是许多设计模式和编程实践的基础。接下来...

    Swift和OC混编

    Swift和Objective-C(OC)混编是iOS开发中常见的场景,因为很多老项目基于OC编写,而新功能或模块可能采用Swift实现。本教程将详细阐述如何在OC项目中引入Swift代码,以及在Swift项目中调用OC代码,以便充分利用两者...

    OC-performSelectorInBackground

    在iOS和Mac开发中,Objective-C(简称OC)是一种广泛使用的编程语言,特别是在Apple的UIKit框架下构建用户界面时。`performSelectorInBackground:`是OC中一个非常关键的方法,用于在后台线程执行任务,这对于避免...

    ios demo,performSelector和respondsToSelector

    在iOS开发中,Objective-C是一门强大的面向对象的语言,它提供了许多高级特性,如消息传递、动态类型和运行时机制。本示例“ios demo,performSelector和respondsToSelector”着重探讨了两个关键的编程概念:`...

    UIWindow-TouchIndicatorExtension:用于指示用户在屏幕上的触摸的 UIWindow 扩展

    用法将文件UIWindow+TouchIndicatorExtension.m添加到您的项目中,并在应用程序委托的 -applicationDidBecomeActive: 方法末尾调用以下代码以激活触摸指示器。 UIWindow *keyWindow = [[UIApplication ...

    OC-performSelectorOnMainThread

    在iOS和Mac开发中,Objective-C(简称OC)是一种广泛使用的编程语言,特别是在Apple的UIKit框架下构建用户界面的应用程序。`performSelectorOnMainThread:` 是一个非常关键的OC方法,用于在主线程上执行某个指定的...

    leetcode题库-iOS-Interview-Questions:收集整理iOS笔试面试题

    http和socket通信的区别,tcp和udp的区别,session和cookie的区别 block实现原理 响应链 frame 和 bounds 写一个宏MIN,这个宏输入两个参数并返回较小的一个 #define MIN(a,b) ((a)&gt;(b)?(b):(a)) 什么是KeyPath #...

    OC与Lua交互调用(不使用第三方库)Demo

    在iOS开发中,Objective-C(OC)和Lua的交互是一个常见的需求,特别是在游戏开发中,因为Lua具有轻量级、脚本化的特性,适合用于处理游戏逻辑和动态内容。本Demo提供了一种不依赖第三方库的OC与Lua交互方式,通过...

    smooth-objc-debug:objc可调式可编译的原始代码,添加了理解注释,帮助自己更好的理解能够

    了解消息传递机制,包括`-[NSObject performSelector:]`,`NSInvocation`的使用,以及动态方法解析等。 2. **类别(Class Categories)**:类别可以为已有的类添加方法,而不扩展其实例变量。理解类别如何工作,以及...

    C++ oc 互相调用

    OC调用C++主要通过在OC类中引入C++代码或使用`@selector`和`performSelector:withObject:`方法。以下是一个示例: 1. 创建C++类(如`MyCppClass.cpp`): ```cpp #include "MyCppClass.h" void MyCppClass::...

    objective c 消息 objc_msgSend

    在Objective-C中,消息传递是实现对象间通信的核心机制,`objc_msgSend`函数就是这个机制的底层实现。本文将深入探讨`objc_msgSend`以及与其相关的`performSelector`方法,并通过实例来阐述它们的工作原理和用法。 ...

    ios-简单选择器.zip

    在iOS开发中,选择器(Selector)是一种特殊的消息传递机制,用于对象间的通信。这个“ios-简单选择器.zip”文件很可能包含了一个示例项目,演示了如何在iOS应用中自定义选择器的使用。下面将详细介绍选择器的概念、...

    ShutDownMyMac-OSX:OSX 应用程序使用 Bonjour 与 iOS 设备通信远程关闭 mac

    在macOS应用中,开发者会创建一个NSNetService对象,设置服务的名称、类型(例如 "_shutdown._tcp")和端口,然后发布这个服务。在iOS应用中,通过NSNetServiceBrowser对象搜索网络中的相应服务类型,并在找到匹配的...

    iOS面试题-iOS部分

    在iOS面试中,掌握核心知识点至关重要,这有助于应聘者展示其专业技能和理解力。以下是一些关于iOS面试的关键知识点: 1. **MVC模式**: - MVC(Model-View-Controller)是一种广泛用于iOS开发的软件设计模式。...

    kkboxiOS开发

    - 在`performSelector:`等方法中用于传递要执行的方法。 - **注意事项**: - 使用`performSelector:`时需确保对象不是nil。 - 需要注意方法签名与传入参数的匹配。 **Category** - **定义**:Category允许为已...

    Runtime 万能控制器跳转.zip

    Runtime系统在程序运行过程中动态解析类、方法等信息,使得OC具有了消息传递、动态类型、动态加载等特性。在"Runtime 万能控制器跳转.zip"这个压缩包中,我们可以推测其内容可能与利用Runtime进行控制器的动态跳转...

    swift performSelector

    swift并没有提供performSelector ,我伪代码写了一个扩展类。使用时请小心。详见我博文说明。http://blog.csdn.net/fengsh998/article/details/35842441

    OC基础知识

    在深入探讨OC基础知识之前,我们先理解OC的核心特性:消息传递、类与对象、继承、多态和动态性。 1. **消息传递** 在OC中,对象间的通信是通过发送消息实现的。比如,`[receiver performSelector:@selector(method...

    objective-c 程序设计 + 代码

    2. **消息传递**:Objective-C使用消息传递机制进行对象间的通信。这类似于其他语言中的方法调用,但更灵活。例如,`[object performSelector:@selector(methodName)];`发送一个消息到`object`,执行`methodName`...

Global site tag (gtag.js) - Google Analytics