zoukankan      html  css  js  c++  java
  • IOS工作笔记(九)

    关于IOS与服务器交互json数据
    ①从服务器接受json数据:
    这个可以用AFN,接受方式一般为get。如:

     1 +(NSArray *)getContactsFromServer:(ZMMeetingAddViewController *)meetingAddController{
     2     NSMutableArray *contactsArr = [NSMutableArray array];
     3     
     4     NSString *urlGetContactsFromServer = [NSString stringWithFormat:@"http://xxx.com"];
     5     
     6     urlGetContactsFromServer = [urlGetContactsFromServer stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
     7     AFHTTPRequestOperationManager *requestManager = [[AFHTTPRequestOperationManager alloc]init];
     8     requestManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/json", @"text/plain", nil];
     9     requestManager.requestSerializer.HTTPShouldHandleCookies = YES;
    10     
    11     [requestManager GET:urlGetContactsFromServer
    12              parameters:nil
    13                 success:^(AFHTTPRequestOperation *operation, id responseObject) {
    14                     NSDictionary *dict = responseObject;
    15                     NSString *resultStr = [dict objectForKey:@"result"];
    16                     NSArray *contactsList = [dict objectForKey:@"list"];
    17                     NSString *errorStr = [dict objectForKey:@"error_reason"];
    18                     if (resultStr && [resultStr isEqualToString:@"success"]) {
    19                         for (NSDictionary *dic in contactsList) {
    20                             NSString *telNum = [dic objectForKey:@"tel"];
    21                             NSString *userName = [dic objectForKey:@"name"];
    22                             ZMContactsFromServer *contact = [[ZMContactsFromServer alloc]init];
    23                             contact.userName = userName;
    24                             contact.telNum = telNum;
    25                             [contactsArr addObject:contact];
    26                         }
    27                     } else {
    28                         NSLog(@"%@",errorStr);
    29                     }
    30                     meetingAddController.allContacts = contactsArr;
    31                     
    32                     [meetingAddController.tableView reloadData];
    33                     
    34                 } failure:^(AFHTTPRequestOperation *operation, NSError *error){
    35                     NSLog(@"数据请求失败");
    36                     
    37                 }];
    38     
    39     return contactsArr;
    40 }

    ②往服务器发送json数据,需要注意以下几点
    ⑴一定要用post方式
    ⑵设置请求头
    ⑶设置请求体

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        // 1.创建请求
        NSURL *url = [NSURL URLWithString:@"http://xxx.com"];
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        request.HTTPMethod = @"POST";
        
        // 2.设置请求头
        [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        
        // 3.设置请求体
        NSDictionary *json = @{@"order_id" : @"123",@"user_id" : @"789",@"shop" : @"Toll"};
        
        // NSData --> NSDictionary
        // NSDictionary --> NSData
        NSData *data = [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:nil];
        request.HTTPBody = data;
        
        // 4.发送请求
        [NSURLConnection sendAsynchronousRequest:request 
                                           queue:[NSOperationQueue mainQueue] 
                               completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError){
                                    NSLog(@"%d", data.length);
        }];
    }

    2.关于如何在项目中如何建立分类文件夹的问题,即Classes下分Controller、Model、View、Others等,像这样

    但下午在建文件夹时,是这样

    不知道是什么原因,按照以前那种方法分类后再编译会提示“Appdelegate.h file not found”错误。
    由于新建项目时用的Xcode版本是6.3.2,用5.2版本按照这种方法则完全正常,所以想当然的以为是xcode版本问题,还在网上查了半天资料。后来问了人,才恍然大悟。
    xcode中用new group方法添加的文件夹不是真实存在的,若想真实存在,则需要点击项目名称——show in finder,新建文件夹Classes,然后往里边拖或Add Files To "项目名称"时,这时就要特别注意选择类型

    应选择“Create groups”,而不是“Create folder reference”。
    切记。

    3. 关于多线程处理问题
    子线程是不能更新UI的,它只负责数据的处理。只有主线程才能更新UI,若在子线程更新UI,则会报

    Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now

    比如,点击按钮产生一个新线程

    -(void)clickTheBtn{
        NSThread *buttonThread = [[NSThread alloc] initWithTarget:self selector:@selector(readSim) object:nil];
        [buttonThread setName:@"thread-1"];
        [buttonThread start];
    }

    错误做法

    -(void)readSim{
        //其它操作
        //更新UI
        [self.imgBtn setImage:[UIImage imageNamed:@"newimg"] forState:UIControlStateNormal];//会报错  
    }

    正确的做法

    -(void)readSim{
        //其它操作
        //更新UI
        dispatch_sync(dispatch_get_main_queue(), ^(){
            // 这里的代码会在主线程执行
            [self.imgBtn setImage:[UIImage imageNamed:@"newimg"] forState:UIControlStateNormal]; 
        });
    }

    利用block块的方法比普通方法简单,如

    //在主线程上执行操作
    [self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES];

    4.json格式问题,在处理json时,从后台返回来的格式如下

    @"{"name":"wewins bluetooth data","uuid":"26750052"}"

    但打印后却显示正常

    {"name":"wewins bluetooth data","uuid":"26750052"}

    这可能是Xcode进行了自动转译。对于这种形式的json,用传统替代字符串的方法处理会出现问题,由于“”属于转义字符,所以处理“”时得用“\”

    1 NSString *uuidDev = self.locationArray[1];//self.locationArray[1]即为上述反斜杠形式字符串
    2 NSString *character = nil;
    3 for (int i=0; i<strWithLine.length; i++) {
    4     character = [strWithLine substringWithRange:NSMakeRange(i, 1)];
    5     if ([character isEqualToString:@"""]) {
    6         [strWithLine deleteCharactersInRange:NSMakeRange(i, 1)];
    7     }
    8 }

    上面的方法在处理单个或无规则位置的反斜杠“”有用。对于上述形式则需要用SBJson来处理。

    1 NSMutableString *strWithLine = self.locationArray[1];
    2 SBJsonParser *jsonParser = [[SBJsonParser alloc] init];
    3 NSDictionary *dictTemp = [jsonParser objectWithString:strWithLine];

    SBJson的功能很强大,对于数组或是字典,都可以用objectWithString来解析

    1 NSString *jsonStr  = @"{"name":"jia","age":"24"}";
    2 NSString *jsonStr2 = @"["1","2"]";
    3 SBJsonParser *jsonParser = [[SBJsonParser alloc] init];
    4 NSMutableDictionary *dict = [jsonParser objectWithString:jsonStr];
    5 NSLog(@"%@",dict); 
    6 NSMutableArray *arr = [jsonParser objectWithString:jsonStr2];
    7 NSLog(@"%@",arr);

    5.关于代理取值后数组数据消失的问题
    经过是这样的,之前要把一个功能整合到项目中,这个功能的作用是在程序启动时用代理获取一个数组,先做的demo,测试正常。但加到项目中后,刚开始数组是有值的,但跳转几次页面后数据消失(之前的demo也有页面跳转),然后开始分析。
    之前一直以为是自己的程序有问题,debug测试了老半天,对该数组进行追踪,发现没有其它操作,但数据就是莫名其妙地消失了。
    经过debug,还是有了点眉目,因为项目是混合应用,所以页面有的是单纯的html跳转,有的是controller之间跳。当时完全没有想到这里,认为demo中经过几次页面跳转后数据还在,现在跳转后不在了是自己的写法有问题。
    然后呢,最后问题确认了,项目中多加了一个跳转,然后再返回原页面。这步操作是原生写法,不是HTML的。在返回原页面时用的是

    1 MainPageViewController *second = [[MainPageViewController alloc] init];
    2 AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    3 appDelegate.window.rootViewController = second;

    由上边可以看出返回原页面时对controller新建了一个实例。跟原来的controller不是同一个,所以数组数据就消失了。
    知道问题在哪,解决方法就有了。
    ①页面跳转时都不要新建实例,可以把controller作为参数传递。这样不管跳转几次,都只有一个实例
    ②若新建了实例,那么需要把数据存到单例中,最明显的例子就是Appdelegate和NSUserDefaults。

    1 //存储时,在AppDelegate.h中
    2 @property(nonatomic,strong) NSMutableArray *tempArr;
    3 //在需要储存数据的controller中
    4 AppDelegate *mainDelegate = [[UIApplication sharedApplication] delegate];
    5 mainDelegate.tempArr = self.dataArr;
    6 //读取数据时
    7 AppDelegate *mainDelegate = [[UIApplication sharedApplication] delegate];
    8 NSArray *arrList = mainDelegate.tempArr;

    6.沙盒的应用,往plist文件存储数据
    一些基本的数据类型,如NSString、NSDictionary、NSArray、NSData、NSNumber等可以存储到plist文件中。如:

     1 //要存储的数组
     2 NSString *str = @"abc";  
     3 NSString *astr = @"efg";  
     4 NSArray *Array = [NSArray arrayWithObjects:str, astr, nil];
     5 
     6 //存储数据 
     7 NSString *Path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; 
     8 NSString *filename = [Path stringByAppendingPathComponent:@"test.plist"];  
     9 [NSKeyedArchiver archiveRootObject:Array toFile:filename];
    10 
    11 //获取数据
    12 NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithFile: filename];  
    13 NSString *strRead = [arr objectAtIndex:0];  
    14 NSString *astrRead =  [arr objectAtIndex:1];

    此类操作时需要用NSKeyedArchiver的archiveRootObject和unarchiveObjectWithFile操作。这在SSKeyChain中也有用到。
    若要存储model类,则需要重写encodeWithCoder和initWithCoder方法。如在一个对象User中
    在.h中

    1 @interface ZMUserModel : NSObject<NSCoding>
    2 @property(nonatomic,copy) NSString *userId;
    3 @property(nonatomic,copy) NSString *userIpAdd;
    4 @property(nonatomic,copy) NSString *userTel;
    5 @end

    在.m中

    @implementation ZMUserModel
    
    -(void)encodeWithCoder:(NSCoder *)aCoder{
        [aCoder encodeObject:self.userId forKey:@"userId"];
        [aCoder encodeObject:self.userIpAdd forKey:@"userIpAdd"];
        [aCoder encodeObject:self.userTel forKey:@"userTel"];
    }
    
    -(id)initWithCoder:(NSCoder *)aDecoder{
        if (self == [super init]) {
            self.userId = [aDecoder decodeObjectForKey:@"userId"];
            self.userIpAdd = [aDecoder decodeObjectForKey:@"userIpAdd"];
            self.userTel = [aDecoder decodeObjectForKey:@"userTel"];
        }
        return self;
    }
    
    @end

    然后在获取时可以用类似于下边的

    for (NSData *userData in defaultsArr) {
        ZMUserModel *userModel = [NSKeyedUnarchiver unarchiveObjectWithData:userData];
        if ([userId isEqualToString:userModel.userId]) {
            ipStr = userModel.userIpAdd;
            userTel = userModel.userTel;
        }
    }

    7.Category是IOS的一种设计模式,用来对已存在的类添加方法,新添加的方法也会被扩展的类的子类继承。假如某个类的方法有bug而不方便修改时,可以用Category解决。但要注意的是,此时要实现那个bug方法的所有功能,否则会出现新bug。
    Category只能添加类的方法,不能添加新属性。
    添加方法为:新建File——>Source—>Objective-C File,选FileType和Class,命名。如对NSString添加TurnStn方法(反转字符),则

    系统会生成固定格式的方法名,NSString+TurnStn。

    在NSString+TurnStn的.h中

    @interface NSString (TurnStn)  
    + (NSString *) reverseString:(NSString *)strSrc;  
    @end

    在.m中实现

     1 @implementation NSString (TurnStn)  
     2 + (NSString *)reverseString:(NSString *)strSrc;  
     3 {  
     4     NSMutableString *reversedString =[[NSMutableString alloc]init];  
     5     NSInteger charIndex = [strSrc length];  
     6     while (charIndex > 0) {  
     7         charIndex--;  
     8         NSRange subStrRange =NSMakeRange(charIndex, 1);  
     9         [reversedString appendString:[strSrcsubstringWithRange:subStrRange]];  
    10     }  
    11     return reversedString;  
    12 }  
    13 @end

    使用时直接调用reverseString即可,非常方便。

    还有一种特殊的Category使用方法。如在view中想获取宽度,需要用

    CGFloat widthTemp = self.frame.size.width;

    若不想这么繁琐,则可以扩展

    在UIView+ShortStr的.h中

    1 @interface UIView (ShortStr)
    2 // 这样只会生成方法的声明, 不会生成方法的实现和成员变量
    3 @property(nonatomic,assign) CGFloat width;
    4 @end

    在.m中

     1 @implementation UIView (ShortStr)
     2 - (void)setWidth:(CGFloat)width
     3 {
     4     CGRect frame = self.frame;
     5     frame.size.width = width;
     6     self.frame = frame;
     7 }
     8 
     9 - (CGFloat)width
    10 {
    11     return self.frame.size.width;
    12 }
    13 @end

    8.NSString转换大小写
    有两套方法,一种是lowercaseString,uppercaseString;另一套是uppercaseStringWithLocale,lowercaseStringWithLocale
    推荐用第二套,因为考虑到本地化问题

    1 NSString *testString = @"Hello World";
    2 NSString *lowerCaseString1 = [testString lowercaseString];
    3 NSLog(@"lowerCaseString1: %@",lowerCaseString1);
    4 //结果为 hello world
    5 
    6 NSString *upperStr = [testString uppercaseStringWithLocale:[NSLocale currentLocale]];
    7 NSLog(@"upperStr: %@",upperStr);
    8 //结果为 HELLO WORLD

    9.获取folder reference(即蓝色文件夹)内文件

    常规的图片获取方法

    UIImage *img = [UIImage imageNamed:@"a"];

    是获取不到的,这只适用于group类型的(黄色文件夹)。
    正确的应该是

    1 NSString *path = [[NSBundle mainBundle] pathForResource:@"a.png" ofType:nil inDirectory:@"images/ttt"];
    2 UIImage *img = [UIImage imageWithContentsOfFile:path];

    或者是

    UIImage *img = [UIImage imageNamed:@"images/ttt/a.png"];

    这里牵涉到ios的相对路径和绝对路径。下边的就是相对路径

    UIImage *img=[UIImage imageNamed:@"cellicon.png"];

    folder里的由于不能编译,所以得是绝对路径。获取程序根目录的方法为:

    NSString *basePath = [NSString stringWithFormat:@"%@%@",[[NSBundle mainBundle]resourcePath],@"/"];
    NSLog("根路径为%@",basePath);

    打印结果为

    根路径为/Users/apologize/Library/Developer/CoreSimulator/Devices/9175C57E-399F-41DA-8405-D153A5FBEC9F/data/Containers/Bundle/Application/9852AB51-3E69-4CB6-ADF3-AAE3AF563429/Demo-获取文件内图片.app/

    10.关于block变量的使用
    在使用MBProgressHud时,出现

    Variable is not assignable (missing__block type specifier)
    出错代码如下

    经查找,是因为在block内部使用block外部定义的局部变量时,该变量没有被__block修饰(注意是双下划线)。没有被__block修饰的变量在block内部是readonly的。若想修改,只能在前边加__block修饰
    但是,若在block内修改全局变量,则不需要__block修饰。
    那么修改方案如下

     1 @property(nonatomic,strong) MBProgressHUD *hhh;
     2 
     3 -(void)showTextDialog{
     4     __block MBProgressHUD *mbHud = [[MBProgressHUD alloc]initWithView:self.view];
     5     [self.view addSubview:mbHud];
     6     mbHud.dimBackground = YES;
     7     mbHud.labelText = @"请稍等";
     8     [mbHud showAnimated:YES whileExecutingBlock:^{
     9         //对话框显示时进行的操作
    10         sleep(3);
    11     }completionBlock:^{
    12         //操作完后取消对话框
    13         [mbHud removeFromSuperview];
    14         mbHud = nil;
    15         self.hhh = nil;
    16     }];
    17 }

    11. 关于UIAlertView的自动消失
    网上的有两种方法,一种用NSTimer,一种用dismissWithClickedButtonIndex。这里说下第二种。
    之前用

     1 -(void)clickBtn{
     2     NSThread *newThread = [[NSThread alloc]initWithTarget:self selector:@selector(showAlert) object:nil];
     3     [newThread setName:@"newThread"];
     4     [newThread start];
     5     
     6 }
     7 
     8 -(void)showAlert{
     9     __block UIAlertView *alert = [[UIAlertView alloc]init];
    10     dispatch_sync(dispatch_get_main_queue(), ^(){
    11         alert = [[UIAlertView alloc]initWithTitle:nil message:@"正在写卡" delegate:self cancelButtonTitle:nil otherButtonTitles:nil, nil];
    12         [alert show];
    13         
    14     });
    15 
    16     [alert dismissWithClickedButtonIndex:0 animated:YES ];
    17 }

    但有时会出现alert窗口一直不消失的情况。这时只需做个判断即可,alertView有个visible属性

    if (alert.visible) {
            [alert dismissWithClickedButtonIndex:0 animated:YES ];
    }

    还有一种是delay再执行消失,但这种情况下alert的展示是固定时长的,酌情使用

     1 - (void)showAlert{            
     2     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"title" message:@"message" delegate:nil 
     3 cancelButtonTitle:nil otherButtonTitles:nil];
     4     
     5     [alert show];
     6     [self performSelector:@selector(dimissAlert) withObject:alert afterDelay:2.0];
     7 }
     8 
     9 -(void)dimissAlert{
    10     if (alert.visible) {
    11         [alert dismissWithClickedButtonIndex:0 animated:YES ];
    12     }
    13 }
  • 相关阅读:
    自动登录网站
    爬取梨视频
    爬虫介绍,request模块和代理ip
    数据结构与算法
    CMDB的总结
    自动化运维模块
    linux命令补充
    centos7的目录结构,文件系统常用的命令,vim编辑器
    linux配置网卡文件,xshell链接服务,快照,克隆,修改主机名
    flask的请求扩展,错误处理,标签和过滤器,中间件以及cbv的写法
  • 原文地址:https://www.cnblogs.com/Apologize/p/4654764.html
Copyright © 2011-2022 走看看