精华内容
下载资源
问答
  • TouchUI是一套高质量移动端UI框架。基于vue.js框架,我们精心打造了上百种的移动端UI组件,几乎囊括了开发移动应用的所有细节,真正实现拿来即用,像搭积木一样开发移动应用。
  • TouchUI-WX是一套完全免费的微信小程序开发框架,包含丰富的UI控件用于官方组件的补充
  • TouchUI组件与API演示

    2019-08-11 02:30:21
    TouchUI 组件与API演示。
  • Hi,我们做了一款高质量的、免费的移动端UI框架。 经过将两年多开发和项目实践,我们终于把Touch UI开放出来了。这是一套基于vue.js打造的移动端UI框架,包含近百种组件,几乎囊括了开发移动应用的所有细节。 Touch ...

    Hi,我们做了一款高质量的、免费的移动端UI框架。

    经过将两年多开发和项目实践,我们终于把Touch UI开放出来了。这是一套基于vue.js打造的移动端UI框架,包含近百种组件,几乎囊括了开发移动应用的所有细节。

    Touch UI有何亮点?

    我们认为,一套足够好的移动端UI框架应该满足4个要素。

    1、丰富的组件和易用的API

    我们在做这套UI框架时,分析了大量的主流的移动应用,从中抽象出将近100种组件。囊括了容器布局、切换、模态、表单、列表、文本、多媒体、图形图表、地图等各个方面。

    我们希望开发者在开发移动应用时,不必把精力花在到处找组件、处理兼容等事情上,而是能够专注于业务,真正实现组件拿来即用,像搭积木一样开发移动应用。

    图片描述

    2、友好的移动交互

    跟PC开发不同,移动端的UI应该具备友好的移动端交互,例如上拉下拉、手指滑屏、按下滑动等等。我们在这些方面做了充分的考虑,每个组件都是针对移动端精心打造,追求原生级别的操作体验。
    图片描述

    图片描述

    图片描述

    图片描述

    3、优秀的性能

    如何在功能丰富的基础上还能保证高性能,是我们做这套框架的一个很大挑战。为此,我们做了大量的工作来优化性能,例如按需编译、资源拆分加载等等。现在基本可以达到点击页面秒开的效果。

    4、良好的开发体验

    前端工程化的出现,大大提高了前端项目的开发效率并降低维护成本,但对于完全没有经验的传统前端工程师来说,各种的环境配置和依赖安装还是有不小的门槛。

    基于微软VSCode编辑器的插件机制,我们把前端工程化所需要的做的各种构建、编译环节全部封装起来,给开发者提供可视化右键菜单,从而简化环境配置并降低入门门槛。同时,Touch UI框架和组件也都在插件里面,这样当框架有升级时,开发者通过在线升级插件的方式就可以更新Touch UI,非常方便。

    图片描述

    One More Thing

    我们同时还推出了一套微信小程序UI框架:TouchUI-WX,它增加了30多种常用的组件用于官方组件的补充,并且扩充了不少功能,例如支持阿里的iconfont矢量图标库,支持less语法等。

    图片描述

    最重要的是,你可以将Touch UI工程与TouchUI-WX工程相互进行转换。实现开发一套代码,发布H5和微信小程序两种应用。

    官网:https://www.touchui.io

    Github:https://github.com/uileader/t...

    展开全文
  • 推荐7款H5手机移动端页面UI框架.zip Amaze UI Touch Framework7 Frozen UI GMU MUI SUI WeUI
  • Touch UI:高质量的移动端UI框架介绍

    千次阅读 2018-05-30 09:46:23
    Hi,我们做了一款高质量的、免费的移动端UI框架。经过将两年多开发和项目实践,我们终于把Touch UI开放出来了。这是一套基于vue.js打造的移动端UI框架,包含近百种组件,几乎囊括了开发移动应用的所有细节。Touch UI...

      Hi,我们做了一款高质量的、免费的移动端UI框架。

      经过将两年多开发和项目实践,我们终于把Touch UI开放出来了。这是一套基于vue.js打造的移动端UI框架,包含近百种组件,几乎囊括了开发移动应用的所有细节。


    Touch UI有何亮点?

    我们认为,一套足够好的移动端UI框架应该满足4个要素。


    1、丰富的组件和易用的API

    我们在做这套UI框架时,分析了大量的主流的移动应用,从中抽象出将近100种组件。囊括了容器布局、切换、模态、表单、列表、文本、多媒体、图形图表、地图等各个方面。

    我们希望开发者在开发移动应用时,不必把精力花在到处找组件、处理兼容等事情上,而是能够专注于业务,真正实现组件拿来即用,像搭积木一样开发移动应用。


    2、友好的移动交互

    PC开发不同,移动端的UI应该具备友好的移动端交互,例如上拉下拉、手指滑屏、按下滑动等等。我们在这些方面做了充分的考虑,每个组件都是针对移动端精心打造,追求原生级别的操作体验。








    3、优秀的性能

    如何在功能丰富的基础上还能保证高性能,是我们做这套框架的一个很大挑战。为此,我们做了大量的工作来优化性能,例如按需编译、资源拆分加载等等。现在基本可以达到点击页面秒开的效果。


    4、良好的开发体验

    前端工程化的出现,大大提高了前端项目的开发效率并降低维护成本,但对于完全没有经验的传统前端工程师来说,各种的环境配置和依赖安装还是有不小的门槛。

    基于微软VSCode编辑器的插件机制,我们把前端工程化所需要的做的各种构建、编译环节全部封装起来,给开发者提供可视化右键菜单,从而简化环境配置并降低入门门槛。同时,Touch UI框架和组件也都在插件里面,这样当框架有升级时,开发者通过在线升级插件的方式就可以更新Touch UI,非常方便。




    One More Thing

    我们同时还推出了一套微信小程序UI框架:TouchUI-WX,它增加了30多种常用的组件用于官方组件的补充,并且扩充了不少功能,例如支持阿里的iconfont矢量图标库,支持less语法等。





    最重要的是,你可以将Touch UI工程与TouchUI-WX工程相互进行转换。实现开发一套代码,发布H5和微信小程序两种应用。


    官网:https://www.touchui.io

    Githubhttps://github.com/uileader/touchui




    展开全文
  • Cocoa touch框架预览

    千次阅读 2016-04-22 19:40:31
    概述 我们前面的章节中就一直新建Cocoa Class,那么Cocoa到底是什么,它和我们前面以及...今天我们将揭开这层面纱,重点分析在IOS开发中一个重要的框架Foundation,今天的主要内容有: Foundation概述常用结构体

    概述

    我们前面的章节中就一直新建Cocoa Class,那么Cocoa到底是什么,它和我们前面以及后面要讲的内容到底有什么关系呢?Objective-C开发中经常用到NSObject,那么这个对象到底是谁?它为什么又出现在Objective-C中间呢?今天我们将揭开这层面纱,重点分析在IOS开发中一个重要的框架Foundation,今天的主要内容有:

    1. Foundation概述
    2. 常用结构体
    3. 日期
    4. 字符串
    5. 数组
    6. 字典
    7. 装箱和拆箱
    8. 反射
    9. 拷贝
    10. 文件操作
    11. 归档

    Foundation概述

    为什么前面说的内容中新建一个类的时候我们都是选择Cocoa Class呢?Cocoa是什么呢?

    Cocoa不是一种编程语言(它可以运行多种编程语言),它也不是一个开发工具(通过命令行我们仍然可以开发Cocoa程序),它是创建Mac OS X和IOS程序的原生面向对象API,为这两者应用提供了编程环境。

    我们通常称为“Cocoa框架”,事实上Cocoa本身是一个框架的集合,它包含了众多子框架,其中最重要的要数“Foundation”和“UIKit”。前者是框架的基础,和界面无关,其中包含了大量常用的API;后者是基础的UI类库,以后我们在IOS开发中会经常用到。这两个框架在系统中的位置如下图:

    Cocoa

    其实所有的Mac OS X和IOS程序都是由大量的对象构成,而这些对象的根对象都是NSObject,NSObject就处在Foundation框架之中,具体的类结构如下:

    Foundation1

    Foundation2

    Foundation3

    通常我们会将他们分为几类:

    1. 值对象 
    2. 集合 
    3. 操作系统服务:文件系统、URL、进程通讯 
    4. 通知 
    5. 归档和序列化 
    6. 表达式和条件判断 
    7. Objective-C语言服务

    UIKit主要用于界面构架,这里我们不妨也看一下它的类结构:

    UIKit

    常用结构体

    在Foundation中定义了很多常用结构体类型来简化我们的日常开发,这些结构体完全采用Objective-C定义,和我们自己定义的结构体没有任何区别,之所以由框架为我们提供完全是为了简化我们的开发。常用的结构体有NSRange、NSPoint、NSSize、NSRect等

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    /*NSRange表示一个范围*/
    void test1(){
        NSRange rg={3,5};//第一参数是起始位置第二个参数是长度
        //NSRange rg;
        //rg.location=3;
        //rg.length=5;
        //NSRange rg={.location=3,.length=5};
        //常用下面的方式定义 NSRange rg2=NSMakeRange(3,5);//使用NSMakeRange定义一个NSRange 
        //打印NSRange可以使用Foundation中方法 NSLog(@"rg2 is %@", NSStringFromRange(rg2));//注意不能直接NSLog(@"rg2 is %@", rg2),因为rg2不是对象(准确的说%@是指针)而是结构体
    }
    /*NSPoint表示一个点*/
    void test2(){
        NSPoint p=NSMakePoint(10, 15);//NSPoint其实就是CGPoint
        //这种方式比较常见 NSPoint p2=CGPointMake(10, 15);
        NSLog(NSStringFromPoint(p2));
    }
    /*NSSize表示大小*/
    void test3(){
        NSSize s=NSMakeSize(10, 15);//NSSize其实就是CGSize
        //这种方式比较常见 CGSize s2=CGSizeMake(10, 15);
        NSLog(NSStringFromSize(s2));
    }
    /*NSRect表示一个矩形*/
    void test4(){
        NSRect r=NSMakeRect(10, 5, 100, 200);//NSRect其实就是CGRect
        //这种方式比较常见 NSRect r2=CGRectMake(10, 5, 100, 200);
        NSLog(NSStringFromRect(r2));
    }
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            test1();
            test2();
            test3();
            test4();
        } return 0;
    }
    可以看到对于常用结构体在Foundation框架中都有一个对应的make方法进行创建,这也是我们日后比较常用的操作;而且与之对应的还都有一个NSStringFromXX方法来进行字符串转换,方便我们调试。上面也提到NSSize其实就是CGSize,NSRect其实就是CGRect,我们可以通过查看代码进行确认,例如NSSize定义: 

    NSSize

    继续查看CGSize的代码:

    CGSize

    日期

    接下来熟悉一下Foundation框架中日期的操作

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    
    int main(int argc, const char * argv[]) {
        
        NSDate *date1=[NSDate date];//获得当前日期
        NSLog(@"%@",date1); //结果:2014-07-16 07:25:28 +0000
        
        NSDate *date2=[NSDate dateWithTimeIntervalSinceNow:100];//在当前日期的基础上加上100秒,注意在ObjC中多数时间单位都是秒
        NSLog(@"%@",date2); //结果:2014-07-16 07:27:08 +0000
        
        NSDate *date3=[NSDate distantFuture];//随机获取一个将来的日期
        NSLog(@"%@",date3); //结果:4001-01-01 00:00:00 +0000
        
        NSTimeInterval time=[date2 timeIntervalSinceDate:date1];//日期之差,返回单位为秒
        NSLog(@"%f",time); //结果:100.008833
        
        NSDate *date5=[date1 earlierDate:date3];//返回比较早的日期
        NSLog(@"%@",date5); //结果:2014-07-16 07:25:28 +0000
        
        //日期格式化
        NSDateFormatter *formater1=[[NSDateFormatter alloc]init];
        formater1.dateFormat=@"yy-MM-dd HH:mm:ss";
        NSString *datestr1=[formater1 stringFromDate:date1];
        NSLog(@"%@",datestr1); //结果:14-07-16 15:25:28
        //字符串转化为日期
        NSDate *date6=[formater1 dateFromString:@"14-02-14 11:07:16"];
        NSLog(@"%@",date6); //结果:2014-02-14 03:07:16 +0000
    
        return 0;
    }

    字符串

    不可变字符串

    在ObjC中字符串操作要比在C语言中简单的多,在下面的例子中你将看到字符串的初始化、大小写转化、后缀前缀判断、字符串比较、字符串截取、字符串转换等,通过下面的例子我们基本可以掌握常用的字符串操作(注意这些内容虽然基本,但却是十分常用的操作,需要牢记):

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    
    /**字符串操作*/
    void test1(){
        char *str1="C string";//这是C语言创建的字符串
        NSString *str2=@"OC string";//ObjC字符串需要加@,并且这种方式创建的对象不需要自己释放内存
    
        //下面的创建方法都应该释放内存
        NSString *str3=[[NSString alloc] init];
        str3=@"OC string";
        NSString *str4=[[NSString alloc] initWithString:@"Objective-C string"];
        NSString *str5=[[NSString alloc] initWithFormat:@"age is %i,name is %.2f",19,1.72f];
        NSString *str6=[[NSString alloc] initWithUTF8String:"C string"];//C语言的字符串转换为ObjC字符串
    
        //以上方法都有对应静态方法(一般以string开头),不需要管理内存(系统静态方法一般都是自动释放)
        NSString *str7=[NSString stringWithString:@"Objective-C string"];
    }
    void test2(){
        NSLog(@"\"Hello world!\" to upper is %@",[@"Hello world!" uppercaseString]);
        //结果:"Hello world!" to upper is HELLO WORLD!
        NSLog(@"\"Hello world!\" to lowwer is %@",[@"Hello world!" lowercaseString]);
        //结果:"Hello world!" to lowwer is hello world!
         
        //首字母大写,其他字母小写
        NSLog(@"\"Hello world!\" to capitalize is %@",[@"Hello world!" capitalizedString]);
        //结果:"Hello world!" to capitalize is Hello World!
         
        BOOL result= [@"abc" isEqualToString:@"aBc"];
        NSLog(@"%i",result);
        //结果:0
        NSComparisonResult result2= [@"abc" compare:@"aBc"];//如果是[@"abc" caseInsensitiveCompare:@"aBc"]则忽略大小写比较
        if(result2==NSOrderedAscending){
            NSLog(@"left<right.");
        }else if(result2==NSOrderedDescending){
            NSLog(@"left>right.");
        }else if(result2==NSOrderedSame){
            NSLog(@"left=right.");
        }
        //结果:left>right.
    }
    void test3(){
        NSLog(@"has prefix ab? %i",[@"abcdef" hasPrefix:@"ab"]);
        //结果:has prefix ab? 1
        NSLog(@"has suffix ab? %i",[@"abcdef" hasSuffix:@"ef"]);
        //结果:has suffix ab? 1
        NSRange range=[@"abcdefabcdef" rangeOfString:@"cde"];//注意如果遇到cde则不再往后面搜索,如果从后面搜索或其他搜索方式可以设置第二个options参数
        if(range.location==NSNotFound){
            NSLog(@"not found.");
        }else{
            NSLog(@"range is %@",NSStringFromRange(range));
        }
        //结果:range is {2, 3}
    }
    //字符串分割
    void test4(){
        NSLog(@"%@",[@"abcdef" substringFromIndex:3]);//从第三个索引开始(包括第三个索引对应的字符)截取到最后一位
        //结果:def
        NSLog(@"%@",[@"abcdef" substringToIndex:3]);从0开始截取到第三个索引(不包括第三个索引对应的字符)
        //结果:abc
        NSLog(@"%@",[@"abcdef" substringWithRange:NSMakeRange(2, 3)]);
        //结果:cde
        NSString *str1=@"12.abcd.3a";
        NSArray *array1=[str1 componentsSeparatedByString:@"."];//字符串分割
        NSLog(@"%@",array1);
         /*结果:
          (
             12,
             abcd,
             3a
          )
          */
     
    }
    //其他操作
    void test5(){
        NSLog(@"%i",[@"12" intValue]);//类型转换
        //结果:12
        NSLog(@"%zi",[@"hello world,世界你好!" length]);//字符串长度注意不是字节数
        //结果:17
        NSLog(@"%c",[@"abc" characterAtIndex:0]);//取出制定位置的字符
        //结果:a
        const char *s=[@"abc" UTF8String];//转换为C语言字符串
        NSLog(@"%s",s);
        //结果:abc
    }
    
    int main(int argc, const char * argv[]) {
        test1();
        test2();
        test3();
        test4();
        test5();
        return 0;
    }

    注意:上面代码注释中提到的需要释放内存指的是在MRC下的情况,当然本质上在ARC下也需要释放,只是这部分代码编译器会自动创建。

    扩展--文件操作

    在ObjC中路径、文件读写等操作是利用字符串来完成的,这里通过几个简单的例子来演示(首先在桌面上新建一个test.txt文件,里面存储的内容是”hello world,世界你好!”)

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    
    void test1(){
        //读取文件内容
        NSString *path=@"/Users/kenshincui/Desktop/test.txt";
        NSString *str1=[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
        //注意上面也可以使用gb2312 gbk等,例如kCFStringEncodingGB_18030_2000,但是需要用CFStringConvertEncodingToNSStringEncoding转换
        NSLog(@"str1 is %@",str1);
        //结果:str1 is hello world,世界你好!
    
        
        
        
        //上面我们看到了读取文件,但并没有处理错误,当然在ObjC中可以@try @catch @finnally但通常我们并不那么做
        //由于我们的test.txt中有中文,所以使用下面的编码读取会报错,下面的代码演示了错误获取的过程
        NSError *error;
        NSString *str2=[NSString stringWithContentsOfFile:path encoding:kCFStringEncodingGB_18030_2000 error:&error];//注意这句话中的error变量是**error,就是指针的指针那就是指针的地址,由于error就是一个指针此处也就是error的地址&error,具体原因见下面补充
        if(error){
            NSLog(@"read error ,the error is %@",error);
        }else{
            NSLog(@"read success,the file content is %@",str2);
        }
        //结果:read error ,the error is Error Domain=NSCocoaErrorDomain Code=261 "The file couldn’t be opened using the specified text encoding." UserInfo=0x100109620 {NSFilePath=/Users/kenshincui/Desktop/test.txt, NSStringEncoding=1586}
    
        
        
        
        //读取文件内容还有一种方式就是利用URl,它除了可以读取本地文件还可以读取网络文件
        //NSURL *url=[NSURL URLWithString:@"file:///Users/kenshincui/Desktop/test.txt"];
        NSURL *url=[NSURL URLWithString:@"http://www.apple.com"];
        NSString *str3=[NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
        NSLog(@"str3 is %@",str3);
    }
    void test2(){
        //下面是文件写入
        NSString *path1=@"/Users/kenshincui/Desktop/test2.txt";
        NSError *error1;
        NSString *str11=@"hello world,世界你好!";
        [str11 writeToFile:path1 atomically:YES encoding:NSUTF8StringEncoding error:&error1];//automically代表一次性写入,如果写到中间出错了最后就全部不写入
        if(error1){
            NSLog(@"write fail,the error is %@",[error1 localizedDescription]);//调用localizedDescription是只打印关键错误信息
        }else{
            NSLog(@"write success!");
        }
        //结果:write success!
    }
    //路径操作
    void test3(){
        NSMutableArray *marray=[NSMutableArray array];//可变数组
        [marray addObject:@"Users"];
        [marray addObject:@"KenshinCui"];
        [marray addObject:@"Desktop"];
    
        NSString *path=[NSString pathWithComponents:marray];
        NSLog(@"%@",path);//字符串拼接成路径
        //结果:Users/KenshinCui/Desktop
    
        NSLog(@"%@",[path pathComponents]);//路径分割成数组
        /*结果: 
         (
            Users,
            KenshinCui,
            Desktop
        )
        */
    
        NSLog(@"%i",[path isAbsolutePath]);//是否绝对路径(其实就是看字符串是否以“/”开头)
        //结果:0
        NSLog(@"%@",[path lastPathComponent]);//取得最后一个目录
        //结果:Desktop
        NSLog(@"%@",[path stringByDeletingLastPathComponent]);//删除最后一个目录,注意path本身是常量不会被修改,只是返回一个新字符串
        //结果:Users/KenshinCui
        NSLog(@"%@",[path stringByAppendingPathComponent:@"Documents"]);//路径拼接
        //结果:Users/KenshinCui/Desktop/Documents
    }
     //扩展名操作
    void test4(){
        NSString *path=@"Users/KenshinCui/Desktop/test.txt";
        NSLog(@"%@",[path pathExtension]);//取得扩展名,注意ObjC中扩展名不包括"."
        //结果:txt
        NSLog(@"%@",[path stringByDeletingPathExtension]);//删除扩展名,注意包含"."
        //结果:Users/KenshinCui/Desktop/test
        NSLog(@"%@",[@"Users/KenshinCui/Desktop/test" stringByAppendingPathExtension:@"mp3"]);//添加扩展名
        //结果:Users/KenshinCui/Desktop/test.mp3
    }
    
    int main(int argc, const char * argv[]) {
        test1();
        test2();
        test3();
        test4();
        return 0;
    }

    注意:在上面的例子中我们用到了可变数组,下面会专门介绍。

    可变字符串

    我们知道在字符串操作过程中我们经常希望改变原来的字符串,当然这在C语言中实现比较复杂,但是ObjC为我们提供了新的可变字符串类NSMutableString,它是NSString的子类。

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    
    int main(int argc, const char * argv[]) {
        
        /*可变字符串,注意NSMutableString是NSString子类*/
        //注意虽然initWithCapacity分配字符串大小,但是不是绝对的不可以超过此范围,声明此变量对性能有好处
        NSMutableString *str1= [[NSMutableString alloc] initWithCapacity:10];
        [str1 setString:@"hello"];//设置字符串
        NSLog(@"%@",str1);
        //结果:hello
    
        [str1 appendString:@",world!"];//追加字符串
        NSLog(@"%@",str1);
        //结果:hello,world!
    
        [str1 appendFormat:@"我的年龄是%i。dear,I love you.",18];
        NSLog(@"%@",str1);
        //结果:hello,world!我的年龄是18。dear,I love you.
        
        //替换字符串
        NSRange range=[str1 rangeOfString:@"dear"];
        [str1 replaceCharactersInRange:range withString:@"Honey"];
        NSLog(@"%@",str1);
        //结果:hello,world!我的年龄是18。Honey,I love you.
        
        //插入字符串
        [str1 insertString:@"My name is Kenshin." atIndex:12];
        NSLog(@"%@",str1);
        //结果:hello,world!My name is Kenshin.我的年龄是18。Honey,I love you.
        
        //删除指定字符串
        [str1 deleteCharactersInRange:[str1 rangeOfString:@"My name is Kenshin."]];//删除指定范围的字符串
        NSLog(@"%@",str1);
        //结果:hello,world!我的年龄是18。Honey,I love you.
        
        return 0;
    }

    数组

    不可变数组

    下面将演示常用的数组操作:初始化、数组对象的方法执行、数组元素的遍历、在原有数组基础上产生新数组、数组排序等

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    #import "Person.h"
    
    
    void test1(){
        //NSArray长度不可变所以初始化的时候就赋值,并且最后以nil结尾
        //此外需要注意NSArray不能存放C语言的基础类型
        NSObject *obj=[[NSObject alloc]init];
        //NSArray *array1=[[NSArray alloc] initWithObjects:@"abc",obj,@"cde",@"opq", nil];
        NSArray *array1=[NSArray arrayWithObjects:@"abc",obj,@"cde",@"opq",@25, nil];
        NSLog(@"%zi",array1.count);//数组长度,结果:5
        NSLog(@"%i",[array1 containsObject:@"cde"]);//是否包含某个对象,结果:1
        NSLog(@"%@",[array1 lastObject]);//最后一个对象,结果:25
        NSLog(@"%zi",[array1 indexOfObject:@"abc"]);//对象所在的位置:0
        
        Person *person1=[Person personWithName:@"Kenshin"];
        Person *person2=[Person personWithName:@"Kaoru"];
        Person *person3=[Person personWithName:@"Rosa"];
        NSArray *array2=[[NSArray alloc]initWithObjects:person1,person2,person3, nil];
        [array2 makeObjectsPerformSelector:@selector(showMessage:) withObject:@"Hello,world!"];//执行所有元素的showMessage方法,后面的参数最多只能有一个
        /*结果:
         My name is Kenshin,the infomation is "Hello,world!".
         My name is Kaoru,the infomation is "Hello,world!".
         My name is Rosa,the infomation is "Hello,world!".
         */
    }
    //数组的遍历
    void test2(){
        NSObject *obj=[[NSObject alloc]init];
        NSArray *array=[[NSArray alloc] initWithObjects:@"abc",obj,@"cde",@"opq",@25, nil];
        //方法1
        for(int i=0,len=array.count;i<len;++i){
            NSLog(@"method1:index %i is %@",i,[array objectAtIndex:i]);
        }
        /*结果:
         method1:index 0 is abc
         method1:index 1 is <NSObject: 0x100106de0>
         method1:index 2 is cde
         method1:index 3 is opq
         method1:index 4 is 25
         */
        
        
        //方法2
        for(id obj in array){
            NSLog(@"method2:index %zi is %@",[array indexOfObject:obj],obj);
        }
        /*结果:
         method2:index 0 is abc
         method2:index 1 is <NSObject: 0x100602f00>
         method2:index 2 is cde
         method2:index 3 is opq
         method2:index 4 is 25
         */
        
        
        //方法3,利用代码块方法
        [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            NSLog(@"method3:index %zi is %@",idx,obj);
            if(idx==2){//当idx=2时设置*stop为YES停止遍历
                *stop=YES;
            }
        }];
        /*结果:
         method3:index 0 is abc
         method3:index 1 is <NSObject: 0x100106de0>
         method3:index 2 is cde
         */
        
        
        //方法4,利用迭代器
        //NSEnumerator *enumerator= [array objectEnumerator];//获得一个迭代器
        NSEnumerator *enumerator=[array reverseObjectEnumerator];//获取一个反向迭代器
        //NSLog(@"all:%@",[enumerator allObjects]);//获取所有迭代对象,注意调用完此方法迭代器就遍历完了,下面的nextObject就没有值了
        id obj2=nil;
        while (obj2=[enumerator nextObject]) {
            NSLog(@"method4:%@",obj2);
        }
        /*结果:
         method4:25
         method4:opq
         method4:cde
         method4:<NSObject: 0x100106de0>
         method4:abc
         */
    }
    //数组派生出新的数组
    void test3(){
        NSArray *array=[NSArray arrayWithObjects:@"1",@"2",@"3", nil];
        NSArray *array2=[array arrayByAddingObject:@"4"];//注意此时array并没有变
        NSLog(@"%@",array2);
        /*结果:
         (
             1,
             2,
             3,
             4
         )
         */
        
        
        NSLog(@"%@",[array2 arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:@"5",@"6", nil]]);//追加形成新的数组
        /*结果:
         (
             1,
             2,
             3,
             4,
             5,
             6
         )
         */
        
        
        NSLog(@"%@",[array2 subarrayWithRange:NSMakeRange(1, 3)]);//根据一定范围取得生成一个新的数组
        /*结果:
         (
             2,
             3,
             4
         )
         */
        
        
        NSLog(@"%@",[array componentsJoinedByString:@","]);//数组连接,形成一个字符串
        //结果:1,2,3
        
        //读写文件
        NSString *path=@"/Users/KenshinCui/Desktop/array.xml";
        [array writeToFile:path atomically:YES];
        NSArray *array3=[NSArray arrayWithContentsOfFile:path];
        NSLog(@"%@",array3);
        /*结果:
         (
             1,
             2,
             3
         )
         */
    }
    //数组排序
    void test4(){
        //方法1,使用自带的比较器
        NSArray *array=[NSArray arrayWithObjects:@"3",@"1",@"2", nil];
        NSArray *array2= [array sortedArrayUsingSelector:@selector(compare:)];
        NSLog(@"%@",array2);
        /*结果:
         (
             1,
             2,
             3
         )
         */
        
        
        //方法2,自己定义比较器
        Person *person1=[Person personWithName:@"Kenshin"];
        Person *person2=[Person personWithName:@"Kaoru"];
        Person *person3=[Person personWithName:@"Rosa"];
        NSArray *array3=[NSArray arrayWithObjects:person1,person2,person3, nil];
        NSArray *array4=[array3 sortedArrayUsingSelector:@selector(comparePerson:)];
        NSLog(@"%@",array4);
        /*结果:
         (
             "name=Kaoru",
             "name=Kenshin",
             "name=Rosa"
         )
         */
        
        
        //方法3使用代码块
        NSArray *array5=[array3 sortedArrayUsingComparator:^NSComparisonResult(Person *obj1, Person *obj2) {
            return [obj2.name compare:obj1.name];//降序
        }];
        NSLog(@"%@",array5);
        /*结果:
         (
             "name=Rosa",
             "name=Kenshin",
             "name=Kaoru"
         )
         */
        
        
        //方法4 通过描述器定义排序规则
        Person *person4=[Person personWithName:@"Jack"];
        Person *person5=[Person personWithName:@"Jerry"];
        Person *person6=[Person personWithName:@"Tom"];
        Person *person7=[Person personWithName:@"Terry"];
        NSArray *array6=[NSArray arrayWithObjects:person4,person5,person6,person7, nil];
        //定义一个排序描述
        NSSortDescriptor *personName=[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
        NSSortDescriptor *accountBalance=[NSSortDescriptor sortDescriptorWithKey:@"account.balance" ascending:YES];
        NSArray *des=[NSArray arrayWithObjects:personName,accountBalance, nil];//先按照person的name排序再按照account的balance排序
        NSArray *array7=[array6 sortedArrayUsingDescriptors:des];
        NSLog(@"%@",array7);
        /*结果:
         (
             "name=Jack",
             "name=Jerry",
             "name=Terry",
             "name=Tom"
         )
         */
    }
    
    int main(int argc, const char * argv[]) {
        test1();
        test2();
        test3();
        test4();
        return 0;
    }

    需要注意几点:

    • NSArray中只能存放对象,不能存放基本数据类型,通常我们可以通过在基本数据类型前加@进行转换; 
    • 数组中的元素后面必须加nil以表示数据结束; 
    • makeObjectsPerformSelector执行数组中对象的方法,其参数最多只能有一个; 
    • 上面数组操作中无论是数组的追加、删除、截取都没有改变原来的数组,只是产生了新的数组而已; 
    • 对象的比较除了使用系统自带的方法,我们可以通过自定义比较器的方法来实现;

    可变数组

    下面看一下可变数组的内容:

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    #import "Person.h"
    
    
    void test1(){
        Person *person1=[Person personWithName:@"Kenshin"];
        Person *person2=[Person personWithName:@"Kaoru"];
        Person *person3=[Person personWithName:@"Rosa"];
        NSMutableArray *array1=[NSMutableArray arrayWithObjects:person1,person2,person3, nil];
        NSLog(@"%@",array1);
        /*结果:
         (
             "name=Kenshin",
             "name=Kaoru",
             "name=Rosa"
         )
         */
        
        Person *person4=[Person personWithName:@"Jack"];//此时person4的retainCount为1
        [array1 addObject:person4];//添加一个元素,此时person4的retainCount为2
        NSLog(@"%@",array1);
        /*结果:
         (
             "name=Kenshin",
             "name=Kaoru",
             "name=Rosa",
             "name=Jack"
         )
         */
        
        [array1 removeObject:person3];//删除一个元素
        NSLog(@"%@",array1);
        /*结果:
         (
             "name=Kenshin",
             "name=Kaoru",
             "name=Jack"
         )
         */
        
        [array1 removeLastObject];//删除最后一个元素,//此时person4的retainCount为1
        NSLog(@"%@",array1);
        /*结果:
         (
             "name=Kenshin",
             "name=Kaoru"
         )
         */
        
        [array1 removeAllObjects];//删除所以元素
        
        //注意当往数组中添加一个元素时会retain因此计数器+1,当从数组中移除一个元素时会release因此计数器-1
        //当NSMutalbeArray对象release的时候会依次调用每一个对象的release
    }
    void test2(){
        NSMutableArray *array1=[NSMutableArray arrayWithObjects:@"1",@"3",@"2", nil];
        NSLog(@"%@",array1);
        /*结果:
         (
             1,
             3,
             2
         )
         */
        
        NSArray *array2= [array1 sortedArrayUsingSelector:@selector(compare:)];//注意这个方法没有修改array1
        NSLog(@"%@",array1);
        /*结果:
         (
             1,
             3,
             2
         )
         */
        
        NSLog(@"%@",array2);
        /*结果:
         (
             1,
             2,
             3
         )
         */
        [array1 sortUsingSelector:@selector(compare:)];//这个方法会修改array1
        NSLog(@"%@",array1);
        /*结果:
         (
             1,
             2,
             3
         )
         */
        
    }
    
    int main(int argc, const char * argv[]) {
        
        test1();
        
        test2();
        
        return 0;
    }
    • 可变数组中的元素后面必须加nil以表示数据结束; 
    • 往一个可变数组中添加一个对象,此时这个对象的引用计数器会加1,当这个对象从可变数组中移除其引用计数器减1。同时当整个数组销毁之后会依次调用每个对象的releaes方法。 
    • 在不可变数组中无论对数组怎么排序,原来的数组顺序都不会改变,但是在可变数组中如果使用sortUsingSelector:排序原来的数组顺序就发生了变化。

     

    字典

    字典在我们日常开发中也是比较常用的,通过下面的代码我们看一下在ObjC中的字典的常用操作:初始化、遍历、排序

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    
    void test1(){
        NSDictionary *dic1=[NSDictionary dictionaryWithObject:@"1" forKey:@"a"];
        NSLog(@"%@",dic1);
        /*结果:
         {
            a = 1;
         }
         */
        
        //常用的方式
        NSDictionary *dic2=[NSDictionary dictionaryWithObjectsAndKeys:
                            @"1",@"a",
                            @"2",@"b",
                            @"3",@"c",
                            nil];
        NSLog(@"%@",dic2);
        /*结果:
         {
             a = 1;
             b = 2;
             c = 3;
         }
         */
        
        
        NSDictionary *dic3=[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"1",@"2", nil] forKeys:[NSArray arrayWithObjects:@"a",@"b", nil]];
        NSLog(@"%@",dic3);
        /*结果:
         {
             a = 1;
             b = 2;
         }
         */
        
        
        //更简单的方式
        NSDictionary *dic4=@{@"1":@"a",@"2":@"b",@"3":@"c"};
        NSLog(@"%@",dic4);
        /*结果:
         {
             1 = a;
             2 = b;
             3 = c;
         }
         */
    }
    void test2(){
        NSDictionary *dic1=[NSDictionary dictionaryWithObjectsAndKeys:
                            @"1",@"a",
                            @"2",@"b",
                            @"3",@"c",
                            @"2",@"d",
                            nil];
        NSLog(@"%zi",[dic1 count]); //结果:4
        NSLog(@"%@",[dic1 valueForKey:@"b"]);//根据键取得值,结果:2
        NSLog(@"%@",dic1[@"b"]);//还可以这样读取,结果:2
        NSLog(@"%@,%@",[dic1 allKeys],[dic1 allValues]);
        /*结果:
         (
             d,
             b,
             c,
             a
         ),(
             2,
             2,
             3,
             1
         )
    
         */
        
        NSLog(@"%@",[dic1 objectsForKeys:[NSArray arrayWithObjects:@"a",@"e" , nil]notFoundMarker:@"not fount"]);//后面一个参数notFoundMarker是如果找不到对应的key用什么值代替
        /*结果:
         (
             1,
             "not fount"
         )
         */
    }
    void test3(){
        NSDictionary *dic1=[NSDictionary dictionaryWithObjectsAndKeys:
                            @"1",@"a",
                            @"2",@"b",
                            @"3",@"c",
                            @"2",@"d",
                            nil];
        //遍历1
        for (id key in dic1) {//注意对于字典for遍历循环的是key
            NSLog(@"%@=%@",key,[dic1 objectForKey:key]);
        }
        /*结果:
         d=2
         b=2
         c=3
         a=1
         */
        
        //遍历2
        NSEnumerator *enumerator=[dic1 keyEnumerator];//还有值的迭代器[dic1 objectEnumerator]
        id key=nil;
        while (key=[enumerator nextObject]) {
            NSLog(@"%@=%@",key,[dic1 objectForKey:key]);
            
        }
        /*结果:
         d=2
         b=2
         c=3
         a=1
         */
        
        //遍历3
        [dic1 enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            NSLog(@"%@=%@",key,obj);
        }];
        /*结果:
         d=2
         b=2
         c=3
         a=1
         */
    }
    
    void test4(){
        NSMutableDictionary *dic=[NSMutableDictionary dictionaryWithObjectsAndKeys:@"1",@"a",
                                  @"2",@"b",
                                  @"3",@"c",
                                  @"2",@"d",
                                nil];
        [dic removeObjectForKey:@"b"];
        NSLog(@"%@",dic);
        /*结果:
         {
             a = 1;
             c = 3;
             d = 2;
         }
         */
        
        [dic addEntriesFromDictionary:@{@"e":@"7",@"f":@"6"}];
        NSLog(@"%@",dic);
        /*结果:
         {
             a = 1;
             c = 3;
             d = 2;
             e = 7;
             f = 6;
         }
         */
        
        [dic setValue:@"5" forKey:@"a"];
        NSLog(@"%@",dic);
        /*结果:
         {
             a = 5;
             c = 3;
             d = 2;
             e = 7;
             f = 6;
         }
         */
         
        
        //注意,一个字典的key或value添加到字典中时计数器+1;字典释放时调用key或value的release一次,计数器-1
    }
    
    
    int main(int argc, const char * argv[]) {
        test1();
        test2();
        test3();
        test4();
        return 0;
    }

    注意:同数组一样,不管是可变字典还是不可变字典初始化元素后面必须加上nil以表示结束。

    装箱和拆箱

    其实从上面的例子中我们也可以看到,数组和字典中只能存储对象类型,其他基本类型和结构体是没有办法放到数组和字典中的,当然你也是无法给它们发送消息的(也就是说有些NSObject的方法是无法调用的),这个时候通常会用到装箱(boxing)和拆箱(unboxing)。其实各种高级语言基本上都有装箱和拆箱的过程,例如C#中我们将基本数据类型转化为Object就是一个装箱的过程,将这个Object对象转换为基本数据类型的过程就是拆箱,而且在C#中装箱的过程可以自动完成,基本数据类型可以直接赋值给Object对象。但是在ObjC中装箱的过程必须手动实现,ObjC不支持自动装箱。

    在ObjC中我们一般将基本数据类型装箱成NSNumber类型(当然它也是NSObject的子类,但是NSNumber不能对结构体装箱),调用其对应的方法进行转换:

    +(NSNumber *)numberWithChar:(char)value;

    +(NSNumber *)numberWithInt:(int)value;

    +(NSNumber *)numberWithFloat:(float)value;

    +(NSNumber *)numberWithDouble:(double)value;

    +(NSNumber *)numberWithBool:(BOOL)value;

    +(NSNumber *)numberWithInteger:(NSInteger)value;

    拆箱的过程就更加简单了,可以调用如下方法:

    -(char)charValue;

    -(int)intValue;

    -(float)floatValue;

    -(double)doubleValue;

    -(BOOL)boolValue;

    简单看一个例子

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    
    /*可以存放基本类型到数组、字典*/
    void test1(){
        //包装类NSNumber,可以包装基本类型但是无法包装结构体类型
        NSNumber *number1=[NSNumber numberWithChar:'a'];//'a'是一个C语言的char类型我们无法放倒NSArray中,但是我们可以通过NSNumber包装
        NSArray *array1=[NSArray arrayWithObject:number1];
        NSLog(@"%@",array1);
        /*结果:
         (
            97
         )
         */
        
        NSNumber *number2= [array1 lastObject];
        NSLog(@"%@",number2);//返回的不是基本类型,结果:97
        
        
        char char1=[number2 charValue];//number转化为char
        NSLog(@"%c",char1); //结果:a
    }
    
    int main(int argc, const char * argv[]) {
        test1();
        return  0;
    }

    上面我们看到了基本数据类型的装箱和拆箱过程,那么结构体呢?这个时候我们需要引入另外一个类型NSValue,其实上面的NSNumber就是NSValue的子类,它包装了一些基本数据类型的常用装箱、拆箱方法,当要对结构体进行装箱、拆箱操作我们需要使用NSValue,NSValue可以对任何数据类型进行装箱、拆箱操作。

    事实上对于常用的结构体Foundation已经为我们提供好了具体的装箱方法:

    +(NSValue *)valueWithPoint:(NSPoint)point;

    +(NSValue *)valueWithSize:(NSSize)size;

    +(NSValue *)valueWithRect:(NSRect)rect;

    对应的拆箱方法:

    -(NSPoint)pointValue;

    -(NSSize)sizeValue;

    -(NSRect)rectValue;

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    //NSNumber是NSValue的子类,而NSValue可以包装任何类型,包括结构体
    void test1(){
        CGPoint point1=CGPointMake(10, 20);
        NSValue *value1=[NSValue valueWithPoint:point1];//对于系统自带类型一般都有直接的方法进行包装
        NSArray *array1=[NSArray arrayWithObject:value1];//放倒数组中
        NSLog(@"%@",array1);
        /*结果:
         (
            "NSPoint: {10, 20}"
         )
         */
        
        NSValue *value2=[array1 lastObject];
        CGPoint point2=[value2 pointValue];//同样对于系统自带的结构体有对应的取值方法(例如本例pointValue)
        NSLog(@"x=%f,y=%f",point2.x,point2.y);//结果:x=10.000000,y=20.000000
    }
    
    
    int main(int argc, const char * argv[]) {
        test1();
        return  0;
    }

     

    那么如果是我们自定义的结构体类型呢,这个时候我们需要使用NSValue如下方法进行装箱:

    +(NSValue *)valueWithBytes:(const void *)value objCType:(const char *)type;

    调用下面的方法进行拆箱:

    -(void)getValue:(void *)value;

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    typedef struct {
        int year;
        int month;
        int day;
    } Date;
    
    
    //NSNumber是NSValue的子类,而NSValue可以包装任何类型,包括结构体
    void test1(){
        //如果我们自己定义的结构体包装
        Date date={2014,2,28};
        char *type=@encode(Date);
        NSValue *value3=[NSValue value:&date withObjCType:type];//第一参数传递结构体地址,第二个参数传递类型字符串
        NSArray *array2=[NSArray arrayWithObject:value3];
        NSLog(@"%@",array2);
        /*结果:
         (
            "<de070000 02000000 1c000000>"
         )
         */
        
        Date date2;
        [value3 getValue:&date2];//取出对应的结构体,注意没有返回值
        //[value3 objCType]//取出包装内容的类型
        NSLog(@"%i,%i,%i",date2.year,date2.month,date2.day); //结果:2014,2,28
        
    }
    
    
    int main(int argc, const char * argv[]) {
        test1();
        return  0;
    }

    扩展1-NSNull

    通过前面的介绍大家都知道无论在数组还是在字典中都必须以nil结尾,否则数组或字典无法判断是否这个数组或字典已经结束(与C语言中的字符串比较类似,C语言中定义字符串后面必须加一个”\0”)。但是我们有时候确实想在数据或字典中存储nil值而不是作为结束标记怎么办呢?这个时候需要使用NSNull,这个类是一个单例,只有一个null方法。简单看一下:

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    
    
    int main(int argc, const char * argv[]) {
        
        NSNull *nl=[NSNull null];//注意这是一个对象,是一个单例,只有一个方法null创建一个对象
        NSNull *nl2=[NSNull null];
        NSLog(@"%i",nl==nl2);//由于是单例所以地址相等,结果:1
        
        NSArray *array1=[NSArray arrayWithObjects:@"abc",nl,@123, nil];
        NSLog(@"%@",array1);
        /*结果:
         (
             abc,
             "<null>",
             123
         )
         */
    
        return  0;
    }

     

    扩展2-@符号

    我们知道在ObjC中很多关键字前都必须加上@符号,例如@protocol、@property等,当然ObjC中的字符串必须使用@符号,还有就是%@可以表示输出一个对象。其实@符号在新版的ObjC中还有一个作用:装箱。

    相信聪明的童鞋在前面的例子中已经看到了,这里简单的介绍一下(在下面的演示中你也将看到很多ObjC新特性)。

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    typedef enum {
        spring,
        summer,
        autumn,
        winter
    } Season;
    
    int main(int argc, const char * argv[]) {
        /*装箱*/
        NSNumber *number1=@100;
        NSArray *array1=[NSArray arrayWithObjects:number1,@"abc",@16,@'A',@16.7,@YES, nil];
        NSLog(@"%@",array1);
        /*结果:
         (
             100,
             abc,
             16,
             65,
             "16.7"
             1
         )
         */
        NSNumber *number2=@(1+2*3);
        NSLog(@"%@",number2); //结果:7
        NSNumber *number3=@(autumn);
        NSLog(@"%@",number3); //结果:2
        
    
        NSArray *array2=@[@"abc",@16,@'A',@16.7,@YES];//使用这种方式最后不用添加nil值了
        NSLog(@"%@",array2[2]); //结果:65
        NSMutableArray *array3=[NSMutableArray arrayWithArray:array2];
        array3[0]=@"def";
        NSLog(@"%@",array3[0]); //结果:def
        
        NSDictionary *dic1=@{@"a":@123,@"b":@'c',@"c":@YES};
        NSLog(@"%@",dic1);
        /*结果:
         {
             a = 123;
             b = 99;
             c = 1;
         }
         */
        NSMutableDictionary *dic2=[NSMutableDictionary dictionaryWithDictionary:dic1];
        dic2[@"a"]=@456;
        NSLog(@"%@",dic2[@"a"]);//结果:456
    
        return 0;
    }

    反射

    由于ObjC动态性,在ObjC中实现反射可以说是相当简单,下面代码中演示了常用的反射操作,具体作用也都在代码中进行了注释说明:

    Account.h

    //
    //  Account.h
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    @interface Account : NSObject
    
    @property (nonatomic,assign) double balance;
    
    @end

    Account.m

    //
    //  Account.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import "Account.h"
    
    @implementation Account
    
    @end

    Person.h

    //
    //  Person.h
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    @class Account;
    
    @interface Person : NSObject
    
    @property (nonatomic,copy) NSString *name;
    @property (nonatomic,retain) Account *account;
    
    -(Person *)initWithName:(NSString *)name;
    
    +(Person *)personWithName:(NSString *)name;
    
    -(void)showMessage:(NSString *)infomation;
    
    //自己实现对象比较方法
    -(NSComparisonResult)comparePerson:(Person *)person;
    @end

    Person.m

    //
    //  Person.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import "Person.h"
    
    @implementation Person
    
    -(Person *)initWithName:(NSString *)name{
        if(self=[super init]){
            self.name=name;
        }
        return self;
    }
    
    +(Person *)personWithName:(NSString *)name{
        Person *person=[[Person alloc]initWithName:name];
        return person;
    }
    
    -(void)showMessage:(NSString *)infomation{
        NSLog(@"My name is %@,the infomation is \"%@\".",_name,infomation);
    }
    
    //自己实现对象比较方法
    -(NSComparisonResult)comparePerson:(Person *)person{
        return [_name compare:person.name];
    }
    
    -(NSString *)description{
        return [NSString stringWithFormat:@"name=%@",_name];
    }
    
    @end

    main.m

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    #import "Person.h"
    
    
    int main(int argc, const char * argv[]) {
        /*常用方法*/
        Person *person1=[Person personWithName:@"Kenshin"];
        NSLog(@"%i",[person1 isKindOfClass:[NSObject class]]); //判断一个对象是否为某种类型(如果是父类也返回YES),结果:1
        NSLog(@"%i",[person1 isMemberOfClass:[NSObject class]]); //判断一个对象是否是某个类的实例化对象,结果:0
        NSLog(@"%i",[person1 isMemberOfClass:[Person class]]); //结果:1
        NSLog(@"%i",[person1 conformsToProtocol:@protocol(NSCopying)]);//是否实现了某个协议,结果:0
        NSLog(@"%i",[person1 respondsToSelector:@selector(showMessage:)]);//是否存在某个方法,结果:1
        
        [person1 showMessage:@"Hello,world!"];//直接调用一个方法
        [person1 performSelector:@selector(showMessage:) withObject:@"Hello,world!"];
        //动态调用一个方法,注意如果有参数那么参数类型只能为ObjC对象,并且最多只能有两个参数
    
        
        /*反射*/
        //动态生成一个类
        NSString *className=@"Person";
        Class myClass=NSClassFromString(className);//根据类名生成类
        Person *person2=[[myClass alloc]init]; //实例化
        person2.name=@"Kaoru";
        NSLog(@"%@",person2);//结果:name=Kaoru
    
        //类转化为字符串
        NSLog(@"%@,%@",NSStringFromClass(myClass),NSStringFromClass([Person class])); //结果:Person,Person
    
        //调用方法
        NSString *methodName=@"showMessage:";
        SEL mySelector=NSSelectorFromString(methodName);
        Person *person3=[[myClass alloc]init];
        person3.name=@"Rosa";
        [person3 performSelector:mySelector withObject:@"Hello,world!"]; //结果:My name is Rosa,the infomation is "Hello,world!".
    
        //方法转化为字符串
        NSLog(@"%@",NSStringFromSelector(mySelector)); //结果:showMessage:
        
        return 0;
    }

    拷贝

    对象拷贝操作也比较常见,在ObjC中有两种方式的拷贝:copy和mutablecopy,这两种方式都将产生一个新的对象,只是后者产生的是一个可变对象。在ObjC中如果要想实现copy或者mutablecopy操作需要实现NSCopy或者NSMutableCopy协议,拷贝操作产生的新的对象默认引用计数器是1,在非ARC模式下我们应该对这个对象进行内存管理。在熟悉这两种操作之前我们首先需要弄清两个概念:深复制(或深拷贝)和浅复制(或浅拷贝)。

    • 浅复制:在执行复制操作时,对于对象中每一层(对象中包含的对象,例如说属性是某个对象类型)复制都是指针复制(如果从引用计数器角度出发,那么每层对象的引用计数器都会加1)。 
    • 深复制:在执行复制操作时,至少有一个对象的复制是对象内容复制(如果从引用计数器角度出发,那么除了对象内容复制的那个对象的引用计数器不变,其他指针复制的对象其引用计数器都会加1)。

    注:

    指针拷贝:拷贝的是指针本身(也就是具体对象的地址)而不是指向的对象内容本身。

    对象复制:对象复制指的是复制内容是对象本身而不是对象的地址。

    完全复制:上面说了深复制和浅复制,既然深复制是至少一个对象复制是对象内容复制,那么如果所有复制都是对象内容复制那么这个复制就叫完全复制。

    对比copy和mutablecopy其实前面我们一直还用到一个操作是retain,它们之间的关系如下:

    retain:始终采取浅复制,引用计数器会加1,返回的对象和被复制对象是同一个对象1(也就是说这个对象的引用多了一个,或者说是指向这个对象的指针多了一个);

    copy:对于不可变对象copy采用的是浅复制,引用计数器加1(其实这是编译器进行了优化,既然原来的对象不可变,复制之后的对象也不可变那么就没有必要再重新创建一个对象了);对于可变对象copy采用的是深复制,引用计数器不变(原来的对象是可变,现在要产生一个不可变的当然得重新产生一个对象);

    mutablecopy:无论是可变对象还是不可变对象采取的都是深复制,引用计数器不变(如果从一个不可变对象产生一个可变对象自然不用说两个对象绝对不一样肯定是深复制;如果从一个可变对象产生出另一个可变对象,那么当其中一个对象改变自然不希望另一个对象改变,当然也是深复制)。

    注:

    可变对象:当值发生了改变,那么地址也随之发生改变;

    不可变对象:当值发生了改变,内容首地址不发生变化;

    引用计数器:用于计算一个对象有几个指针在引用(有几个指针变量指向同一个内存地址);

     

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    
    void test1(){
        NSString *name=@"Kenshin";
        NSString *str1=[NSString stringWithFormat:@"I'm %@.",name];//注意此时str1的计数器是1
        NSLog(@"%lu",[str1 retainCount]); //结果:1
        
        
        NSMutableString *str2=[str1 mutableCopy];//注意此时str2的计数器为1,str1的计数器还是1
        //NSMutableString *str5 =CFRetain((__bridge CFTypeRef)str2);
        NSLog(@"retainCount(str1)=%lu,retainCount(str2)=%lu",[str1 retainCount],[str2 retainCount]);
        //结果:retainCount(str1)=1,retainCount(str2)=1
        
        
        [str2 appendString:@"def"];//改变str2,str1不变
        NSLog(@"%zi",str1==str2);//二者不是向同一个对象,结果:0
        NSLog(@"str1=%@",str1); //结果:str1=I'm Kenshin.
        NSLog(@"str2=%@",str2); //结果:str2=I'm Kenshin.def
        
        
        NSLog(@"str1's %lu",[str1 retainCount]);
        NSString *str3=[str1 copy];//str3不是产生的新对象而是复制了对象指针,但是str1的计数器+1(当然既然str3同样指向同一个对象,那么如果计算str3指向的对象引用计数器肯定等于str1的对象引用计数器)
        NSLog(@"%zi",str1==str3);//二者相等指向同一个对象,结果:1
        NSLog(@"retainCount(str1)=%lu,retainCount(str3)=%lu",str1.retainCount,str3.retainCount);
        //结果:retainCount(str1)=2,retainCount(str3)=2
        
        //需要注意的是使用copy和mutableCopy是深复制还是浅复制不是绝对,关键看由什么对象产生什么样的对象
        NSString *str4=[str2 copy];//由NSMutableString产生了NSString,二者类型都不同肯定是深拷贝,此时str2的计数器还是1,str4的计数器也是1
        [str2 appendString:@"g"];//改变原对象不影响str4
        NSLog(@"%zi",str2==str4); //结果:0
        NSLog(@"str2=%@",str2); //结果:str2=I'm Kenshin.defg
        NSLog(@"str4=%@",str4); //结果:str4=I'm Kenshin.def
    
        
        [str1 release];
        str1=nil;
        [str3 release];//其实这里也可以调用str1再次release,因为他们两个指向的是同一个对象(但是一般不建议那么做,不容易理解)
        str3=nil;
        
        [str2 release];
        str2=nil;
        [str4 release];
        str4=nil;
        
        //上面只有一种情况是浅拷贝:不可变对象调用copy方法
        
    }
    
    int main(int argc,char *argv[]){
        test1();
        return 0;
    }

    为了方便大家理解上面的代码,这里以图形画出str1、str2、str3、str4在内存中的存储情况: 

    MemoryStore

    从上面可以清楚的看到str1和str3同时指向同一个对象,因此这个对象的引用计数器是2(可以看到两箭头指向那个对象),str2和str4都是两个新的对象;另外ObjC引入对象拷贝是为了改变一个对象不影响另一个对象,但是我们知道NSString本身就不能改变那么即使我重新复制一个对象也没有任何意义,因此为了性能着想如果通过copy方法产生一个NSString时ObjC不会再复制一个对象而是将新变量指向同一个对象。 

    注意网上很多人支招在ARC模式下可以利用_objc_rootRetainCount()或者CFGetRetainCount()取得retainCount都是不准确的,特别是在对象拷贝操作之后你会发现二者取值也是不同的,因此如果大家要查看retainCount最好还是暂时关闭ARC。

    要想支持copy或者mutablecopy操作那么对象必须实现NSCoping协议并实现-(id)copyWithZone:(NSZone*)zone方法,在Foundation中常用的可复制对象有:NSNumber、NSString、NSMutableString、NSArray、NSMutableArray、NSDictionary、NSMutableDictionary。下面看一下如何让自定义的类支持copy操作:

    Person.h

    //
    //  Person.h
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    @class Account;
    
    @interface Person : NSObject
    
    @property  NSMutableString *name;
    @property (nonatomic,assign) int age;
    
    
    @end

    Person.m

    //
    //  Person.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import "Person.h"
    
    @implementation Person
    
    
    -(NSString *)description{
        return [NSString stringWithFormat:@"name=%@,age=%i",_name,_age];
    }
    
    //实现copy方法
    -(id)copyWithZone:(NSZone *)zone{
        //注意zone是系统已经分配好的用于存储当前对象的内存
        //注意下面创建对象最好不要用[[Person allocWithZone:zone]init],因为子类如果没有实现该方法copy时会调用父类的copy方法,此时需要使用子类对象初始化如果此时用self就可以表示子类对象,还有就是如果子类调用了父类的这个方法进行重写copy也需要调用子类对象而不是父类Person
        Person *person1=[[[self class] allocWithZone:zone]init];
        person1.name=_name;
        person1.age=_age;
        return person1;
    }
    
    @end
    

    main.m

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    #import "Account.h"
    #import "Person.h"
    
    void test1(){
        Person *person1=[[Person alloc]init];
        NSMutableString *str1=[NSMutableString stringWithString:@"Kenshin"];
        person1.name=str1;
        //由于name定义的时候使用属性参数采用的是copy策略,而根据前面的知识我们知道NSMutableString的copy策略采用的是对象内容复制,因此如果修改str1不会改变person1.name
        [str1 appendString:@" Cui"];
        NSLog(@"%@",str1);//结果:Kenshin Cui
        NSLog(@"%@",person1.name); //结果:Kenshin
        
    }
    
    void test2(){
        Person *person1=[[Person alloc]init];
        person1.name=[NSMutableString stringWithString:@"Kenshin"];
        person1.age=28;
        Person *person2=[person1 copy];
        NSLog(@"%@",person1); //结果:name=Kenshin,age=0
        NSLog(@"%@",person2); //结果:name=Kenshin,age=0
        
        [person2.name appendString:@" Cui"];
        
        NSLog(@"%@",person1);//结果:name=Kenshin Cui,age=28
        NSLog(@"%@",person2);//结果:name=Kenshin Cui,age=28
    }
    
    int main(int argc,char *argv[]){
        test1();
        test2();
        return 0;
    }

    在上面的代码中重点说一下test2这个方法,在test2方法中我们发现当修改了person2.name属性之后person1.name也改变了,这是为什么呢?我们可以看到在Person.m中自定义实现了copy方法,同时实现了一个浅拷贝。之所以说是浅拷贝主要是因为我们的name属性参数是直接赋值完成的,同时由于name属性定义时采用的是assign参数(默认为assign),所以当通过copy创建了person2之后其实person2对象的name属性和person1指向同一个NSMutableString对象。通过图形表示如下:

    MemoryStore2

    上面test2的写法纯属为了让大家了解复制的原理和本质,实际开发中我们很少会遇到这种情况,首先我们一般定义name的话可能用的是NSString类型,根本也不能修改;其次我们定义字符串类型的话一般使用(copy)参数,同样可以避免这个问题(因为NSMutableString的copy是深复制)。那么如果我们非要使用NSMutabeString同时不使用属性的copy参数如何解决这个问题呢?答案就是使用深复制,将-(id)copyWithZone:(NSZone *)zone方法中person1.name=_name改为,person1.name=[_name copy];或person1.name=[_name mutablecopy]即可,这样做也正好满足我们上面对于深复制的定义。

    补充-NSString的引用计数器

    在好多语言中字符串都是一个特殊的对象,在ObjC中也不例外。NSString作为一个对象类型存储在堆中,多数情况下它跟一般的对象类型没有区别,但是这里我们需求强调一点那就是字符串的引用计数器。

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    
    int main(int argc,char *argv[]){
        
        NSString *str1=@"Kenshin";
        NSLog(@"retainCount(str1)=%i",(unsigned long)str1.retainCount); //结果:-1
        [str1 retain];
        NSLog(@"retainCount(str1)=%i",(unsigned long)str1.retainCount); //结果:-1
        
        NSString *str2=[NSString stringWithString:@"Kaoru"];
        NSLog(@"retainCount(str2)=%i",str2.retainCount); //结果:-1
        [str1 retain];
        NSLog(@"retainCount(str2)=%i",str2.retainCount); //结果:-1
        NSString *str2_1=[NSString stringWithString:[NSString stringWithFormat:@"Kaoru %@",@"sun"]];
        NSLog(@"retainCount(str2_1)=%i",str2_1.retainCount);//结果:2 
        [str2_1 release];
        [str2_1 release];
        
        
        
        NSString *str3=[NSString stringWithFormat:@"Rosa %@",@"Sun"];
        NSLog(@"retainCount(str3)=%i",str3.retainCount); //结果:1
        [str3 retain];
        NSLog(@"retainCount(str3)=%i",str3.retainCount); //结果:2
        [str3 release];
        [str3 release];
        
        NSString *str4=[NSString stringWithUTF8String:"Jack"];
        NSLog(@"retainCount(str4)=%i",str4.retainCount); //结果:1
        [str4 retain];
        NSLog(@"retainCount(str4)=%i",str4.retainCount); //结果:2
        [str4 release];
        [str4 release];
        
        NSString *str5=[NSString stringWithCString:"Tom" encoding:NSUTF8StringEncoding];
        NSLog(@"retainCount(str5)=%i",str5.retainCount); //结果:1
        [str5 retain];
        NSLog(@"retainCount(str5)=%i",str5.retainCount); //结果:2
        [str5 release];
        [str5 release];
        
        
        
        NSMutableString *str6=@"Jerry";
        NSLog(@"retainCount(str6)=%i",str6.retainCount); //结果:-1
        [str6 retain];
        NSLog(@"retainCount(str6)=%i",str6.retainCount); //结果:-1
        [str6 release];
        [str6 release];
        
        NSMutableArray *str7=[NSMutableString stringWithString:@"Lily"];
        NSLog(@"retainCount(str7)=%i",str7.retainCount); //结果:1
        [str7 retain];
        NSLog(@"retainCount(str7)=%i",str7.retainCount); //结果:2
        [str7 release];
        [str7 release];
    
        
        return 0;
    }

    看完上面的例子如果不了解NSString的处理你也许会有点奇怪(注意上面的代码请在Xcode5下运行)?请看下面的解释

    • str1是一个字符串常量,它存储在常量区,系统不会对它进行引用计数,因此无论是初始化还是做retain操作其引用计数器均为-1; 
    • str3、str4、str5创建的对象同一般对象类似,存储在堆中,系统会对其进行引用计数; 
    • 采用stringWithString定义的变量有些特殊,当后面的字符串是字符串常量,则它本身就作为字符串常用量存储(str2),类似于str1;如果后面的参数是通过类似于str3、str4、str5的定义,那么它本身就是一个普通对象,只是后面的对象引用计数器默认为1,当给它赋值时会做一次拷贝操作(浅拷贝),引用计数器加1,所有str2_1引用计数器为2; 
    • str6其实和str1类似,虽然定义的是可变数组,但是它的本质还是字符串常量,事实上对于可变字符串只有为字符串常量时引用计数器才为-1,其他情况它的引用计数器跟一般对象完全一致;

    后记:注意上面这段代码的运行结果是在Xcode5中运行的结果,事实上针对最新的Xcode6由于LLVM的优化,只有str2_1和str7的引用计数器为1(str7 retain一次后第二次为2),其他均为-1。

    文件操作

    在今天的最后一节内容中让我们看一下Foundation中文件操作,下面将以一个例子进行说明:

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    /*目录操作*/
    void test1(){
        //文件管理器是专门用于文件管理的类
        NSFileManager *manager=[NSFileManager defaultManager];
        
        //获得当前程序所在目录(当然可以改变)
        NSString *currentPath=[manager currentDirectoryPath];
        NSLog(@"current path is :%@",currentPath);
        //结果:/Users/kenshincui/Library/Developer/Xcode/DerivedData/FoundationFramework-awxjohcpgsqcpsanqofqogwbqgbx/Build/Products/Debug
        
        //创建目录
        NSString *myPath=@"/Users/kenshincui/Desktop/myDocument";
        BOOL result= [manager createDirectoryAtPath:myPath withIntermediateDirectories:YES attributes:nil error:nil];
        if(result==NO){
            NSLog(@"Couldn't create directory!");
        }
        
        //目录重命名,如果需要删除目录只要调用removeItemAtPath:<#(NSString *)#> error:<#(NSError **)#>
        NSError *error;
        NSString *newPath=@"/Users/kenshincui/Desktop/myNewDocument";
        if([manager moveItemAtPath:myPath toPath:newPath error:&error]==NO){
            NSLog(@"Rename directory failed!Error infomation is:%@",error);
        }
        
        //改变当前目录
        if([manager changeCurrentDirectoryPath:newPath]==NO){
            NSLog(@"Change current directory failed!");
        }
        NSLog(@"current path is :%@",[manager currentDirectoryPath]);
        //结果:current path is :/Users/kenshincui/Desktop/myNewDocument
        
        //遍历整个目录
        NSString *path;
        NSDirectoryEnumerator *directoryEnumerator= [manager enumeratorAtPath:newPath];
        while (path=[directoryEnumerator nextObject]) {
            NSLog(@"%@",path);
        }
        /*结果:
         documents
         est.txt
        */
        
        //或者这样遍历
        NSArray *paths= [manager contentsOfDirectoryAtPath:newPath error:nil];
        NSObject *p;
        for (p in paths) {
            NSLog(@"%@",p);
        }
        /*结果:
         documents
         est.txt
         */
    }
    
    /*文件操作*/
    void test2(){
        NSFileManager *manager=[NSFileManager defaultManager];
        NSString *filePath=@"/Users/kenshincui/Desktop/myNewDocument/test.txt";
        NSString *filePath2=@"/Users/kenshincui/Desktop/test.txt";
        NSString *newPath=@"/Users/kenshincui/Desktop/myNewDocument/test2.txt";
        
        //判断文件是否存在,这个方法也可以判断目录是否存在,这要后面的参数设置位YES
        if ([manager fileExistsAtPath:filePath isDirectory:NO]) {
            NSLog(@"File exists!");
        }
        
        //文件是否可读
        if([manager isReadableFileAtPath:filePath]){
            NSLog(@"File is readable!");
        }
        
        //判断两个文件内容是否相等
        if ([manager contentsEqualAtPath:filePath andPath:filePath2]) {
            NSLog(@"file1 equals file2");
        }
        
        //文件重命名,方法类似于目录重命名
        if (![manager moveItemAtPath:filePath toPath:newPath error:nil]) {
            NSLog(@"Rename file1 failed!");
        }
        
        //文件拷贝
        NSString *filePath3=@"/Users/kenshincui/Desktop/test3.txt";
        if(![manager copyItemAtPath:newPath toPath:filePath3 error:nil]){
            NSLog(@"Copy failed!");
        }
        
        //读取文件属性
        NSDictionary *attributes;
        if ((attributes=[manager attributesOfItemAtPath:newPath error:nil])==nil) {
            NSLog(@"Read attributes failed!");
        }
        for (NSObject *key in attributes) {
            NSLog(@"%@=%@",key,attributes[key]);
        }
        /*结果:
             NSFileOwnerAccountID=501
             NSFileHFSTypeCode=0
             NSFileSystemFileNumber=1781953
             NSFileExtensionHidden=0
             NSFileSystemNumber=16777218
             NSFileSize=27
             NSFileGroupOwnerAccountID=20
             NSFileOwnerAccountName=kenshincui
             NSFileCreationDate=2014-07-28 11:47:58 +0000
             NSFilePosixPermissions=420
             NSFileHFSCreatorCode=0
             NSFileType=NSFileTypeRegular
             NSFileExtendedAttributes={
             "com.apple.TextEncoding" = <7574662d 383b3133 34323137 393834>;
             }
             NSFileGroupOwnerAccountName=staff
             NSFileReferenceCount=1
             NSFileModificationDate=2014-07-28 11:47:58 +0000
         */
        
        //删除文件
        [manager removeItemAtPath:newPath error:nil];
        
    }
    //文件操作--文件内容操作(NSData,非结构化字节流对象,有缓冲区管理机制,可用于网络传输)
    void test3(){
        NSFileManager *manager=[NSFileManager defaultManager];
        NSString *filePath=@"/Users/kenshincui/Desktop/myNewDocument/test2.txt";
        NSData *data=[manager contentsAtPath:filePath];
        NSLog(@"%@",data);//存储的是二进制字节流
        //结果:<68656c6c 6f20776f 726c642c e4b896e7 958ce4bd a0e5a5bd efbc81>
        
        //NSData转化成字符串
        NSString *str1=[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"%@",str1);
        //结果:hello world,世界你好!
        
        //字符串转化成NSData
        NSString *str2=@"Kenshin";
        NSData *data2=[str2 dataUsingEncoding:NSUTF8StringEncoding];
        NSLog(@"%@",data2);
        
        //当然一般如果仅仅是简单读取文件内容,直接用户NSString方法即可
        NSString *content=[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
        NSLog(@"%@",content);
        //结果:hello world,世界你好!
        
    }
    //文件操作--细粒度控制文件,文件操作柄
    void test4(){
        NSFileManager *manager=[NSFileManager defaultManager];
        NSString *filePath=@"/Users/kenshincui/Desktop/myNewDocument/test2.txt";
        
        //以只读方式打开文件
        NSFileHandle *fileHandle=[NSFileHandle fileHandleForReadingAtPath:filePath];//注意这个方法返回类型为instancetype,也就是说对于上面的NSFileHandle它的返回类型也是NSFileHandle
        NSData *data= [fileHandle readDataToEndOfFile];//完整读取文件
        NSString *newPath=@"/Users/kenshincui/Desktop/test4.txt";
        [manager createFileAtPath:newPath contents:nil attributes:nil];
        NSFileHandle *fileHandle2=[NSFileHandle fileHandleForWritingAtPath:newPath];//以可写方式打开文件
        [fileHandle2 writeData:data];//写入文件内容
        
        [fileHandle2 closeFile];//关闭文件
    
        
        //定位到指定位置,默认在文件开头
        [fileHandle seekToFileOffset:12];
        NSData *data2= [fileHandle readDataToEndOfFile];
        NSLog(@"data2=%@",[[NSString alloc]initWithData:data2 encoding:NSUTF8StringEncoding]);
        //结果:data2=世界你好!
        
        [fileHandle seekToFileOffset:6];
        NSData *data3=[fileHandle readDataOfLength:5];
        NSLog(@"data3=%@",[[NSString alloc]initWithData:data3 encoding:NSUTF8StringEncoding]);
        //结果:data3=world
        
        [fileHandle closeFile];
        
    }
    
    //文件路径
    void test5(){
        NSString *filePath=@"/Users/kenshincui/Desktop/myDocument";
        NSString *filePath2=@"/Users/kenshincui/Desktop/test.txt";
    
        //临时文件所在目录
        NSString *path=NSTemporaryDirectory();
        NSLog(@"temporary directory is :%@",path);
        //结果:/var/folders/h6/lss6gncs509c2pgzgty3wd_40000gn/T/
    
        NSString *lastComponent= [filePath lastPathComponent];
        NSLog(@"%@",lastComponent); //结果:myDocument
        
        NSLog(@"%@",[filePath stringByDeletingLastPathComponent]);
        //结果:/Users/kenshincui/Desktop
        NSLog(@"%@",[filePath stringByAppendingPathComponent:@"Pictrues"]);
        //结果:/Users/kenshincui/Desktop/myDocument/Pictrues
        NSLog(@"%@",[filePath2 pathExtension]);
        //结果:txt
        
        [[filePath pathComponents] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            NSLog(@"%i=%@",idx,obj);
        }];
        /*结果:
         0=/
         1=Users
         2=kenshincui
         3=Desktop
         4=myDocument
         */
        
        
    }
    
    //文件操作--NSURL
    void test6(){
        NSURL *url=[NSURL URLWithString:@"http://developer.apple.com"];
        NSString *str1=[NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
        NSLog(@"%@",str1);
    }
    
    //文件操作--NSBundle,程序包,一般用于获取Resource中的资源(当然由于当前并非IOS应用没有程序包,只是表示当前程序运行路径)
    //在ios中经常用于读取应用程序中的资源文件,如图片、声音、视频等
    void test7(){
        //在程序包所在目录创建一个文件
        NSFileManager *manager=[NSFileManager defaultManager];
        NSString *currentPath=[manager currentDirectoryPath];
        NSLog(@"current path is :%@",currentPath);
        //结果:current path is :/Users/kenshincui/Library/Developer/Xcode/DerivedData/FoundationFramework-awxjohcpgsqcpsanqofqogwbqgbx/Build/Products/Debug
        NSString *filePath=[currentPath stringByAppendingPathComponent:@"test.txt"];
        [manager createFileAtPath:filePath contents:[@"Hello,world!" dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
        
        
        //利用NSBundle在程序包所在目录查找对应的文件
        NSBundle *bundle=[NSBundle mainBundle];//主要操作程序包所在目录
        //如果有test.txt则返回路径,否则返回nil
        NSString *path=[bundle pathForResource:@"test" ofType:@"txt"];//也可以写成:[bundle pathForResource:@"instructions.txt" ofType:nil];
        NSLog(@"%@",path);
        //结果:/Users/kenshincui/Library/Developer/Xcode/DerivedData/FoundationFramework-awxjohcpgsqcpsanqofqogwbqgbx/Build/Products/Debug/test.txt
        NSLog(@"%@",[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]);
        //结果:Hello,world!
        
        //假设我们在程序运行创建一个Resources目录,并且其中新建pic.jpg,那么用下面的方法获得这个文件完整路径
        NSString *path1= [bundle pathForResource:@"pic" ofType:@"jpg" inDirectory:@"Resources"];
        NSLog(@"%@",path1);
        //结果:/Users/kenshincui/Library/Developer/Xcode/DerivedData/FoundationFramework-awxjohcpgsqcpsanqofqogwbqgbx/Build/Products/Debug/Resources/pic.jpg
    }
    
    int main(int argc,char *argv[]){
    
        test1();
        test2();
        test3();
        test4();
        test5();
        test6();
        test7();
        
        return 0;
    }

    归档

    归档,在其他语言中又叫“序列化”,就是将对象保存到硬盘;解档,在其他语言又叫“反序列化”就是将硬盘文件还原成对象。其实归档就是数据存储的过程,在IOS中数据的存储有五种方式:

    1. xml属性列表(plist归档)

    2. NSUserDefaults(偏好设置)

    3. NSKeyedArchiver归档(加密形式)

    4. SQLite3(嵌入式数据库)

    5. Core Data(面向对象方式的嵌入式数据库)

    当然关于2、4、5点不是我们今天介绍的重点,这个在IOS开发过程中我们会重点说到。

    xml属性列表

    首先我们先来看一下xml属性列表,xml属性列表进行归档的方式是将对象存储在一个plist文件中,这个操作起来比较简单,其实相当于xml序列化。但是同时它也有缺点:一是这种方式是明文保存的;二是这种方式操作的对象有限,只有NSArray、NSMutableArray、NSDictionary、NSMutableDictionary支持(归档时只要调用对应的writeToFile方法即可,解档调用arrayWithContentsOfFile或dictionaryWithContentsOfFile,注意像NSString、NSNumber、NSData即使有这个方法它存储的也不是xml格式)。

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    //xml属性
    void test1(){
        //数组
        NSString *path=@"/Users/kenshincui/Desktop/arrayXml.plist";
        NSArray *array1=@[@"Kenshin",@"Kaoru",@"Rosa"];
        [array1 writeToFile:path atomically:YES];
        
        NSArray *array2=[NSArray arrayWithContentsOfFile:path];
        [array2 enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            NSLog(@"array2[%lu]=%@",idx,obj);
        }];
        /*结果:
         array1[0]=Kenshin
         array1[1]=Kaoru
         array1[2]=Rosa
         */
        
        
        //字典
        NSString *path2=@"/Users/kenshincui/Desktop/dicXml.plist";
        NSDictionary *dic1=@{@"name":@"Kenshin",@"age":@28,@"height":@172.5};
        [dic1 writeToFile:path2 atomically:YES];
        
        NSDictionary *dic2=[NSDictionary dictionaryWithContentsOfFile:path2];
        [dic2 enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            NSLog(@"dic2[%@]=%@",key,obj);
        }];
        /*结果:
         dic2[height]=172.5
         dic2[age]=28
         dic2[name]=Kenshin
         */
    }
    
    int main(int argc,char *argv[]){
        
        test1();
        
        return 0;
    }

    生成的文件如下

    arrayXml

    dicXml

    NSKeyedArchiver归档

    如果要针对更多对象归档或者需要归档时能够加密的话就需要使用NSKeyedArchiver进行归档和解档,使用这种方式归档的范围更广而且归档内容是密文存储。从归档范围来讲NSKeyedArchiver适合所有ObjC对象,但是对于自定义对象我们需要实现NSCoding协议;从归档方式来讲NSKeyedArchiver分为简单归档和复杂对象归档,简单归档就是针对单个对象可以直接将对象作为根对象(不用设置key),复杂对象就是针对多个对象,存储时不同对象需要设置不同的Key。

    首先看一下系统对象两种归档方式(注意由于本章主要介绍Foundation内容,下面的程序是OS X命令行程序并没有创建成iOS应用,如果移植到到iOS应用下运行将NSArchiver和NSUnarchiver换成NSKeyedArchiver和NSKeyedUnarchiver。虽然在Foundation部分iOS和OS X在设计上尽可能通用但是还存在着细微差别。)

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    //系统对象简单归档
    void test1(){
        //NSString归档
        NSString *str1=@"Hello,world!";
        NSString *path1=@"/Users/kenshincui/Desktop/archiver1.arc";
        if(![NSArchiver archiveRootObject:str1 toFile:path1]){
            NSLog(@"archiver failed!");
        }
        //NSString解档
        NSString *str2= [NSUnarchiver unarchiveObjectWithFile:path1];
        NSLog(@"str2=%@",str2);//结果:str2=Hello,world!
        
        
        //NSArray归档
        NSString *path2=@"/Users/kenshincui/Desktop/archiver2.arc";
        NSArray *array1=@[@"Kenshin",@"Kaoru",@"Rosa"];
        if(![NSArchiver archiveRootObject:array1 toFile:path2]){
            NSLog(@"archiver failed!");
        }
        //NSArray解档
        NSArray *array2=[NSUnarchiver unarchiveObjectWithFile:path2];
        [array2 enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            NSLog(@"array2[%lu]=%@",idx,obj);
        }];
        /*结果:
         array2[0]=Kenshin
         array2[1]=Kaoru
         array2[2]=Rosa
         */
    }
    
    //系统复杂对象归档(多对象归档)
    void test2(){
        /*归档*/
        NSString *path1=@"/Users/kenshincui/Desktop/archiver3.arc";
        
        int int1=89;
        CGSize size1={12.5,16.8};
        NSNumber *number1=@60.5;
        NSString *str1=@"Hello,world!";
        NSArray *array1=@[@"Kenshin",@"Kaoru",@"Rosa"];
        NSDictionary *dic1=@{@"name":@"Kenshin",@"age":@28,@"height":@172.5};
        
        //同时对多个对象进行归档
        NSMutableData *data1=[[NSMutableData alloc]init];//定义一个NSMutableData用于临时存放数据
        NSKeyedArchiver *archiver=[[NSKeyedArchiver alloc]initForWritingWithMutableData:data1];//定义归档对象
        [archiver encodeInt:int1 forKey:@"int"];//对int1归档并指定一个key以便以后读取
        [archiver encodeSize:size1 forKey:@"size"];
        [archiver encodeObject:number1 forKey:@"number"];
        [archiver encodeObject:str1 forKey:@"string"];
        [archiver encodeObject:array1 forKey:@"array"];
        [archiver encodeObject:dic1 forKey:@"dic"];
    
        [archiver finishEncoding];//结束归档
        
        [data1 writeToFile:path1 atomically:YES];//写入文件
        
        
        
        /*解档*/
        int int2;
        CGSize size2;
        NSNumber *number2;
        NSString *str2;
        NSArray *array2;
        NSDictionary *dic2;
        
        NSData *data2=[[NSData alloc]initWithContentsOfFile:path1];//读出数据到NSData
        NSKeyedUnarchiver *unarchiver=[[NSKeyedUnarchiver alloc]initForReadingWithData:data2];
        
        int2= [unarchiver decodeInt64ForKey:@"int"];
        size2=[unarchiver decodeSizeForKey:@"size"];
        number2=[unarchiver decodeObjectForKey:@"number"];
        str2=[unarchiver decodeObjectForKey:@"string"];
        array2=[unarchiver decodeObjectForKey:@"array"];
        dic2=[unarchiver decodeObjectForKey:@"dic"];
        
        [unarchiver finishDecoding];
        
        NSLog(@"int2=%i,size=%@,number2=%@,str2=%@,array2=%@,dic2=%@",int2,NSStringFromSize(size2),number2,str2,array2,dic2);
        /*结果:
         int2=89,
         size={12.5, 16.800000000000001},
         number2=60.5,
         str2=Hello,world!,
         array2=(
             Kenshin,
             Kaoru,
             Rosa
         ),
         dic2={
             age = 28;
             height = "172.5";
             name = Kenshin;
         }
         */
    }
    
    
    int main(int argc,char *argv[]){
    
        test1();
        test2();
        
        return 0;
    }

    接下来看一下自定义的对象如何归档,上面说了如果要对自定义对象进行归档那么这个对象必须实现NSCoding协议,在这个协议中有两个方法都必须实现:

    -(void)encodeWithCoder:(NSCoder *)aCoder;通过给定的Archiver对消息接收者进行编码;

    -(id)initWithCoder:(NSCoder *)aDecoder;从一个给定的Unarchiver的数据返回一个初始化对象;

    这两个方法分别在归档和解档时调用。下面通过一个例子进行演示(注意对于自定义类的多对象归档与系统类多对象归档完全一样,代码中不再演示):

    Person.h

    //
    //  Person.h
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    @interface Person : NSObject<NSCoding>
    
    @property (nonatomic,copy) NSString *name;
    @property (nonatomic,assign) int age;
    @property (nonatomic,assign) float height;
    @property (nonatomic,assign) NSDate *birthday;
    
    @end

    Person.m

    //
    //  Person.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import "Person.h"
    
    @implementation Person
    
    #pragma mark 解码
    -(id)initWithCoder:(NSCoder *)aDecoder{
        NSLog(@"decode...");
        if (self=[super init]) {
            self.name=[aDecoder decodeObjectForKey:@"name"];
            self.age=[aDecoder decodeInt64ForKey:@"age"];
            self.height=[aDecoder decodeFloatForKey:@"heiht"];
            self.birthday=[aDecoder decodeObjectForKey:@"birthday"];
        }
        return self;
    }
    
    #pragma mark 编码
    -(void)encodeWithCoder:(NSCoder *)aCoder{
        NSLog(@"encode...");
        [aCoder encodeObject:_name forKey:@"name"];
        [aCoder encodeInt64:_age forKey:@"age" ];
        [aCoder encodeFloat:_height forKey:@"height"];
        [aCoder encodeObject:_birthday forKey:@"birthday"];
    
    }
    
    #pragma mark 重写描述
    -(NSString *)description{
        NSDateFormatter *formater1=[[NSDateFormatter alloc]init];
        formater1.dateFormat=@"yyyy-MM-dd";
        return [NSString stringWithFormat:@"name=%@,age=%i,height=%.2f,birthday=%@",_name,_age,_height,[formater1 stringFromDate:_birthday]];
    }
    
    @end

    main.m

    //
    //  main.m
    //  FoundationFramework
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    #import "Person.h"
    
    
    int main(int argc,char *argv[]){
    
        //归档
        Person *person1=[[Person alloc]init];
        person1.name=@"Kenshin";
        person1.age=28;
        person1.height=1.72;
        NSDateFormatter *formater1=[[NSDateFormatter alloc]init];
        formater1.dateFormat=@"yyyy-MM-dd";
        person1.birthday=[formater1 dateFromString:@"1986-08-08"];
        
        NSString *path1=@"/Users/kenshincui/Desktop/person1.arc";
        
        [NSKeyedArchiver archiveRootObject:person1 toFile:path1];
    
        //解档
        Person *person2= [NSKeyedUnarchiver unarchiveObjectWithFile:path1];
        NSLog(@"%@",person2);
        /*结果:
         name=Kenshin,age=28,height=0.00,birthday=1986-08-08
         */
        
        return 0;
    }
    今天的文章就到这里了,内容确实不少,但是要用一篇文章把Foundation的所有内容说完也是完全不现实的。这篇文章有一部分内容并没有详细的解释代码,这部分内容主要是个人认为比较简单,通过注释大家就可以理解。不过并不是说这部分内容不重要,而是这些内容多数是记忆性的东西,不需要过多解释。
    展开全文
  • cocoa touch框架UIkit学习

    2016-03-16 13:15:06
    iOS中框架就是一个目录,这个目录包含了共享库,可以访问共享库的头文件,和其他图片声音等资源,... Touch Layer(触摸UI层)、MediaLayer(媒体层)、Core Services Layer(核心服务层)、Core OS Layer(核心OS层)。

    iOS中框架就是一个目录,这个目录包含了共享库,可以访问共享库的头文件,和其他图片声音等资源,可以被应用程序所调用.应用程序可以使用很多框架,这些框架构成了IOS操作系统的层次架构,一共分为四层,从上到下依次为:Cocoa Touch Layer(触摸UI层)MediaLayer(媒体层)Core Services Layer(核心服务层)Core OS Layer(核心OS层)

       今天学习了cocoa touch layer的UIKIT,uikit为iOS提供了基于触摸的界面对象和控制器

    1.UIButton 按钮,是UIKIT的一个类,你可以设置按钮的文字,图像背景等属性.这个对象可以侦听触摸事件.当用户触摸按钮时这个对象可以调用事件关联对象上的某一个方法

    2.UILabel-标签,用于显示不可更改的文本,并会随着文本的大小改变自身的大小

    3.UISlider-滑动条 通过滑动条来选择某一个范围的值,只允许用户选择一个值

    4.UIDatePicker-日期选择器 显示一个多栏旋转的轮子,用于用户选择日期和时间

    5.UISegmentedControl-分段控制器 显示多个分段按钮,每个按钮类似于独立按钮

    6.UITestField-文本输入框 用户点击输入框时键盘出现,点击完成时键盘消失

    7.UISwitch-开关 显示类似一个布尔类型的元素,当用户选择其中一个状态时候,改变元素的值

    8.UITableView-表视图 可以按照多种风格(例如plain sectioned group等风格)来显示数据

    9.UITestView-文本视图 在一个可拖动的视图内显示一个可编辑的多行的文本

    10.UIImageView-图片视图 显示一张单独图片或者一组图片显示的动画

    11.UIWebView-网页视图 可以显示网页并具有导航功能

    12.UIScrollView-滚动视图 提供一种机制显示可以比应用程序窗口显示更多的内容

    13.UIView-视图 是窗口上的一个矩形区域,用于显示UI对象与接收事件

    14.UISearchBar-搜索栏他显示一个可编辑的搜索栏,其中包括搜索图标

    15.UINavigation Bar-导航栏 用于显示一个导航栏

    16.UITab Bar-标签栏 在视图底部显示一定数目的标签,用户可以点击不同的标签

    展开全文
  • webapp界面UI框架Sencha Touch 是WEB APP的界面UI组件库,明显比jquery Mobile快.zip
  • 以下是几个精致的web UI框架   1.Aliceui Aliceui是支付宝的样式解决方案,是一套精选的基于 spm 生态圈的样式模块集合,是 Arale 的子集,也是一套模块化的样式命名和组织规范,是写 CSS 的更好方式。 gitHub...
  • TouchUI-WX是一套完全免费的微信小程序开发框架,包含丰富的UI控件用于官方组件的补充。与TouchUI开发方式很相似,也是通过VSCode编辑器+插件的方式开发,经过编译后输出小程序代码。与其他小程序框架最主要的区别...
  • 基于jquery开发的UI框架有哪些

    万次阅读 2017-09-20 17:00:53
    根据调查得知,现在市场中的UI框架差不多40个左右,不知大家都习惯性的用哪个框架,现在市场中有几款UI框架稍微的成熟一些,也是大家比较喜欢的一种UI框架,那应该是jQuery,有部分UI框架都是根据jQuery研发出来的...
  • 注:内容来自github 致力于android推广 包括 listview GridView 刷新动画 侧滑动画 抽屉动画 button动画 加载动画 ...List of Android UI/UX Libraries A curated list of awesome Android UI/UX libraries. ...
  • 以下是几个精致的web UI框架1.AliceuiAliceui是支付宝的样式解决方案,是一套精选的基于 spm 生态圈的样式模块集合,是 Arale 的子集,也是一套模块化的样式命名和组织规范,是写 CSS 的更好方式。 gitHub地址:...
  • 响应式WebUI框架整理

    千次阅读 2019-07-12 17:25:44
    移动框架 ,简单易用,轻量快捷,为移动端服务的前端框架。 依赖第三方类库,如 zeptojs 官网地址:http://frozenui.github.io/ 2 YDUI Touch YDUI Touch 专为移动端打造,在技术实现、交互设计上兼容主流移动设备,...
  • 14 款(移动端)UI 框架

    万次阅读 2018-08-18 16:26:56
    因业务需求,特调研了当前比较火的移动端UI框架,在此做一个汇总。 star数据是截止发文时间(2018-08-18),仅供参考。 一、【H5通用UI】 WeUI Star:19743 WeUI 是一套同微信原生视觉体验一致的基础样式库...
  • Hi,我们做了一款高质量的、免费的移动端UI框架。 经过将两年多开发和项目实践,我们终于把Touch UI开放出来了。这是一套基于vue.js打造的移动端UI框架,包含近百种组件,几乎囊括了开发移动应用的所有细节。 Touch ...
  • 如果你在找Web UI框架,不妨看看百度的NoahV框架:https://github.com/baidu/NoahV 主要包含如下特性: 1.上手简单,提供常用的各类组件,快速开始前端开发 2.导航栏、页面布局JSON配置,无需代码逻辑开发 3.API数据...
  • 推荐几个精致的web UI框架 1.Aliceui Aliceui是支付宝的样式解决方案,是一套精选的基于 spm 生态圈的样式模块集合,是 Arale 的子集,也是一套模块化的样式命名和组织规范,是写 CSS 的更好方式。 gitHub地址:...
  • 16个最佳Angular UI框架

    万次阅读 2020-08-15 11:06:30
    angular ui框架Angular is a full-fledged framework with its own tooling and best practice implementation designed on top. Angular是一个成熟的框架,其顶部设计了自己的工具和最佳实践实现。 It encourages ...
  • Android 开源UI框架汇总

    万次阅读 2019-06-12 10:23:46
    2、https://github.com/wasabeef/awesome-android-ui 目录UIUI卫星菜单节选器下拉刷新模糊效果HUD与Toast进度条UI其它 动画网络相关响应式编程地图数据库图像浏览及处理视频音频处理测试及调试动态更...
  • ipad ui框架Want to test drive iPad without heading over to an Apple store? Here’s a way you can experience some of the iPad UI straight from your browser! 是否想在不前往Apple商店的情况下试驾iPad? 您...
  • Touch 库时编写的所有 iOS 应用程序。 这里的每个文件夹都是一个完全不同的项目,要么完全由我自己创建,要么来自 Hacking with Swift 网站。 它明确说明了我从头到尾编写了哪个应用程序。 每个项目都试图从 SwiftUI...
  • 微信小程序UI框架

    千次阅读 2019-10-01 09:49:39
    微信小程序UI框架,快速解决微信小程序前端开发。 1.WeUI WXSS WeUI WXSS是腾讯官方UI组件库WeUI的小程序版,提供了跟微信界面风格一致的用户体验。 GitHub地址:https://github.com/Tencent/weui-wxss 2....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 20,368
精华内容 8,147
关键字:

touchui框架