iOS - AsyncSocket 的使用

简介: 1、AsyncSocket基于 CFSocket、GCD 进行的封装(OC)。支持 TCP 和 UDP。完整的回调函数(用于处理各种回调事件,连接成功,断开连接,收到数据等)。需要注意的问题:1、Socekt 连接成功回调方法中主动调用:[self.socket readDataWithTimeout:-1 tag:0];,相当于主动添加一个读取请求,不然不会执行读取信息回调方法。

1、AsyncSocket

  • 基于 CFSocket、GCD 进行的封装(OC)。

  • 支持 TCP 和 UDP。

  • 完整的回调函数(用于处理各种回调事件,连接成功,断开连接,收到数据等)。

  • 需要注意的问题:

    • 1、Socekt 连接成功回调方法中主动调用:[self.socket readDataWithTimeout:-1 tag:0];,相当于主动添加一个读取请求,不然不会执行读取信息回调方法。
    • 2、读取信息回调方法中,读取完信息后,主动调用一下 [self.socket readDataWithTimeout:-1 tag:0];,读取完信息后,重新向队列中添加一个读取请求,不然当收到信息后不会执行读取回调方法。

2、基本使用

2.1 Client 客户端

  • TCP 客户端

        #import "GCDAsyncSocket.h"
    
        @interface ViewController () <GCDAsyncSocketDelegate>
    
        @property (weak, nonatomic) IBOutlet UITextField *addrTF;
        @property (weak, nonatomic) IBOutlet UITextField *portTF;
    
        @property (weak, nonatomic) IBOutlet UITextField *msgTF;
        @property (weak, nonatomic) IBOutlet UITextView *logTV;
    
        @property (nonatomic, strong) GCDAsyncSocket *clientSockfd;
    
        @end
    
        @implementation ViewController
    
        - (void)viewDidLoad {
            [super viewDidLoad];
    
            // 创建 Socket,在主队列中处理,所有的回执都在主队列中执行。
            self.clientSockfd = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
        }
    
        - (IBAction)connectBtnClick:(id)sender {
    
            NSError *error = nil;
    
            // Socket 连接
            if (![self.clientSockfd isConnected]) {
                [self.clientSockfd connectToHost:_addrTF.text onPort:_portTF.text.intValue error:&error];
            }
    
            if (error != nil) {
                [self showLogMessage:@"连接失败..."];
            }
        }
    
        #pragma mark - GCDAsyncSocketDelegate 协议方法
    
        // 连接成功,协议方法
        - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
    
            // 读取数据,必须添加,相当于主动添加一个读取请求,不然不会执行读取信息回调方法
            [self.clientSockfd readDataWithTimeout:-1 tag:0];
    
            [self showLogMessage:[NSString stringWithFormat:@"连接服务器地址:%@, 端口:%d 成功", host, port]];
            [self showLogMessage:[NSString stringWithFormat:@"本地地址:%@, 端口:%d", sock.localHost, sock.localPort]];
        }
    
        // 已经断开链接,协议方法
        - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
    
            [self showLogMessage:@"socket 断开连接..."];
        }
    
        // 读取到数据,协议方法
        - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    
            // 注意:要想长连接,必须还要在 DidReceiveData 的 delegate 中再写一次 [_udpSocket receiveOnce:&error]
    
            // 读取数据,读取完信息后,重新向队列中添加一个读取请求,不然当收到信息后不会执行读取回调方法。
            [self.clientSockfd readDataWithTimeout:-1 tag:0];
    
            NSString *strMsg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            [self showLogMessage:[NSString stringWithFormat:@"recv:%@", strMsg]];
        }
    
        #pragma mark - 发送数据
    
        - (IBAction)sendBtnClick:(id)sender {
    
            // Socket 发送数据
            [self.clientSockfd writeData:[_msgTF.text dataUsingEncoding:NSUTF8StringEncoding] withTimeout:30 tag:0];
    
            [self showLogMessage:[NSString stringWithFormat:@"send:%@", _msgTF.text]];
        }
    
        // 显示信息
        - (void)showLogMessage:(NSString *)msg {
    
            _logTV.text = [_logTV.text stringByAppendingFormat:@"%@\n", msg];
        }
    
        // 键盘回收
        - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
            [self.view endEditing:YES];
        }
    
        @end

    Socket11

2.2 Server 服务端

  • TCP 服务端

        #import <arpa/inet.h>
        #import <ifaddrs.h>
    
        #import "GCDAsyncSocket.h"
    
        @interface ViewController () <GCDAsyncSocketDelegate>
    
        @property (weak, nonatomic) IBOutlet UITextField *addrTF;
        @property (weak, nonatomic) IBOutlet UITextField *portTF;
    
        @property (weak, nonatomic) IBOutlet UITextField *msgTF;
        @property (weak, nonatomic) IBOutlet UITextView *logTV;
    
        @property (nonatomic, strong) GCDAsyncSocket *serverSocketfd;
        @property (nonatomic, strong) GCDAsyncSocket *clientSocketfd;
    
        @end
    
        @implementation ViewController
    
        - (void)viewDidLoad {
            [super viewDidLoad];
    
            NSString *ip_addr = [self getIPAddress];
            _addrTF.text = ip_addr;
    
            // 创建 Socket,在主队列中处理,所有的回执都在主队列中执行。
            self.serverSocketfd = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
        }
    
        - (IBAction)listenBtnClick:(id)sender {
    
            NSError *error = nil;
    
            // Socket 监听
            [self.serverSocketfd acceptOnPort:_portTF.text.intValue error:&error];
    
            if (error != nil) {
                NSLog(@"监听出错:%@", error);
            } else{
                [self showLogMessage:@"正在监听..."];
            }
        }
    
        #pragma mark - GCDAsyncSocketDelegate 协议方法
    
        // 接收到连接请求,协议方法
        - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket {
    
            // 读取数据,必须添加,相当于主动添加一个读取请求,不然不会执行读取信息回调方法
            [newSocket readDataWithTimeout:-1 tag:0];
    
            [self showLogMessage:@"收到客户端连接...."];
            [self showLogMessage:[NSString stringWithFormat:@"客户端地址:%@, 端口:%d", newSocket.connectedHost, newSocket.connectedPort]];
    
            self.clientSocketfd = newSocket;
        }
    
        // 已经断开链接,协议方法
        - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
    
            [self showLogMessage:@"socket 断开连接..."];
        }
    
        // 读取到数据,协议方法
        - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    
            // 读取数据,读取完信息后,重新向队列中添加一个读取请求,不然当收到信息后不会执行读取回调方法。
            [sock readDataWithTimeout:-1 tag:0];
    
            NSString *strMsg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            [self showLogMessage:[NSString stringWithFormat:@"recv:%@",strMsg]];
        }
    
        #pragma mark - 发送数据
    
        - (IBAction)sendBtnClick:(id)sender {
    
            // Socket 发送数据
            [self.clientSocketfd writeData:[_msgTF.text dataUsingEncoding:NSUTF8StringEncoding] withTimeout:30 tag:0];
    
            [self showLogMessage:[NSString stringWithFormat:@"send:%@", _msgTF.text]];
        }
    
        #pragma mark - 获取本地 IP 地址
    
        - (NSString *)getIPAddress {
    
            NSString *address = @"error";
            struct ifaddrs *interfaces = NULL;
            struct ifaddrs *temp_addr = NULL;
            int success = 0;
    
            // retrieve the current interfaces - returns 0 on success
            success = getifaddrs(&interfaces);
    
            if (success == 0) {
    
                // Loop through linked list of interfaces
                temp_addr = interfaces;
    
                while (temp_addr != NULL) {
    
                    if (temp_addr->ifa_addr->sa_family == AF_INET) {
    
                        // Check if interface is en0 which is the wifi connection on the iPhone
                        if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {
    
                            // Get NSString from C String
                            address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
                        }
                    }
                    temp_addr = temp_addr->ifa_next;
                }
            }
    
            // Free memory
            freeifaddrs(interfaces);
            return address;
        }
    
        // 显示信息
        - (void)showLogMessage:(NSString *)msg {
    
            _logTV.text = [_logTV.text stringByAppendingFormat:@"%@\n", msg];
        }
    
        // 键盘回收
        - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
            [self.view endEditing:YES];
        }
    
        @end

    Socket12

2.3 UDP 通信

  • UDP 通信

        #import <arpa/inet.h>
        #import <ifaddrs.h>
    
        #import "GCDAsyncUdpSocket.h"
    
        @interface ViewController () <GCDAsyncUdpSocketDelegate>
    
        @property (weak, nonatomic) IBOutlet UITextField *desAddrTF;
        @property (weak, nonatomic) IBOutlet UITextField *desPortTF;
    
        @property (weak, nonatomic) IBOutlet UITextField *locAddrTF;
        @property (weak, nonatomic) IBOutlet UITextField *locPortTF;
    
        @property (weak, nonatomic) IBOutlet UITextField *msgTF;
        @property (weak, nonatomic) IBOutlet UITextView *logTV;
    
        @property (nonatomic, strong) GCDAsyncUdpSocket *udpSocketfd;
    
        @end
    
        @implementation ViewController
    
        - (void)viewDidLoad {
            [super viewDidLoad];
    
            NSString *ip_addr = [self getIPAddress];
            _locAddrTF.text = ip_addr;
    
            // 创建 Socket,在主队列中处理,所有的回执都在主队列中执行
            self.udpSocketfd = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
        }
    
        - (IBAction)connectBtnClick:(id)sender {
    
            NSError *error = nil;
    
            // 绑定端口
            [self.udpSocketfd bindToPort:_locPortTF.text.intValue error:&error];
    
            if (error != nil) {
                NSLog(@"绑定端口出错:%@", error);
                return;
            } else{
                [self showLogMessage:[NSString stringWithFormat:@"绑定端口 %d 成功...", _locPortTF.text.intValue]];
            }
    
            // 开始接收数据
            [self.udpSocketfd beginReceiving:&error];
    
            if (error != nil) {
                NSLog(@"开始接收数据出错:%@", error);
            } else{
                [self showLogMessage:@"开始接收数据..."];
            }
        }
    
        #pragma mark - GCDAsyncUdpSocketDelegate 协议方法
    
        // 接收到的数据,协议方法
        - (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
                                                       fromAddress:(NSData *)address
                                                 withFilterContext:(id)filterContext {
    
            NSString *strMsg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
            [self showLogMessage:[NSString stringWithFormat:@"recv:%@", strMsg]];
        }
    
        #pragma mark - 发送数据
    
        - (IBAction)sendBtnClick:(id)sender {
    
            // Socket 发送数据
            [self.udpSocketfd sendData:[_msgTF.text dataUsingEncoding:NSUTF8StringEncoding]
                                toHost:_desAddrTF.text
                                  port:_desPortTF.text.intValue
                           withTimeout:30 tag:0];
    
            [self showLogMessage:[NSString stringWithFormat:@"send:%@", _msgTF.text]];
        }
    
        #pragma mark - 获取本地 IP 地址
    
        - (NSString *)getIPAddress {
    
            NSString *address = @"error";
            struct ifaddrs *interfaces = NULL;
            struct ifaddrs *temp_addr = NULL;
            int success = 0;
    
            // retrieve the current interfaces - returns 0 on success
            success = getifaddrs(&interfaces);
    
            if (success == 0) {
    
                // Loop through linked list of interfaces
                temp_addr = interfaces;
    
                while (temp_addr != NULL) {
    
                    if (temp_addr->ifa_addr->sa_family == AF_INET) {
    
                        // Check if interface is en0 which is the wifi connection on the iPhone
                        if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {
    
                            // Get NSString from C String
                            address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
                        }
                    }
                    temp_addr = temp_addr->ifa_next;
                }
            }
    
            // Free memory
            freeifaddrs(interfaces);
            return address;
        }
    
        // 显示信息
        - (void)showLogMessage:(NSString *)msg {
    
            _logTV.text = [_logTV.text stringByAppendingFormat:@"%@\n", msg];
        }
    
        // 键盘回收
        - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
            [self.view endEditing:YES];
        }
    
        @end

    Socket13

2.4 Socket 长连接

  • PublicTool.h

        @interface PublicTool : NSObject
    
        // 字典转换成 JSON 字符串
        + (NSString *)JSONStringWithDic:(NSDictionary *)dic;
    
        // JSON 字符串转换成字典
        + (NSDictionary *)dictionaryWithJSON:(NSString *)json;
    
        @end
  • PublicTool.m

        @implementation PublicTool
    
        // 字典转成成 JSON 字符串
        + (NSString *)JSONStringWithDic:(NSDictionary *)dic {
    
            NSError *error = nil;
    
            NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic
                                                               options:0
                                                                 error:&error];
    
            if (jsonData == nil) {
                NSLog(@"fail to get JSON from dictionary: %@, error: %@", self, error);
    
                return nil;
            }
            NSString *jsonString = [[NSString alloc] initWithData:jsonData
                                                         encoding:NSUTF8StringEncoding];
    
            return jsonString;
        }
    
        // JSON 字符串转换成字典
        + (NSDictionary *)dictionaryWithJSON:(NSString *)json {
    
            NSError *error = nil;
    
            NSData *jsonData = [json dataUsingEncoding:NSUTF8StringEncoding];
    
            NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData
                                                                     options:NSJSONReadingMutableContainers |
                                                                             NSJSONReadingAllowFragments
                                                                       error:&error];
    
            if (jsonDict == nil) {
                NSLog(@"fail to get dictioanry from JSON: %@, error: %@", json, error);
    
                return nil;
            }
    
            return jsonDict;
        }
    
        @end
  • SocketSingleTon.h

        @interface SocketSingleTon : NSObject
    
        @property (nonatomic, copy) NSString *hostAddr;
        @property (nonatomic, copy) NSString *port;
    
        @property (nonatomic, copy) void(^msgLog)(NSString *);
    
        + (instancetype)shareInstance;
    
        // 连接到服务器
        - (void)connectToServer;
    
        // 断开连接
        - (void)cutOffSocket;
    
        // 发送数据到服务器
        - (void)sendDataToServer:(NSData*)data;
    
        @end
  • SocketSingleTon.m

        #import "GCDAsyncSocket.h"
        #import <netdb.h>
        #import <arpa/inet.h>
    
        #import "PublicTool.h"
    
        typedef NS_ENUM(NSInteger, SocketOffline) {
            SocketOfflineByServer,
            SocketOfflineByUser
        };
    
        @interface SocketSingleTon () <GCDAsyncSocketDelegate>
    
        @property (nonatomic, strong) GCDAsyncSocket *socket;
    
        @property (nonatomic, strong) NSTimer *beatTimer;
    
        @end
    
        @implementation SocketSingleTon
    
        + (instancetype)shareInstance {
    
            static SocketSingleTon *shareInstance = nil;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                shareInstance = [[self alloc] init];
            });
            return shareInstance;
        }
    
        // 连接到服务器
        - (void)connectToServer {
    
            NSError *error = nil;
    
            if (self.socket != nil) {
    
                if ([self.socket isConnected]) {
    
                    // 断开后再连接
                    self.socket.userData = @(SocketOfflineByUser);
                    [self cutOffSocket];
    
                    [self.socket connectToHost:self.hostAddr onPort:self.port.intValue error:&error];
    
                } else {
    
                    [self.socket connectToHost:self.hostAddr onPort:self.port.intValue error:&error];
                }
    
            } else {
                self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    
                [self.socket connectToHost:self.hostAddr onPort:self.port.intValue error:&error];
            }
    
            if (error != nil) {
                NSLog(@"socket 连接失败:%@", error);
            } else {
                NSLog(@"socket 连接成功");
            }
        }
    
        // 断开连接
        - (void)cutOffSocket {
    
            self.socket.userData = @(SocketOfflineByUser);
            [self.socket disconnect];
        }
    
        #pragma mark  - GCDAsyncSocketDelegate 协议方法
    
        // 连接成功,协议方法
        - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
    
            [sock readDataWithTimeout:-1 tag:0];
    
            NSString *logStr = [NSString stringWithFormat:@"连接主机:%@:%d 成功\n", host, port];
            NSLog(@"%@", logStr);
    
            if (self.msgLog) {
                self.msgLog(logStr);
            }
    
            // 创建定时器,定时发送 beat 包
            self.beatTimer = [NSTimer scheduledTimerWithTimeInterval:5 
                                                              target:self 
                                                            selector:@selector(longConnectToServer) 
                                                            userInfo:nil 
                                                             repeats:YES];
        }
    
        // 连接断开,协议方法
        - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
    
            self.socket = nil;
    
            [self.beatTimer invalidate];
            self.beatTimer = nil;
    
            if ([sock.userData  isEqual: @(SocketOfflineByUser)]) {
    
                NSLog(@"the socket have been cutted off by user");
    
                if (self.msgLog) {
                    self.msgLog(@"the socket have been cutted off by user");
                }
    
            } else if (sock.userData == SocketOfflineByServer) {
    
                NSLog(@"the socket have been cutted off by server");
    
                if (self.msgLog) {
                    self.msgLog(@"the socket have been cutted off by server");
                }
    
                // reconnect
                [self connectToServer];
    
            } else {
    
                NSLog(@"%@", err.localizedDescription);
    
                if (self.msgLog) {
                    self.msgLog([NSString stringWithFormat:@"%@", err.localizedDescription]);
                }
    
                [self connectToServer];
            }
        }
    
        // 收到消息,协议方法
        - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    
            [sock readDataWithTimeout:-1 tag:0];
    
            char buf[1024];
            [data getBytes:buf length:1024];
    
            NSString *receivedData = [NSString stringWithCString:buf encoding:NSUTF8StringEncoding];
            NSLog(@"receivedData:%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    
            if (receivedData.length > 0) {
    
                NSDictionary *dataDic = [PublicTool dictionaryWithJSON:receivedData];
    
                if ([dataDic[@"msgType"] isEqualToString:@"beta"]) {
                    NSString *strMsg = [NSString stringWithFormat:@"收到心跳确认的数据:%@\n", receivedData];
                    if (self.msgLog) {
                        self.msgLog(strMsg);
                    }
                } else if ([dataDic[@"msgType"] isEqualToString:@"normal"]) {
                    NSString *strMsg = [NSString stringWithFormat:@"收到正常的数据:%@\n", receivedData];
                    if (self.msgLog) {
                        self.msgLog(strMsg);
                    }
                } else if ([dataDic[@"msgType"] isEqualToString:@"exit"]) {
                    NSString *strMsg = [NSString stringWithFormat:@"收到关闭的数据:%@\n", receivedData];
                    if (self.msgLog) {
                        self.msgLog(strMsg);
                    }
                    [self cutOffSocket];
                }
            }
        }
    
        // 发送数据
        - (void)longConnectToServer {
            [self sendDataToServer:[@"hello" dataUsingEncoding:NSUTF8StringEncoding]];
        }
    
        // 发送数据到服务器
        - (void)sendDataToServer:(NSData*)data {
    
            NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSMutableDictionary *dicParams = [NSMutableDictionary dictionary];
    
            if ([dataStr isEqualToString:@"hello"]) {
                [dicParams setValue:dataStr forKey:@"msg"];
                [dicParams setValue:@"beta" forKey:@"msgType"];
            } else {
                [dicParams setValue:dataStr forKey:@"msg"];
                [dicParams setValue:@"normal" forKey:@"msgType"];
            }
    
            NSData *sendData = [[PublicTool JSONStringWithDic:dicParams] dataUsingEncoding:NSUTF8StringEncoding];
    
            NSString *strMsg = [NSString stringWithFormat:@"发送数据: %@\n", [PublicTool JSONStringWithDic:dicParams]];
            if (self.msgLog) {
                self.msgLog(strMsg);
            }
    
            [self.socket writeData:sendData withTimeout:30 tag:0];
        }
    
        @end
  • ViewController.m

        #import "SocketSingleTon.h"
        #import "PublicTool.h"
    
        @interface ViewController ()
    
        @property (weak, nonatomic) IBOutlet UITextField *addressTF;
        @property (weak, nonatomic) IBOutlet UITextField *portTF;
    
        @property (weak, nonatomic) IBOutlet UITextField *msgTF;
        @property (weak, nonatomic) IBOutlet UITextView *logTV;
    
        @end
    
        @implementation ViewController
    
        - (IBAction)connectToServer:(id)sender {
    
            // 创建 Socket
            SocketSingleTon *socketInstance = [SocketSingleTon shareInstance];
    
            socketInstance.hostAddr = _addressTF.text;
            socketInstance.port = _portTF.text;
    
            __weak __typeof (self)weakSelf = self;
            socketInstance.msgLog = ^(NSString *log){
    
                dispatch_async(dispatch_get_main_queue(), ^{
                    weakSelf.logTV.text = [weakSelf.logTV.text stringByAppendingString:log];
                });
            };
    
            // 连接到服务器
            [socketInstance connectToServer];
        }
    
        - (IBAction)cutOffConnect:(id)sender {
    
            SocketSingleTon *socketInstance = [SocketSingleTon shareInstance];
    
            // 断开连接
            [socketInstance cutOffSocket];
        }
    
        - (IBAction)sendDataToServer:(id)sender {
    
            SocketSingleTon *socketInstance = [SocketSingleTon shareInstance];
    
            // 发送数据到服务器
            [socketInstance sendDataToServer:[_msgTF.text dataUsingEncoding:NSUTF8StringEncoding]];
        }
    
        - (IBAction)sendBetaDataToServer:(id)sender {
    
            SocketSingleTon *socketInstance = [SocketSingleTon shareInstance];
    
            NSMutableDictionary *dicParams = [NSMutableDictionary dictionary];
            [dicParams setValue:@"beta" forKey:@"msgType"];
            [dicParams setValue:@"hello" forKey:@"msg"];
            NSString *strMsg = [PublicTool JSONStringWithDic:dicParams];
    
            // 发送心跳数据到服务器
            [socketInstance sendDataToServer:[strMsg dataUsingEncoding:NSUTF8StringEncoding]];
        }
    
        - (IBAction)clearLog:(id)sender {
            _logTV.text = nil;
        }
    
        // 键盘回收
        - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
            [self.view endEditing:YES];
        }
    
        @end

    Socket14

目录
相关文章
|
6天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
1月前
|
Java Android开发 Swift
安卓与iOS开发对比:平台选择对项目成功的影响
【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
114 1
|
1月前
|
设计模式 安全 Swift
探索iOS开发:打造你的第一个天气应用
【9月更文挑战第36天】在这篇文章中,我们将一起踏上iOS开发的旅程,从零开始构建一个简单的天气应用。文章将通过通俗易懂的语言,引导你理解iOS开发的基本概念,掌握Swift语言的核心语法,并逐步实现一个具有实际功能的天气应用。我们将遵循“学中做,做中学”的原则,让理论知识和实践操作紧密结合,确保学习过程既高效又有趣。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你打开一扇通往iOS开发世界的大门。
|
1月前
|
搜索推荐 IDE API
打造个性化天气应用:iOS开发之旅
【9月更文挑战第35天】在这篇文章中,我们将一起踏上iOS开发的旅程,通过创建一个个性化的天气应用来探索Swift编程语言的魅力和iOS平台的强大功能。无论你是编程新手还是希望扩展你的技能集,这个项目都将为你提供实战经验,帮助你理解从构思到实现一个应用的全过程。让我们开始吧,构建你自己的天气应用,探索更多可能!
66 1
|
13天前
|
安全 数据处理 Swift
深入探索iOS开发中的Swift语言特性
本文旨在为开发者提供对Swift语言在iOS平台开发的深度理解,涵盖从基础语法到高级特性的全面分析。通过具体案例和代码示例,揭示Swift如何简化编程过程、提高代码效率,并促进iOS应用的创新。文章不仅适合初学者作为入门指南,也适合有经验的开发者深化对Swift语言的认识。
35 9
|
9天前
|
设计模式 Swift iOS开发
探索iOS开发:从基础到高级,打造你的第一款App
【10月更文挑战第40天】在这个数字时代,掌握移动应用开发已成为许多技术爱好者的梦想。本文将带你走进iOS开发的世界,从最基础的概念出发,逐步深入到高级功能实现,最终指导你完成自己的第一款App。无论你是编程新手还是有志于扩展技能的开发者,这篇文章都将为你提供一条清晰的学习路径。让我们一起开始这段旅程吧!
|
13天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
11天前
|
iOS开发 开发者
探索iOS开发中的SwiftUI框架
【10月更文挑战第39天】在苹果的生态系统中,SwiftUI框架以其声明式语法和易用性成为开发者的新宠。本文将深入SwiftUI的核心概念,通过实际案例展示如何利用这一框架快速构建用户界面,并探讨其对iOS应用开发流程的影响。
|
14天前
|
JSON 前端开发 API
探索iOS开发之旅:打造你的第一个天气应用
【10月更文挑战第36天】在这篇文章中,我们将踏上一段激动人心的旅程,一起构建属于我们自己的iOS天气应用。通过这个实战项目,你将学习到如何从零开始搭建一个iOS应用,掌握基本的用户界面设计、网络请求处理以及数据解析等核心技能。无论你是编程新手还是希望扩展你的iOS开发技能,这个项目都将为你提供宝贵的实践经验。准备好了吗?让我们开始吧!
|
19天前
|
设计模式 前端开发 Swift
探索iOS开发:从初级到高级的旅程
【10月更文挑战第31天】在这篇文章中,我们将一起踏上iOS开发的旅程。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。我们将从基础开始,逐步深入到更高级的技术和概念。让我们一起探索iOS开发的世界吧!