首页 文章 精选 留言 我的

精选列表

搜索[天玑9500],共10000篇文章
优秀的个人博客,低调大师

第187天:js基础---常见的Bom对象

BOM(Browser Object Mode)浏览器对象模型,是Javascript的重要组成部分。它提供了一系列对象用于与浏览器窗口进行交互,这些对象通常统称为BOM。 一张图了解一下先 1、window 对象——BOM核心 window,顾名思义,窗口对象。它表示整个浏览器窗口,主要用来操作浏览器窗口。同时, window对象还是 ECMAScript 中的 Global 对象,因而所有全局变量和函数都是它的属性,且所有原生的构造函数及其他函数也都存在于它的命名空间下。 弹框类的方法。前面省略window alert('提示信息') confirm("确认信息") prompt("弹出输入框") open("url地址",“_black或_self”,“新窗口的大小”) close() 关闭当前的网页 定时器,清除定时器。 setTimeout(函数,时间) 只执行一次 clearTimeout(定时器名称) 清除定时器,用于停止执行setTimeout()方法的函数代码。 setInterval(函数,时间) 无限执行 clearInterval() 方法用于停止 setInterval() 方法执行的函数代码。 Cookies 用于存储 web 页面的用户信息。 JavaScript 可以使用document.cookie属性来创建 、读取、及删除 cookies。document.cookie 将以字符串的方式返回所有的 cookies,类型格式: cookie1=value; cookie2=value; cookie3=value; 2、document 对象 它是window对象的一个属性,可以用来处理页面文档 3、location 对象 对象用于获得当前页面的地址 (URL),并把浏览器重定向到新的页面。 window.location对象在编写时可不使用 window 这个前缀。 一些例子: location.herf = 'url地址' location.hostname 返回 web 主机的域名 location.pathname 返回当前页面的路径和文件名 location.port 返回 web 主机的端口 (80 或 443) location.portocol 返回页面使用的web协议。 http:或https: 4、navigator 对象 对象提供了与浏览器有关的信息。userAgent是最常用的属性,用来完成浏览器判断。 window.navigator对象在编写时可不使用 window 这个前缀。 5、screen 对象 主要用来获取用户的屏幕信息。 window.screen对象在编写时可以不使用 window 这个前缀 height: 获取整个屏幕的高。 width : 获取整个屏幕的宽。 availHeight: 整个屏幕的高减去系统部件的高(可用的屏幕宽度) availWidth : 整个屏幕的宽减去系统部件的宽(可用的屏幕高度) 6、history 对象 对象包含浏览器的历史。 window.history对象在编写时可不使用 window 这个前缀。 back() 返回上一页。 forward() 返回下一页。 go(“参数”) -1表示上一页,1表示下一页。

优秀的个人博客,低调大师

第166天:canvas绘制饼状图动画

canvas绘制饼状图动画 1、HTML 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> 6 <title>饼状图面向对象版本</title> 7 <style> 8 body { 9 padding: 0; 10 margin: 0; 11 background-color: #f0f0f0; 12 overflow: hidden; 13 } 14 </style> 15 <script src="bower_components/konva/konva.min.js"></script> 16 <script src="js/PieChart.js"></script> 17 </head> 18 <body> 19 <div id="container"> 20 </div> 21 22 <script> 23 //创建舞台 24 var stage = new Konva.Stage({ 25 container: 'container', 26 width: window.innerWidth,//全屏 27 height: window.innerHeight 28 }); 29 30 //创建层 31 var layer = new Konva.Layer(); 32 stage.add(layer); 33 34 //中心点坐标 35 var cenX = stage.width() / 2; 36 var cenY = stage.height() / 2; 37 38 var data = [ 39 { name: "前端", value: .25, color: '#e0e' }, 40 { name: "php", value: .2, color: 'orange'}, 41 { name: "UI", value: .3, color: 'blue' }, 42 { name: "C++", value: .05, color: 'green' }, 43 { name: "游戏", value: .1, color: 'purple'}, 44 { name: "Java", value: .1, color: 'red' } 45 ]; 46 47 var p = new PieChart({ 48 x: cenX, 49 y: cenY, 50 r: 100, 51 data: data 52 }); 53 54 p.addToGroupOrLayer( layer ); 55 layer.draw(); 56 57 p.playAnimate(); 58 59 </script> 60 </body> 61 </html> 2、PieChart.js 1 //英 [paɪ] 美 [paɪ] 2 function PieChart( option ) { 3 this._init( option ); 4 } 5 6 PieChart.prototype = { 7 _init: function( option ) { 8 this.x = option.x || 0; 9 this.y = option.y || 0; 10 this.r = option.r || 0; 11 this.data = option.data || []; 12 13 //饼状图所有的 物件的组 14 this.group = new Konva.Group({ 15 x: this.x, 16 y: this.y 17 }); 18 19 //饼状图:所有的 扇形的组 20 this.wedgeGroup = new Konva.Group({ 21 x: 0, 22 y: 0 23 }); 24 25 //饼状图: 所有的文字的组 26 this.textGroup = new Konva.Group({ 27 x: 0, 28 y: 0 29 }); 30 31 this.group.add( this.wedgeGroup ); 32 this.group.add( this.textGroup ); 33 34 var self = this; 35 var tempAngel = -90;//从-90开始绘制 36 37 this.data.forEach(function(item, index ) { 38 //把每条数据创建成一个扇形 39 var angle = 360 * item.value;//当前扇形的角度 40 //创建一个扇形 41 var wedge = new Konva.Wedge({ 42 x: 0, //扇形圆心坐标 43 y: 0, 44 angle: angle , //扇形的角度 45 radius: self.r, //扇形的半径 46 fill: item.color, //扇形的填充颜色 47 rotation: tempAngel //扇形的旋转角度 48 }); 49 50 self.wedgeGroup.add( wedge ); 51 52 //绘制文本的 角度 53 var textAngle = tempAngel + 1/2 * angle; 54 55 //绘制的 百分比的文本 56 var text = new Konva.Text({ 57 x: (self.r+20) * Math.cos(Math.PI/ 180 * textAngle ), 58 y: (self.r+20) * Math.sin(Math.PI/ 180 * textAngle ), 59 text: item.value*100 +'%', 60 fill: item.color 61 }); 62 63 //根据角度判断设置文字的 位置 64 if( textAngle > 90 && textAngle < 270 ) { 65 //让文本向左边 移动文字宽度的位置。 66 text.x( text.x() - text.getWidth() ); 67 } 68 69 self.textGroup.add( text ); 70 71 tempAngel += angle; 72 }); 73 //绘制所有的楔形 74 75 //绘制文字 76 77 //绘制圆环的线 78 var cir = new Konva.Circle({ 79 x: 0, 80 y: 0, 81 radius: this.r+10, 82 stroke: '#ccc', 83 strokeWidth: 2 84 }); 85 86 this.group.add( cir ); 87 88 this._animateIndex = 0; 89 }, 90 playAnimate: function() { 91 92 var self = this; 93 //根据索引显示动画 94 //把所有扇区 角度设置为0 95 if( this._animateIndex == 0 ) { 96 //拿到所有的 扇形 97 this.wedgeGroup.getChildren().each(function(item, index ){ 98 item.angle(0); 99 }); 100 } 101 102 //展示当前索引对应的动画 103 var item = this.wedgeGroup.getChildren()[this._animateIndex]; 104 item.to({ 105 angle: this.data[this._animateIndex].value * 360, 106 duration: 2 * this.data[this._animateIndex].value, 107 onFinish: function() { 108 self._animateIndex ++; 109 if( self._animateIndex >= self.data.length) { 110 111 self._animateIndex = 0;//让他的索引再回到0 112 113 //************************重点*********************** 114 return;// 会把整个递归执行完成。 115 } 116 //继续执行当前方法,播放下一个动画 117 self.playAnimate(); 118 } 119 }); 120 }, 121 //把饼状图添加到层里面的方法 122 addToGroupOrLayer: function( arg ) { 123 arg.add( this.group ); 124 } 125 } 运行效果:

优秀的个人博客,低调大师

Objective-C 学习第九天

一、 框架: 系统或者第三方事先写好的写很牛X功能的类,把这些类交给我们使用,这些类的集合就叫框架. Foundation框架: 是一个包,有很多类和函数,定义了一些数据类型.这个框架中的类都是一些最基础的类.其他的框架都是基于Foundation框架的. UIKit. AVFoundation. ...... NSString 1). NSString是一个数据类型,保存OC字符串. NSString的本质是一个类. 最标准的创建NSString对象的方法:NSString *str1 = [NSString new]; NSString *str2 = [[NSString alloc] init]; NSString *str3 = [NSString string]; 使用这种方式创建的字符串是一个空的字符. @"" NSString 对象是用来存储字符串的. 2). NSString是OC中最常用的一个类,所以OC提供了一种更为快速的创建字符串对象的方式. 使用前缀@ @"jack": 本质是一个NSString对象,存储的是"jack"这个字符串 NSString *str1 = @"rose"; a. @"rose"本质是一个NSString对象,存储的是字符串"rose" b. 将这个字符串对象的地址返回赋值给str1指针 格式控制符%p: 打印指针变量的值,打印地址 格式控制符%@: 打印指针指向的对象 NSString对象的恒定性 1). 当我们使用简要的创建字符串对象的时候,也就是使用OC字符串常量来初始化字符串指针的时候,这个字符串对象是存储在常量区的. NSString *str = @"jack"; 当我们调用NSString的类方法来创建对象的时候, 创建的对象是存储在堆区的. NSString *str = [NSString stringWithFormat:@"jack"]; NSString *str = [NSString new]; 2). 恒定性 当在内存中创建一个字符串对象后,这个字符串对象的内容就无法更改. 当我们重新为字符串指针初始化值的时候,并不是修改原来的字符串对象,而是重新的创建一个字符串对象,将这个字符串对象的地址重新赋值给字符串指针变量. 3). 当系统准备要在内存中创建字符串对象的时候,会先检查内存中是否有相同内容的字符串对象,如果有直接指向,如果没有才会重新创建. 4). 存储在常量区的数据不会被回收,所以存储在常量区的字符串也不会被回收. NSString类中使用频率最高的方法 1). 使用拼接的方式创建一个NSString对象 + (instancetype)stringWithFormat:(NSString *)format,... int age = 10; NSString *str = @"jack"; NSString *newStr = [NSString stringWithFormat:@"大家好,我叫%@,今年%d岁",str,age]; 2). 得到字符串的长度 @property (readonly) NSUInteger length; NSString *str = @"jack"; NSUInteger len = str.length; 3). 得到字符串当中指定下标的字符 - (unichar)characterAtIndex:(NSUInteger)index; 返回值是unichar类型的,要打印的话使用%C NSString *str = @"jack"; unichar ch = [str characterAtIndex:2]; NSLog(@"ch = %C", ch); 4). 判断两个字符串的内容是否相等 a. 不能使用==来判断OC字符串的内容是否相同 b. == 运算符的作用: 比较左右两边的数据是否相同. c. 所以如果我们要比较两个OC字符串的内容是否相同,不能使用==去比较.因为==比较的是字符串指针变量的值,而我们比较的是字符串的内容 - (BOOL)isEqualToString:(NSString *) aString; 可以比较字符串的内容 NSString *str1 = @"jack"; NSString *str2 = @"jack"; [str1 isEqualToString:str2]; 5). 将C语言的字符串转换为OC字符串 + (instancetype)stringWithUTF8String:(NSString *)string; char *str = "jack"; NSString *str1 = [NSString stringWithUTF8String: str]; 6). 将OC字符串转换为C语言字符串 @property (nullable, readonly) __strong const char *UTF8String; NSString *str = @"jack"; const char *str1 = str.UTF*String; 字符串读写 1). 将一个字符串写入到磁盘上的某一个文件 (BOOL) writeToFile:(NSString *)path atomically:(BOOL)userAuxiliaryFile encoding:(NSStringEncoding)enc error:(NSError **)error; 参数1: 将字符串内容写入到哪一个文件之中,写上文件的路径. 参数2: YES,先将内容写入到一个临时文件,如果成功过再搬到指定的目录; NO,直接将内容写入到指定的文件,不安全,效率高 参数3: 指定写入的时候的编码.一般情况下使用UTF8. NSUTF8StringEncoding 参数4: 二级指针,要传递一个NSError指针的地址.如果写入成功,这个指针的值就是nil,如果失败,这个指针就会指向一个错误对象,这个对象描述了发生错误的信息. err.localizedDescription中存储发生错误的信息 返回值是BOOL类型,代表是否写入成功. NSString *str = @"jack"; NSError *err; [str writeToFile:@"/Users/Itcast/Desktop/abc.txt", atomically:NO encoding:NSUTF8StringEncoding error:&err]; 2). 从磁盘文件读取内容 (instancetype) stringWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error; 参数1:文件的路径 参数2:使用的编码NSUTF8StringEncoding 参数3:错误对象 NSError *err; [NSString stringWithContentsOfFile:@"/Users/Itcast/Desktop/abc.txt" encoding:NSUTF8StringEncoding error:&err]; 使用URL来读写字符串数据. 1). 优势: 皆可以读取本地磁盘文件,还可以读写网页文件、ftp服务器文件 2). 不同类型的URL地址的写法 a. 本地磁盘文件: file://+路径---->file:///Users/Itcast/Desktop/abc.txt b. 网页地址: http://www.baidu.com/ c. ftp服务器文件的地址: ftp://server.itcast.cn/1.txt 将不同类型的地址封装在NSURL对象中 a. 从指定资源路径读取文本内容 (nullable instancetype)stringWithContentsOfURL:(NSURL *)url encoding:(NSStringEncoding)enc error:(NSError **)error; b. 将字符串的内容写入到资源路径中 (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)useAuxiliaryFile encoding:(NSStringEncoding)enc error:(NSError **)error; c. 如果要向网页或者ftp写内容要有权限. NSURL *u1 = [NSURL URLWithString:@"http://www.itcast.cn"]; NSString *str = [NSString stringWithContentsOfURL:u1 encoding:NSUTF8StringEncoding error:nil]; NSURL *u1 = [NSURL URLWithString:@"file:///Users/Itcast/Desktop/abc.txt"]; NSString *str = @"hehehe"; BOOL res = [str writeToURL:u1 atomically:NO encoding:NSUTF8StringEncoding error:nil]; 字符串比较 1). 不忽略大小写 (NSComparisonResult)compare:(NSString *)string; NSString *str1 = @"hehehe"; NSString *str2 = @"jack"; NSComparisonResult res = [str compare:str2]; swith(res){ case NSOrderedAscending://str1小 break; case NSOrderedSame://一样大 break; case NSOrderedDescending://str2小 break; default: break; } 2). 忽略大小写 NSString *str1 = @"hehehe"; NSString *str2 = @"JACK"; NSComparisonResult res = [str compare:str2 option:NSCaseInsensitiveSearch]; swith(res){ case NSOrderedAscending://str1小 break; case NSOrderedSame://一样大 break; case NSOrderedDescending://str2小 break; default: break; } 3). 比较字符串中的数字 NSString *str1 = @"hehehe001"; NSString *str2 = @"hehehe002"; NSComparisonResult res = [str compare:str2 option:NSNumericSearch];// 只能比格式固定的 swith(res){ case NSOrderedAscending://str1小 break; case NSOrderedSame://一样大 break; case NSOrderedDescending://str2小 break; default: break; } 4). 判断是否以某个字符串开头 NSString *str = @"http://www.baidu.com/"; BOOL res = [str hasPrefix:@"http://"]; 5). 判断是否以某个字符串结束 NSString *str = @"/Users/Apple/Music/我们不一样.mp3"; BOOL res = [str hasSuffix:@".mp3"]; 在主串中搜索子串 1). 从前往后 - (NSRange) rangeOfString:(NSString *)searchString; 返回值NSRange类型的结构体变量typedef struct _NSRange{ NSUInteger location;//代表子串在主串出现的下标 NSUInteger length;//代表子串在主串中匹配的长度 } NSRange;NSString *str = @"i love itcast!"; // 在str字符串中搜索@"love"出现的范围 NSRange range = [str rangeOfString:@"love"]; if(range.location==NSNotFound){ // 没有找到 } else { // 找到了 } 或者 if(range.length==0){ // 没有找到 } else { // 找到了 } 2). 从后往前NSString *str = @"i love itcast love!"; // 在str字符串中搜索@"love"出现的范围 NSRange range = [str rangeOfString:@"love" options: NSBackwardsSearch] NSRange结构体 1). 是Foundation框架中定义的一个结构体typedef struct _NSRange{ NSUInteger location;//代表子串在主串出现的下标 NSUInteger length;//代表子串在主串中匹配的长度 } NSRange; NSRange range; // 这个结构体变量一般情况下用来表示一段范围,特别用在子串在主串中的范围. 2). 声明并初始化结构体变量的方式 a. 最原始的方式NSRange range; range.location = 3; range.length = 4; b. 第二种方式: NSRange range = {3, 7}; c. 第三种方式: NSRange range = {.location = 3, .length=7}; d. Foundation框架中定义了一个函数,这个函数可以快速的创建一个NSRange结构体. NSMakeRange(loc, len);返回一个指定属性的NSRange结构体变量 NSRange range = NSMakeRange(3,7); 5. 可以将NSRange结构体变量转换为NSString. NSRange range = NSMakeRange(3,7); NSString *str = NSStringFromRange(range); 字符串截取 1). 取到字符串中的一部分 (NSString *)substringFromIndex(NSUIntege)from; 从指定的下标处一直截取到最后 NSString *str = @"背景连锁酒店分开计算懒死了"; NSString *newStr = [str substringFromIndex:3]; 2). 从第0个开始截取指定的个数 (NSString *)substringToIndex(NSUIntege)to; 3). 从指定位置截取指定长度的字符串 (NSString *)substringWithRange(NSRange)range; 字符串替换 1). (NSString *)stringByReplacingOccurrencesOfString:(NSString *)target withString:(NSString *)replacement; 将字符串中第一个参数替换为第二个参数,原来的指针指向的内容是不会变的,新串是以方法的返回值返回的. NSString *str = @"背景连锁酒店分开计算懒死了"; NSString *newStr = [str stringByReplacingOccurrencesOfString:@"背景" withString:@"广州"]; 字符串数据转换为其他类型 @property (readonly)double doubleValue; @property (readonly)float floatValue; @property (readonly)int intValue; @property (readonly)NSUINteger integerValue; @property (readonly)long long longLongValue; @property (readonly)BOOL boolValue; 转换注意: 其他 1). 去掉字符串前后的空格 NSString *str = @" Jack "; [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 2). 将字符串转换为大写或者小写 NSString *str = @" Jack "; [str uppercaseString]; [str lowercaseString]; 二、 NSMutableString 1). 是Foundation框架中的一个类,从NSString继承,是用来存储字符串数据的. 2). NSMutableString在父类NSString的基础之上做扩展,存储在NSMutableString对象中的数据具有可变性,不会新创建对象. NSMutableString的用法 1). 创建对象 NSMutableString *str = [NSMutableString string]; [str appendString:@"jack"]; 2). 往可变字符串对象中追加字符串 (void)appendString:(NSString *)aString;// 直接追加内容 (void)appendFormat:(NSString *)format...;// 以拼接的方式往可变字符串对象中追加内容 3). 创建NSMutableString对象时,不能使用如下方式: NSMutableString *str = @"jack"; 4). NSMutableString做大批量的字符串拼接,速度快,因为NSMutableString只有一个,每次修改的时候直接修改对象的内容 使用建议 1). 平时使用,还是使用NSString,因为效率高 2). 大批量的数据拼接的时候使用NSMutableString 10次以上使用NSMutableString. NSArray 1). 是Foundation框架中的一个类,用来存储多个数据,具备数组功能,所以NSArray是OC中的数组 2). 特点: a. 只能存储OC对象 b. 长度固定,一旦NSArray数组创建完毕之后,元素的长度固定,无法新增、删除元素. c. 每一个元素都是紧密相连的,每一个元素仍然有自己的下标. d. 元素的类型是id类型的 3). NSArray数组的创建 1). 因为是一个类,所以就是创建NSArray对象.NSArray *arr1 = [NSArray new]; NSArray *arr2 = [[NSArray alloc] init]; NSArray *arr3 = [NSArray array]; 这样创建出来的NSArray数组的元素的个数是0个,因为长度固定,所以没有任何意义. 2). 创建数组的同时指定一个数组的元素,仍然没有意义NSArray *arr1 = [NSArray arrayWithObject:@"jack"]; 3). 最常用创建NSArray数组的方式. + (instancetype)arrayWithObjects:(ObjectType)firstObj,...NSArray *arr1 = [NSArray arrayWithObjects:@"jack",@"rose",@"lili",nil]; 4). 创建NSArray简要方式 NSArray *arr = @[@"jack", @"rose", @"lili"]; 5). 使用注意 a. 只能存储OC对象,不能存储非OC对象 b. 将元素写完之后,最后要写一个nil,表示元素到此结束了 NSArray数组的编译 1). for循环 原理: 将下标挨个遍历出来 NSArray *arr = @[@"jack",@"rose"]; for(int i=0;i<arr.count;i++){ //NSLog(@"%@",arr[i]); NSLog(@"%@", [arr objectAtIndex:i]); } 2). 使用增强for循环 a. 语法格式: for(元素类型 变量名 in 数组名){ 直接通过变量名拿到数组中的每一个元素 }NSArray *arr = @[@"jack",@"rose"]; for(NSString *str in arr){ NSLog(@"%@", str); } b. 声明在for()中的变量叫做迭代变量 c. 执行变量 将数组中的第一个元素的值赋值给迭代变量,执行循环体 ... 结束循环 d. 语法总结 -> 迭代变量的类型和数组中的元素的类型保持一致 -> 迭代变量的名称可以任意取 -> in是固定的 -> 遍历那个数组就将数组写在in后面 -> 循环体里面,迭代变量的值就是元素的值 当NSArray数组中存储的数据类型不一致的时候,迭代变量的类型建议使用id类型 使用block遍历 (void) enumerateObjectsUsingBlock:(void (^)OjbectType obj, NSUInteger idx, BOOL *stop))block; 这是一个方法,这个方法作用来遍历数组中的每一个元素 NSArray *arr = @[@"jack",@"rose"]; // 将arr数组中的每一个元素遍历出来 [arr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop){ NSLog(@"%@", obj); // 如果想停止循环,就将*stop设置为YES }]; NSArray和NSString 1). 将数组中元素连接起来组成一个新的字符串 (NSString *)componentsJoinedByString:(NSString *)separator 参数: 连接符 NSArray *arr = @[@"jack", @"rose", @"lili"]; NSString *str = [arr componentsJoinedByString:@"#"]; 2). 将字符串按指定分隔符分割 NSString *str = @"北京,昌平区,传智"; // 使用字符串进行分割 NSArray *arr = [str componentsSeparatedByString:","]; NSMutableArray是NSArray的子类 1). NSMutableArray仍然是一个数组,就被NSArray的特点,只能存储OC对象,每一个元素是紧密相连的。 2). NSMutableArray相对于父类做的扩展,NSMutableArray数组的元素可以动态的新增和删除,其他用法与NSArray一致。 所以: NSArray数组一旦创建,其元素的个数就固定,无法新增与删除 NSMutableArray数组,可以新增删除 3). NSMutableArray数组的创建NSMutableArray *arr1 = [NSMutableArray new]; NSMutableArray *arr2 = [[NSMutableArray alloc] init]; NSMutableArray *arr3 = [NSMutableArray array]; 这样创建出来的数组对象,数组的元素是0,但是有意义,可以动态的新增,删除. 最容易犯错:这样写不可以 NSMutableArray *arr1 = @[@"jack", @"rose", @"lili"]; 新增与删除 新增 a. // 将传入的参数作为数组的元素添加进去 - (void)addObject:(ObjectType)anObject; // 将另外一个数组中的每一个元素添加到可变数组中 - (void)addObjectsFromArray:(NSArray<ObjectType> *)otherArray;NSMutableArray *arr1 = [NSMutableArray new]; [arr1 addObject:@"jack"]; NSArray *arr = @[@"jack", @"rose", @"lili"]; [arr1 addObjectsFromArray:arr]; b. 在可变数组中指定的数组中插入一个元素 - (void)insertObject:(ObjectType)anObject atIndex:(NSUInteger)index;[arr insertObject:@"lilei", anIndex:1]; 删除 a. 删除 // 删除指定下标的元素 - (void) removeObjectAtIndex:(NSUInteger)index;NSMutableArray *arr1 = [NSMutableArray new]; [arr1 addObject:@"jack"]; NSArray *arr = @[@"jack", @"rose", @"lili"]; [arr1 addObjectsFromArray:arr]; [arr removeObjectAtIndex:1]; b. 删除特定的元素 - (void)removeObject:(ObjectType)anObject;NSMutableArray *arr1 = [NSMutableArray new]; [arr1 addObject:@"jack"]; NSArray *arr = @[@"jack", @"rose", @"lili"]; [arr1 addObjectsFromArray:arr]; [arr removeObject:@"lili"]; c. 删除指定范围内的元素 - (void)removeObject:(ObjectType)anObject inRange:(NSRange)range;NSMutableArray *arr1 = [NSMutableArray new]; [arr1 addObject:@"jack"]; NSArray *arr = @[@"jack", @"rose", @"lili"]; [arr1 addObjectsFromArray:arr]; [arr removeObject:@"lili" inRange:NSMakeRange(0, 3)]; d. 删除最后一个元素 - (void)removeLastObject;NSMutableArray *arr1 = [NSMutableArray new]; [arr1 addObject:@"jack"]; NSArray *arr = @[@"jack", @"rose", @"lili"]; [arr1 addObjectsFromArray:arr]; [arr removeLastObject]; e. 删除所有 - (void)removeAllObjects;NSMutableArray *arr1 = [NSMutableArray new]; [arr1 addObject:@"jack"]; NSArray *arr = @[@"jack", @"rose", @"lili"]; [arr1 addObjectsFromArray:arr]; [arr removeAllObjects]; NSNumber 1). 作用: 包装基本数据类型 NSNumber *number = [NSNumber numberWithInt:10]; 等价于 NSNumber *number = @10;

优秀的个人博客,低调大师

Objective-C 学习第一天

一、基础语法 OC相对于C a. 在C的基础上新增了面向对象的语言 b. 将C的复杂、繁琐的语法封装的更为简单 c. OC完全兼容C语言 OC程序的源文件后缀名是.m m代表message 代表OC当中最重要的一个机制 消息机制 C程序的源文件的后缀名.c main函数仍然是OC程序的入口和出口 int类型的返回值代表程序的结束状态 main函数的参数: 仍然可以接收用户在运行程序的时候传递数据给程序, 参数也可以不要 #import指令 1). 以#号开头,是1个预处理指令 2). 作用: 是#include指令的增强版。将文件的内容在预编译的时候拷贝在写指令的地方 3). 增强: 同一个文件无论#import多少次,只会包含一次 如果#include指令要实现这个效果,就必须要配合条件编译指令来实现 而#import指令只需要直接包含就可以 4). 简要原理: #import指令在包含文件的时候,底层会先判断这个文件是否被包含, 如果被包含就会被略过,否则才会包含。 框架 1). 是一个功能集 苹果或者第三方事先将一些程序在开发程序时经常要用到的功能事先写好 把这些功能封装在一个类或函数中,这些函数和类的集合就叫做框架。 有点像C语言的函数库 2). Foundation框架 Foundation: 基础 基本. 这个框架当中提供了一些最基本的功能 输入输出, 数据类型 Foundation.h 这个文件当中包含了Foundation包含了其他所有的头文件 只要包含Foundation.h就想当于包含了框架当中所有的头文件。 @autoreleasepool{} 自动释放池 代码写在这个大括号中 NSLog函数 1). 作用: 是printf函数的增强版, 向控制台输出信息 2). 语法: NSLog(@"格式控制字符串", 变量列表); 最简单语法: NSLog(@"要输出的信息"); 3). 增强: a. 输出一些调试相关的信息 2020-07-01 12:40:05.175 Day01-OC基础用法 [792:213878] Hello,World 执行代码的时间 程序名称 792是进程编号,213878是线程编号 输出信息 可用于测试程序执行时间 b. 会自动换行 c. OC新增的数据类型的变量的值只能使用NSLog函数输出 4). 用法和printf函数差不多 5). 使用注意: a. NSLog函数的第一个参数前面必须要加1个@符号 b. 如果在字符串的末尾加1个'\n'代表换行, 那么函数的自动换行会失效 字符串 1). C语言的字符串的存储方式 a. 使用字符数组存储 b. 使用字符指针 2). OC当中更为好用的存储字符串的类型. NSString NSString类型的指针变量专门用来存储OC字符串的地址 3). OC的字符串必须要使用1个前缀@符号 "jack" 是一个C语言的字符串 @"jack" 是一个OC的字符串常量 NSString 类型的指针变量只能存储OC字符串的地址 4). 使用 NSString *str = @"jack"; 5). 总结 a. 在OC中专门设计了1个NSString类型来存储字符串 b. 字符串分为C字符串和OC字符串 c. 区分: 是否有@前缀 6). 注意 a. NSLog函数的第1个参数是OC字符串 b. 输出 NSString *str = @"jack"; NSLog(str); c. 如果要使用NSLog函数中的占位符输出字符串的值, 应使用"%@" NS前缀 NextStep --> Cocoa --> Foundation框架之中 @符号 1). 将C字符串转换为OC字符串 "jack" ->> @"jack" 2). OC当中的绝大部分关键字是以@符号开头 注释: 和C语言的注释一模一样, 分为单行注释和多行注释 函数的定义和调用: 与C语言的函数调用是一样的 void test();// 声明 void test(){// 实现 } 二、OC与C的对比 OC程序的编译、链接、执行 1). 在.m文件中写上符合OC程序的源代码 2). 使用编译器将源代码编译为目标文件 cc -c xx.m a. 预处理 b. 检查语法 c. 编译 3). 链接 cc xx.o 如果程序中使用到了框架中的函数或者类,那么在链接时就必须要告诉编译器去哪里找Foundation框架 cc xx.o -framework Foundation 4). 链接成功后, 就会生成1个a.out可执行文件, 使用"./a.out"执行 OC程序和C程序各个阶段的后缀名对比 源文件 目标文件 可执行文件 C .m .o .out OC .m .o .out OC中的数据类型 1). OC中支持C语言中的所有的数据类型 a. 基本数据类型 int double float char b. 构造类型 数组 结构体 枚举 c. 指针类型 d. 空类型 e. typedef自定义类型 2). BOOL 类型 BOOL b1; a. 可以存储YES或者NO中的任意1个数据(必须是大写). b. 一般情况下BOOL类型的变量用来存储条件表达式的结果. c. BOOL的本质 typedef signed char BOOL; 实际上BOOL类型的变量是1个有符号的char变量 #define YES ((BOOL)1) #define NO ((BOOL)0) 4). Boolean a. Boolean类型的变量可以存储true或者false b. 一般情况下BOOL类型的变量用来存储条件表达式的结果. c. 本质 typedef unsigned char Boolean; #define true 1 #define false 0 5). class 类型. 类 6). id 类型 万能指针. 7). nil 与 null 差不多 8). SEL 方法选择器 9). block 代码段 三、 面向过程与面向对象 OC是在C的基础之上 1). 将C复杂的语法封装的更为简单 2). 在C语言的基础之上新增了一小部分的面向对象的语法 实现需求之一: 1). 要把大象放进冰箱应该怎么办? a. 打开冰箱门 b. 把冰箱放进去 c. 把冰箱门关上 2). 买电脑的需求的实现方式: a. 根据自己的需求和预算确定买的电脑型号 10000 iOS开发 b. 在网上查资料 确定型号 rMMP 9288 c. 去到数码产品中心 d. 找到Apple专卖店 e. 砍价 成交 f. 回家 3). 更好的方式: a. 自己做 强调的是过程, 步骤. 我是主角 b. 别人做 强调的比人. 我是指挥者 面向过程: 如果解决1件事情的时候,每一件事情都是我们亲自去一步步实现 面向对象: 如果解决1件事情的时候,自己不去做,而是专门找一个人去做 代码世界的面向过程与面向对象. 1). 面向过程 在遇到1个需求的时候,每个步骤都是自己亲自写代码完成 2). 面向对象 在遇到1个需求的时候,而是找一个专门做这件事的人 面向对象和面向过程优缺点 C语言是一门面向过程的语言. 有功能的概念, 但是没有人的概念. OC语言是一门面向对象的语言. 面向过程解决问题的缺点: 后期维护和修改不方便. 面向对象解决问题的优点: 后期维护和修改方便. 如何使用面向对象设计程序 当遇到一个需求的时候,不要亲自去实现. 1). 先看看有没有现成的框架做这件事 2). 如果没有,就自己实现 对象: 现实生活中的一个具体存在, 看得见、摸得着,拿过来可以直接使用 类: 是对一群具有相同特征或者行为的事物的统称.抽象的,不能直接使用. 类和对象之间的关系 四、 类与对象 请问先有类还是先有对象 1). 现实角度 一定是先有对象再有类 2). 代码角度 一定是先有类再有对象 类的作用: 描述一群具有相同特征和行为的事物 如何定义类 1). 类的三要素: 类名/特征/行为 2). 定义类的语法 a. 位置. 直接写在源文件中 b. 类的定义分为两部分 --> 类的声明 @interface 类名 : NSObject { 这类事物具有的共同特征, 将他们定义为变量 } 功能就是方法,将声明写在这里 @end --> 类的实现 @implementation 类名 将方法的实现写在这里 @end 3). 注意 a. 类必须要有声明和实现 b. 类名用你描述事物的名称来命名. 类名的每个单词单词首字母必须要大写. c. 用来表示这类事物共同特征的变量必须要定义在@interface中的大括号中 d. 定义在大括号用来表示类事物共同特征的变量叫属性、成员变量、实例变量、字段... e. 属性名必须要以下划线开头 示例: // 声明 @interface Person : NSObject { NSString *_name; int _age; float _height; } @end // 实现 @implementation Person @end 类是无法直接使用的. 如果非要使用这个类,必须要先找到这个类的具体存在 1). 如何创建一个类的对象 语法: 类名 *对象名 = [类名 new]; Person *p1 = [Person new];//创建Person对象 p1对象的特点: ->> 可以直接使用 ->> 类定定义的东西, 对象都有 如何使用对象 1). 如何访问对象的属性: a. 默认情况下,对象的属型是不允许被外界直接访问的 如果允许对象的属性可以被外界访问,声明属性时加@public关键字 b. 访问对象的属性的方式 对象名->属性名 = 值; 对象名->属性名; (对象名).属性名 = 值; (对象名).属性名 示例: // 声明 @interface Person : NSObject { @public NSString *_name; int _age; float _height; } @end // 实现 @implementation Person @end // 使用-- 平时建议使用这种形式 Person *p1 = [Person new]; p1->_name = @"jack"; p1->_age = 19; p1->_height = 178.5f; NSLog(@"p1对象的_name属性的值是%@", p1->_name); (*p1)._name = @"jack"; (*p1)._age = 19; (*p1)._height = 178.5f; 方法的声明和调用 // 声明 @interface Person : NSObject { 属性: 类的特征 } 方法: 类的功能 @end // 实现 @implementation Person 方法实现 @end 无参数方法声明 1). 声明 a. 位置: 在@interface的大括号的外面 b. 语法: - (返回值类型) 方法名称; - (void)run; 表示声明了1个无返回值的并且无参数的方法, 名字叫run 2). 实现 a. 位置: 在@implementation之中实现 b. 语法: 将方法声明拷贝到@implementation中,去掉分号追加大括号 3). 调用 a. 方法是无法直接调用的, 必须要创建对象 b. 调用对象的方法. [对象名 方法名]; 示例: // 声明 @interface Person : NSObject{ NSString *_name; int _age; } - (void)run; @end // 实现 @implementation Person - (void)run{ NSLog(@"Hello,World!"); } @end // 调用 Person *p1 = [Person new]; [p1 run]; 带1个参数的方法 1). 声明 a. 位置: 在@interface的大括号的外面 b. 语法: - (返回值类型) 方法名称:(参数的类型)形参名称; - (void)eat:(NSString *)foodName; 表示声明了1个无返回值的含有一个参数的方法, 名字叫"eat:",有一个参数类型是NSString *类型,参数的名字叫foodName 2). 实现 a. 位置: 在@implementation之中实现 b. 语法: 将方法声明拷贝到@implementation中,去掉分号追加大括号 3). 调用 a. 方法是无法直接调用的, 必须要创建对象 b. 调用对象的方法. [对象名 方法名:实参]; 示例: // 声明 @interface Person : NSObject{ NSString *_name; int _age; } - (void)run; - (void)eat:(NSString *)foodName; @end // 实现 @implementation Person - (void)run{ NSLog(@"Hello,World!"); } - (void)eat:(NSString *)foodName{ NSLog(@"给我的%@真好吃",foodName); } @end // 调用 Person *p1 = [Person new]; [p1 run]; [p1 eat:@"红烧排骨"]; 方法头中的数据类型都要用1个小括号括起来. (返回值类型)方法名称:(参数类型)参数名称; 带多个参数的方法 1). 声明 a. 位置: 在@interface的大括号的外面 b. 语法: - (返回值类型) 方法名称:(参数的类型)参数名称1 :(参数类型)参数名称2.。。; - (int)sum:(int)num1 :(int)num2; 表示声明了1个返回值为int的含有两个参数的方法, 名字叫"sum: :",有两个参数 参数类型都是int类型, 参数名称叫做num1,num2 2). 实现 a. 位置: 在@implementation之中实现 b. 语法: 将方法声明拷贝到@implementation中,去掉分号追加大括号 3). 调用 a. 方法是无法直接调用的, 必须要创建对象 b. 调用对象的方法. [对象名 方法名:实参1 :实参2]; 示例: // 声明 @interface Person : NSObject{ NSString *_name; int _age; } - (void)run; - (void)eat:(NSString *)foodName; - (int)sum:(int)num1 :(int)num2; @end // 实现 @implementation Person - (void)run{ NSLog(@"Hello,World!"); } - (void)eat:(NSString *)foodName{ NSLog(@"给我的%@真好吃",foodName); } - (int)sum:(int)num1 :(int)num2{ int num3 = num1 + num2; return num3; } @end // 调用 Person *p1 = [Person new]; [p1 run]; [p1 eat:@"红烧排骨"]; int sum = [p1 sum:10 :20]; NSLog(@"sum = %d",sum); 带参数的方法声明的规范:--提高阅读性. 1). 如果方法只有1个参数: 要去最好这个方法的名字叫做xxxWith: - (void)eatWithFood:(NSString *)foodName; 2). 如果方法有多个参数: 方法名With: (参数类型)参数名称1 and:(参数类型)参数名称2; - (int)sum:(int)num1 and:(int)num2; 3). 更详细的写法 方法名With参数1:(参数类型)参数名称1 and参数2:(参数类型)参数名称2... 其他使用注意 1). 快捷 a. 快速实现方法: - -> 空格 方法名 b. 快速创建对象: 对象 new -> 右括号(自动补齐)

优秀的个人博客,低调大师

Objective-C 学习第十天

一、NSDictionary NSArray和NSMutableArray数组 存储数据特点:每个元素紧密相连,并且每个元素中都是直接存储的值. 缺点:数组元素下标不固定,都有可能发生变化,无法通过下标来唯一确定数组中的元素. NSDictionary(Key-Value存储)与NSMutableDictionary 1). 它们是数组,以键值对的形式存储数据,通过别名来找数据,类似于java中的Map集合. 2). 存储数据原理 a. 以键值对的形式存储数据 b. 字典数组一旦创建,其中的元素就无法动态的新增和删除 c. 键: 只能是遵守了NSCoping协议的对象,而NSString遵守了这个协议 值:只能是OC对象 3). 创建字典数组 NSDictionary *dict1 = [NSDictionary new]; NSDictionary *dict2 = [[NSDictionary alloc] init]; NSDictionary *dict3 = [NSDictionary dictionary]; 这种方式创建出来的字典数组没有任何元素,所以没有意义. 4). 一般创建方式 + (instancetype)dictionaryWithObjectsAndKeys:(id)firstObject... NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"jack", @"name",nil]; 将字典数组的值键依次写在后面初始化. 5). 简要创建方式 @{键1:值1,键2:值2...} NSDictionary *dict = @{@"name":@"rose",@"age":@"18"}; 6). 使用%@可以打印这个数组中所有的元素. 如果字典中没有键名,则返回nil. 1). 别名法 对象名[键名];NSDictionary *dict = @{@"name":@"rose",@"age":@"18"}; dict[@"name"]; 2). 方法 - (id)objectForKey:(id)aKey;NSDictionary *dict = @{@"name":@"rose",@"age":@"18"}; [dict objectForKey:@"name"]; 3). 取出键值对的个数 @property (readonly)NSUInteger count;NSDictionary *dict = @{@"name":@"rose",@"age":@"18"}; [dict count]; 遍历NSDictionary 1). 字典数组中的数据无法使用下标取 2). 使用for in 循环,遍历出来的是所有数组的键. NSDictionary *dict = @{@"name":@"rose",@"age":@"18"}; for(NSString *key : dict){ NSLog(@"%@ = %@", key, dict[key]); } 3). 使用block遍历 [dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop)]{ NSLog(@"%@ = %@", key, obj); } 4). 数据存储原理 a. 当往字典数组中存储一个键值对的时候,这个键值对应该存储在下标为几的元素中 ->> 并不是按照顺序挨个的存储的 ->> 存键值对的时候,会根据键值数组的长度做一个哈希算法,算出一个下标,将这个键值存储在该下标处 b. 取值 也是根据键做一个哈希算法,可以算出键值对存储的下标,然后直接找到这个下标的数据取出就可以了. 5). 与NSArray对比 a. NSArray数组的元素,挨个存,按着顺序 b. 存的效率,NSArray要高; 取的时候,单个取NSDictionary相对要快,全部取,NSArray快。 NSMutableDictionary 1). 是NSDictionary的子类 2). 创建可变字典数组 NSMutableDictionary *dict1 = [NSMutableDictionary new]; NSMutableDictionary *dict2 = [[NSMutableDictionary alloc] init]; NSMutableDictionary *dict3 = [NSMutableDictionary dictionary]; 这样创建处理的字典长度为0,有意义,可以动态的新增与删除. NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"jack", @"name",nil]; 创建的时候并且初始化. 注意: 不可以使用以下代码创建 NSMutableDictionary *dict = @{@"name":@"rose",@"age":@"18"}; 3). 新增键值对 NSMutableDictionary *dict1 = [NSMutableDictionary new]; [dict1 setObject:@"jack" forKey:@"name"]; 如果键名重复,则会覆盖. 4). 删除所有的键值对 - (void)removeAllObjects: 删除所有的键值对 - (void)removeObjectForKey:(KeyType)aKey;根据键名删除指定值 5). 持久化--保存到本地 写到本地 [dict writeToFile:@"/Users/Apple/Desktop/1.plist" atomically: NO]; 读到内存 [NSMutableDictionary dictionaryWithContentsOfFile:@"/Users/Apple/Desktop/1.plist"]; 集合的内存管理 将对象存储到集合之中,会为这个对象的引用计数器+1,当集合销毁的时候,就会像存储在集合中的所有的对象发送一条release消息. 使用@[]或者@{}创建的集合已经是被autorelease过的了. 直接调用对象方法和类方法创建的对象,也是autorelease过的. 在ARC模式下,集合的元素是一个强类型指针. 二、NSFileManager作用: 创建、删除、赋值、拷贝、移动 NSFileManager是以单例模式创建的 // 单例返回 NSFileManager *fileManager = [NSFileManager defaultManager]; 判断 1). 判断指定的文件或者文件夹在磁盘上是否真实的存在 - (BOOL)fileExistsAtPath:(NSString *)path; 2). 判断指定的路径是否真实的存储在我们的磁盘上,并且判断这个路径是一个文件夹还是一个路径 - (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)isDirectory; 返回值: 代表这个路径是否真实存在 参数指针:代表这个路径是否是一个文件夹路径,YES为文件夹路径,NO为文件路径 3). 判断指定的文件夹或者文件是否可以读取 - (BOOL)isReadableFileAtPath:(NSString *)path; 4). 判断指定的文件夹或者文件是否可以写入. - (BOOL)isWritableFileAtPath:(NSString *)path; 5). 判断指定的文件夹或者文件是否可以删除 - (BOOL)isDeletableFileAtPath:(NSString *)path; 获取信息 1). 获取指定文件或者文件夹的属性信息 - (NSDictionary *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error 返回是一个字典,如果要拿到特定的信息,通过key 2). 获取指定目录下的所有文件的目录,是拿到指定目录下的所有文件和目录,所有的后代目录和文件 - (NSArray *)subpathsAtPath:(NSString *)path; 3). 获取知道给你目录下的所有的子目录和文件,不保护孙子辈 - (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error 文件/目录 1). 创建 - (BOOL)createFileAtPath:(NSString *)path contents:(NSData *)data attributes:(NSDictionary *)attr; 第1个参数: 要创建的文件路径 第2个参数: 文件的内容,NSData将别的数据转换为二进制数据 将字符串转换到NSData二进制的方式,调用字符串对象的 - (NSData *)dataUsingEncoding:(NSStringEncoding)encoding; 编码参数:NSUTF8StringEncoding 指定一个编码就可以将字符串转换为二进制数据,存储在NSData对象之中,最后再将这个二进制对象通过这个方法写入,如果想创建一个空文件,第2个参数就给nil。 第3个参数:指定创建的文件的属性,如果想要使用系统的默认值使用nil 2). 在指定的目录创建文件夹 - (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL) createIntermediates attributes:(NSDictionary *)attributes error:(NSError **)error; 第1个参数:路径 第2个参数:YES,指一路创建,NO不做一路创建 第3个参数:指定属性,nil为系统默认 第4个参数:异常信息 3). 拷贝文件 - (BOOL) copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error; 4). 移动文件,剪切,重命名 - (BOOL) moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error; 5). 删除文件 - (BOOL) removeItemAtPath:(NSString *)path error:(NSError **)error; 注意: 删除的文件不会倒废纸篓,而是直接删除,小心使用 三、常用的结构体 CGPoint 1). Foundation框架中CGPoint定义 struct CGPoint{ CGFloat x; CGFloat y; }; typedef struct CGPoint CGPoint; typedef CGPoint NSPoint; CGPoint类型实际上就是一个double类型. 这个结构体一般情况下来表示坐标,用来表示控件上的位置 声明CGPoint变量并初始化的方式: a. CGPoint p1; p1.x = 20; p1.y = 30; b. CGPoint p1 = {20, 30}; c. CGPoint p1 = {.x=20,.y=30}; d. Foundation框架中提供的函数快速创建CGPoint a). CGPointMake(x,y) CGPoint p1 = CGPointMake(20,30); b). NSMakePoint(x,y) CGPoint p1 = NSMakePoint(20,30); CGSize 1). 用来描述控件的大小 struct CGSize{ CGFloat width; CGFloat height; }; typedef struct CGSize CGSize; typedef CGSize NSSize; CGSize结构体一般用来表示控件的大小 CGSize声明并初始化的方式 a. CGSize size; size.width = 100; size.height = 30; b. CGSize size = {100, 30}; c. CGSize size = {.width = 100, .height = 30}; d. Foundation框架中提供的函数快速创建CGSize a). CGSizeMake(x,y) CGSize s1 = CGSizeMake(20,30); b). NSSizePoint(x,y) NSSize s1 = NSMakeSize(20,30); CGRect 1). 用来描述控件的大小 struct CGRect{ CGPoint origin; CGSize size; }; typedef struct CGRect CGRect; typedef CGRect CGRect; CGRect结构体一般用来表示控件的大小 CGRect声明并初始化的方式 a. CGRect rect; rect.origin.x = 20; rect.origin.y = 30; rect.size.width = 100; rect.size.height = 30; b. CGRect rect; rect.origin = (CGPoint){100, 30}; rect.size = (CGSize){100, 30}; d. Foundation框架中提供的函数快速创建CGRect a). CGRectMake(x,y,width,height) CGRect s1 = CGRectMake(10,20,20,30); b). NSRectMake(x,y,width,height) NSReca s1 = NSRectMake(10,20,20,30); 建议使用CG开头的 四、NSValue NSRange/CGPoint/CGSize/CGRect都是结构体,无法存储到集合中 先将结构体变量存储到OC对象中,在将OC对象存储到集合中 NSValue类的对象就是用来包装结构体变量的 CGPoint p1 = CGPointMake(10,20); NSValue *v1 = [NSValue valueWithPoint:p1]; 五、NSDate 时间处理 创建NSDate时间 NSDate *date = [NSDate new]; NSDate *date = [NSDate date]; 格式化输出日期 yyyy 代表年份 MM 代表月份 dd 代表日 HH 代表24小时制,hh代表12小时制 mm 代表分钟 ss 代表秒 NSDate *date = [NSDate new]; // 1. 先创建日期格式化对象,输出一个指定格式的对象 NSDateFormatter *formatter = [NSDateFormatter new]; // 2. 指定日期格式化对象 转换的格式 formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss"; // 3. 格式化对象按照指定的格式将日期对象转换,转换的时候会自动的转换为当前系统的时区 NSString *str = [formatter stringFromDate:date]; NSString *strDate = @"2011-12-12 12:12:12"; // 1. 先创建一个日期格式化器对象 NSDateFormatter *formatter = [NSDateFormatter new]; // 2. 指定字符串日期的格式 formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss"; // 3. 转换 NSDate *date = [formatter dateFromString:strDate]; 计算时间 1). 当前时间之上加上一个时间数 + (instancetype)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs; ``` NSDate *date = [NSDate new]; NSDate *date1 = [NSDate dateWithTimeIntervalSinceNow:60*60*10]; ``` 2). 两个时间之差 - (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate; 日历对象 NSDate *date = [NSDate date]; // 1. 创建一个日历对象 NSCalendar *calendar = [NSCalendar currentCalendar]; // 2. 让日历对象从日期对象取出日期的各个部分 NSDateComponents *com = [calendar components: NSCalendarUnitYear fromDate:date]; com.year,com.month,com.day 六、copy 无论是MRC还是ARC下,如果属性的类型是NSString类型的,@property参数使用copy. copy是个方法 1). 定义在NSObject中,作用:拷贝对象 NSString 调用copy方法,没有产生新对象,而是直接将对象本身的地址返回去,这种拷贝我们叫做浅拷贝. NSMutableString调用copy对象,产生了一个新对象,这种拷贝叫做深拷贝。拷贝出的对象是一个不可变的字符串对象。 2). mutableCopy拷贝之后是一个NSMutableString,是一个可变对象 字符串拷贝计数器问题 1). 若字符串对象存储在常量区中不允许被回收的,所以引用计数器是一个超大的数,retain和release无效 2). 若字符串存储在对去,这个字符串对象和普通的对象是一样的,引用计数器默认是1. 3). 字符串对象如果是浅拷贝,会将对象的引用计数器加1. 字符串对象如果是深拷贝,原来的对象的引用计数器不变. @property的copy copy方法的内部调用了另外一个方法copyWithZone方法,这个方法是定义在NSCoping协议中的,因为我们类没有遵守NSCopingWithZone方法,当我们自定义的类调用copy方法就会报错。 copyWithZone方法实现 --> 如果要做深拷贝,即重新创建一个对象,把对象属性的值复制到新对象中,将新对象返回。 --> 如果做浅拷贝,就直接将对象返回即可. 七、单例模式 一个类的对象,无论在什么时候,什么地方创建,得到的对象都是同一个.最终都会调用该alloc方法创建. 1). alloc方法的内部其实什么都没有做,只是调用了allocWithZone方法. 2). 实际上真正申请空间,创建对象的事情是allocWithZone方法在做. 要实现单例模式--重写+allocWithZone方法 + (instancetype)allocWithZone:(struct _NSZone *)zone{ static id instance = nil; if(nil == instance){ instance = [super allocWithZone:zone]; } return instance; } 单例规范 如果类是一个单例模式,要求为类提供一个类方法,来返回这个单例对象。 类方法的名称必须以 shared类名; default类名; 什么时候要把类写成单例的 1). 特点: 一个类的对象,无论在什么时候,什么地方创建,得到的对象都是同一个.最终都会调用该alloc方法创建.存储在单例对象中的数据可以被共享.

优秀的个人博客,低调大师

Objective-C 学习第五天

一、 创建一个对象,内存是如何分配 1). 子类对象中有自己的属性和所有父类的属性 2). 代码段中每一个类都有一个isa指针,这个指针指向它的父类. 结构体与类 相同点: 都可以将多个数据封装为1个整体 struct Data{ int year; int month; int day; } @interface Data : NSObject{ int year; int month; int day; } 2). 不同点: a. 结构体只能封装数据,而类不仅可以封装数据还可以封装行为 b. 结构体变量分配在栈空间(前提是局部变量),而对象分配在堆空间. 栈空间相对较小,但是访问效率高; 堆空间较大, 访问效率低. 3). 应用场景: a. 如果表示的实体不仅有数据还有行为,只能使用类 b. 如果表示的实体只有数据, 没有行为,视属性多少而定.如果属性只有几个,就定义为结构体; 如果属性较多,就定义为类. 类的本质 栈 堆 BSS段 数据段 代码段 内存.png 代码段是用来存储代码的. 类加载,当类第一次被访问的时候,这个类就会被加载到代码段存储起来. 类一旦被加载,是不会被回收的,除非程序结束. 1). 任何存储在内存中的数据都有1个数据类型. 2). 在代码段存储类的步骤 a. 先在代码段中创建Class对象, Class是Foundation框架中的一个类 b. 将类的信息存储在这个Class对象之中 Class对象至少有三个属性: 类名/属性s/方法s 存储类的这个Class对象,叫做类对象 所以存储类的类对象也有一个isa指针 类对象使用 1). 调用类的类方法class,就可以了得到存储类的类对象的地址 Class c1 = [Person class]; 2). 调用对象的对象方法,就可以了得到这个对象所属的类的Class对象的地址. Person p1 = [Person new]; Class c2 = [p1 class]; 注意: 声明Class指针的时候,不需要加,因为typedef的时候就已经加*了. 3). 拿到存储类的类对象以后 Class c1 = [Person class]; c1对象就是Person类, c1 完全等价于Person a. 使用类对象来调用类的类方法, Class c1 = [Person class]; [c1 sayHi] 等价于 [Person sayHi]; b. 创建对象 Class c1 = [Person class]; [c1 new] 等价于 [Person new]; 注意: 使用类对象,只能调用类的类方法,不能调用对象方法 SEL全程 select 选择器 SEL是一个数据类型. 要在内存中申请空间存储数据,SEL其实是一个类,SEL对象用来存储一个方法 1). 类是以Class对象的形式存储在代码段中 类名: 存储这个类的类名.NSString a. 如何将方法存储在类对象之中 a). 先创建一个SEL对象 b). 将方法的信息存储在这个SEL对象之中 c). 再将这个SEL作为Class对象的属性 2). 拿到存储方法的SEL对象 a. 因为SEL是typedef类型的,在自定义的时候已经加了,所以声明SEL指针的时候不需要加 b. 取到存储方法的SEL对象 SEL s1 = @selector(方法名); 示例: SEL s1 = @selector(sayHi); 3). 调用方法的本质 Person p1 = [Person new]; [p1 sayHi]; 内部原理: a. 先拿到存储sayHi方法的SEL对象,也就是拿到sayHi方法的SEL数据,SEL消息 b. 将这个SEL消息发送给p1对象 c. p1对象接收到这个SEL消息之后,就知道调用方法 d. 根据对象的isa指针找到存储类的类对象 e. 如果找到这个类对象之后,搜寻是否有和传入的SEL数据相匹配的,如果有,就执行,如果乜有就找父类,知道NSObject OC最重要的1个机制: 消息机制 调用方法的本质其实就是为对象发送SEL消息 [p1 sayHi]: 为p1对象发送一个sayHi方法 4). 手动为对象发送SEL消息 a. 先得到方法的SEL数据 b. 把这个SEL数据发送给p1对象 - (id)performSelector:(SEL)aSelector; 示例:Person *p1 = [Person new]; SEL s1 = @selector(sayHi); [p1 performSelector:sel]; c. 调用一个对象的方法有两种 a). [对象名 方法名]; b). 手动的为对象发送SEL消息 5). 注意事项: a. 如果方法名有参数 那么方法名是带了参数的 b. 如果方法有参数,就调用另外一个方法 - (id)performSelector:(SEL)aSelector withObject:(id)object; c. 如果方法有多个参数,就将对象传递过去 点语法 OC中也可以使用点语法来访问对象的属性,和Java,C#是完全不一样的. 1). 使用点语法访问对象的属性. 语法:对象名.去掉下划线的属性名p1.name = @"jack";这个时候就会将@"jack"赋值给p1对象的_name属性; NSString *name = p1.name;把p1对象的_name属性值取出来 2). 点语法原理 p1.age = 18; 这句话的本质并不是把18直接赋值给p1对象的_age属性,点语法在编译器编译的时候,会将点语法转换为调用setter/getter的代码 a. 当使用点语法赋值的时候,这个时候编译器会将点语法转换为调用setter方法的代码. 对象名.去掉下划线的属性名 = 数据; 转换为: [对象名 set去掉下划线的属性名首字母大写] = 数据; p1.age = 10相当于[p1 setAge:10]; b. 当使用点语法取值的时候,这个时候编译器会将点语法转换为调用getter方法的代码. 对象名.去掉下划线的属性名; 转换为: [对象名 去掉下划线的属性名]; int age = p1.ag相当于[p1 age]; 3). 注意: a. 在getter/setter方法中慎用点语法, 因为有可能会造成无线递归导致程序崩溃 b. 点语法在编译器编译的时候会转换为setter/getter方法的代码. 如果我们的setter和getter方法名不符合规范,那么就会报错. c. 如果属性没有封装getter/setter是无法使用点语法的 @property 1). 写一个类 a. 写属性 b. 声明属性的getter/setter c. 再实现getter/setter 2). @property a. 作用: 自动生成getter/setter的声明,应该写在@interface声明之中 b. 语法: @property 数据类型 变量名; @property int age; c. 原理: 编译器在编译的时候,会根据@property生成getter/setter方法的实现 @property 数据类型 名称; 生成为: - (void)set首字母大写的名称:参数; - (数据类型)名称; 示例: @property int age; - (void)setAge:(int)age; - (int)age; 3). 使用@property注意: a. @property的类型和属性的类型一致. @property的名称和属性名称一致(去掉下划线). b. @property的名称决定了生成getter和setter方法的名称. @property的数据类型决定了生成的getter和setter方法的数据类型. c. @property只是生成getter和setter方法的声明,实现要自己写. @synthesize 1). 作用: 自动生成getter/setter方法的实现,应该写在类的实现当中 2). 语法: @synthesize @property名称; // 声明 @interface Person : NSObject{ int _age; } @property int age; @end; // 实现 @implementation Person @synthesize age; @end 3). @synthesize做的事情 a. 生成一个真私有的属性,属性的类型和@synthesize对应的@property类型一致,属性的名字和@synthesize对应的@property名字一致. b. 自动生成setter方法的实现 实现的方式:将参数直接赋值给自动生成的那个私有属性.并且没有做任何的逻辑验证 c. 自动生成getter方法的实现 4). 希望@synthesize不要自动生成私有属性,getter/setter的实现中操作我们写好的属性. a. 语法 @synthesize @property 名称 = 已经存在的属性名; 示例: @synthesiez age = _age; a). 不会再生成私有属性 b). 直接生成getter/setter的实现. setter实现: 把参数的值直接赋值给指定的属性. getter的实现: 直接返回指定的属性的值. 5). 注意: a. 如果直接写一个@synthesize @synthesize name; b. 如果指定操作的属性. @synthesize name = _name; c. 生成的setter方法实现中没有任何逻辑验证,生成的getter方法的实现中式直接返回属性的值 d. 如果要有自己的逻辑验证,需要自己实现. 6). 批量声明 a. 如果多个@property的类型一致,可以批量声明. @property float height,weight; b. @synthesize批量写, @synthesize name = _name, age = _age; @property增强 1). 从4.4之后, 对@property做了增强. 2). 只需要写一个@property,编译器就会自动的生成私有属性、生成getter/setter的声明、生成getter/setter的实现. 3). @property NSString *name; a. 自动的生成一个私有属性, 属性的类型和@property类型一致,属性的名称和@property的名称一致,属性的名称自动的加下划线 b. 自动的生成getter/setter的声明 c. 自动的生成getter/setter的实现 setter的实现直接将参数的值赋值给自动生成的私有属性. getter的实现直接返回私有属性的值. 4). 使用注意 a. @property的类型一定要和属性的类型一致,名称要和属性的名称一致 b. 可以批量声明 动态类型和静态类型 1). OC是一门若语言,编译器在编译的时候,检查的时候没有那么严格. 优点:灵活 缺点:太灵活 强类型的语言: 编译器在编译的时候做语言检查的时候,非常严格. 2). a. 静态类型:指的是一个指针指向的对象是一个本类对象, b. 动态类型: 指的是一个指针指向的对象不是本类对象。 3). 编译检查:编译器在编译的时候,能不能通过1个指针去调用指针指向的对象的方法. 判断原则:看这个指针所属的类型之中是否有这个方法,如果有就认为可以调用,编译通过,如果没有就报错。 4). 运行检查: 编译检查只是骗过了编译器,但是这个方法究竟能不能执行还不一定,运行时会去检查对象当中是否有这个方法 NSObject是OC中所有类的基类,根据LSP NSObject指针就可以指向任意的OC对象,所有NSObject指针时一个万能指针.可以指向任意的OC对象. 缺点: 如果要调用指向的子类对象的独有的方法,就必须要就类型的强转。 id指针是一个万能指针,可以指向任意的OC对象. 1). id是一个typedef类型,在定义的时候已经加了,所以声明id指针的时候就不需要加了 2). id指针是一个万能指针,任意的OC对象都可以指。 3). NSObject与id 相同点: 万能指针,都可以指向任意的OC对象 不同点: 通过NSObject指针去调用对象的方法的时候,编译器会做编译检查.通过id类型的指针去调用对象方法的时候,不会报错. 4). 注意:id指针只能调用方法,不能使用点语法 instancetype 1). 如果方法的返回值是instancetype代表方法的返回值是当前类的对象.// 声明 - (instancetype)person; // 实现 - (instancetype)person{ return [self new]; } 建议: a. 如果方法内部是在创建当前类的对象,不要写死成类名[类名 new],而是用self代替类名 b. 如果方法的返回值是当前类的对象,也不要写死了,而是写instancetype. 2). id和instancetype的区别 a. instancetype只能作为方法的返回值,不能在其他地方使用,id既可以声明指针变量也可以作为参数,也可以作为返回值 b. instancetype是一个有类型的代表当前类的对象,id是一个无类型的指针,仅仅是一个地址,没有类型的指针 构造方法 1). 创建对象 类名 *指针名 = [类名 new]; new 实际上是个类方法. new方法作用: -> 创建对象 -> 初始化对象 -> 把对象的地址返回 new方法的内部,其实是先调用alloc方法,再调用init方法 alloc方法是一个类方法,那个类调用这个方法就创建哪个类对象 init是一个对象方法,初始化对象 创建对象的完整步骤: 应该是先使用alloc创建一个对象,然后使用init初始化这个对象 Person *p1 = [Person new]; 完全等价于 Person *p1 = [[Person alloc] init]; 2). init方法 作用: 初始化对象,为对象的属性赋初始值,这个init方法我们叫做构造方法 init方法做到的事情: 初始化对象. 3). 想要让创建对象的属性的默认值不是nil,NULL,0,那么我们可以重写init方法.按照我们自己的想法为对象的属性赋值. 重写init方法规范: a. 必须先调用父类的init方法. b. 调用方法初始化对象失败返回nil c. 判断父类是否初始化成功,如果不是nil,说明初始化成功 d. 如果初始化成功,就初始化当前对象的属性 e. 最后返回self - (instancetype)init{ // 初始化父类属性的值 self = [super init]; if(self){ self.name = @"jack"; } return self; } 4). 自定义构造方法 规范: a. 返回值必须是instancetype b. 自定义构造方法开头必须是initWith c. 方法的实现与init的要求一样 - (instancetype)initWithName:(NSString *)name{ // 初始化父类属性的值 self = [super init]; if(self){ self.name = name; } return self; } Dog *d1 = [[Dog alloc] initWithName:@"小黄"]; 动态类型检测 1). 判断指针指向的对象当中,指定的方法是否可以调用 Person *p1 = [Person new]; BOOL isHave = [p1 responseToSelector:@selector(setName:)]; 2). 判断类方法是否可以调用 3). 判断对象是否为指定类对象或者子类对象 NSMutableString *str = [NSMutableString class]; BOOL res = [str isKindOfClass:[NSString class]]; 4). 判断对象是否为指定类的对象(不包括子类) NSMutableString *str = [NSMutableString class]; BOOL res = [str isMemberOfClass:[NSString class]]; 5). 判断指定的类是否为另外一个类的子类 BOOL res = [NSMutableString isSubclassOfClass:[NSString class]];

优秀的个人博客,低调大师

Objective-C 学习第七天

一、 自动释放池的原理 存入到自动释放池中的对象,在自动释放池销毁的时候,会自动调用储存在该自动释放池中的所有对象的release方法. 可以解决的问题: 将创建的对象,存入到自动释放池之中,就不再需要手动的release这个对象了,因为池子销毁的时候,就会自动的调用池中所有的对象release. 如何创建自动释放池 @autoreleasepool{ } 这对大括号代表这个自动释放池的范围 如何将对象存储到这个自动释放池中 在自动释放池之中调用对象的autoreleasepool方法,就会将这个对象存入到当前自动释放池之中,这个autorelease方法返回的是对象本身,所以可以这么写 @autoreleasepool{ Person *p1 = [[[Person alloc] init] autorelease]; } 这个时候,当这个自动释放池执行完毕之后,就会立即为这个自动释放池中的对象发送一条release消息. autorelease好处: 创建对象,调用对象的autorelease方法,将这个对象存入当前的自动释放池之中,我们不需要再去release,因为自动释放池销毁的时候,就会自动的调用池中所有对象的release. 使用注意 1). 只有在自动释放池中调用了对象的autorelease方法,这个对象才会被存储到这个自动释放池中,如果只是将创建对象的代码写在自动释放池中,而没有调用对象的autorelease方法,是不会将这个对象存储到这个自动释放池之中的. 2). 对象的创建可以在自动释放池的外面,在自动释放池中,调用对象的autorelease方法,就可以将这个对象存储到这个自动释放池中. 3). 如果对象的autorelease方法的调用放在自动释放池的外面,是无法将其存储到这个自动释放池之中的,autorelease的调用只有放在自动释放池之中,才可以将其存储到自动释放池之中,对象的创建可以在外面. 4). 当自动释放池结束的时候,仅仅是对存储在自动释放池中的对象发送一条release消息,而不是销毁对象. 5). 如果在自动释放池中,调用同一个对象的autorelease方法多次,就会将这个对象存储多次到这个自动释放池中,在自动释放池结束的时候,就会为对象发送多次release消息. 所以,一个自动释放池中,只autorelease一次,只讲这个对象放一次,否则就会出现野指针错误. 6). 如果在自动释放池中,调用了存储到自动释放池中的对象的release方法,在自动释放池结束的时候,还会再调用对象的release方法,这个时候也有可能会造成野指针操作. 7). 将对象存储到自动释放池,并不会使对象的引用计数器+1,其好处就是创建对象存储早自动释放池,就不要再写release方法了. 8). 自动释放池可以嵌套.调用对象的autorelease方法,只会讲对象加入到自动释放池中,只有在当前自动释放池结束的时候才会向对象发送release消息. autorelease规范 1). 创建对象,将对象存储到自动释放池中,就不需要再去手动的release 2). 类方法的第一个规范: 一般情况下,要求提供与自定义构造方法相同功能的类方法,这样可以快速的创建一个对象. 3). 类方法的第二个规范: 一般情况下,会为我们的类写一个类方法,用来让外界调用类方法类快速的得到一个对象. 规范: 使用类方法创建的对象,要求这个对象已经被autorelease过了. 提供一个类方法来快速的得到一个对象. 规范: a. 这个类方法已类名开头,如果没有参数就直接是类名,如果有参数就是withXX: b. 使用类方法得到的对象,要求这个对象已经被autorelease过了 + (instancetype)person{ return [[[selft alloc] init] autorelease]; } 这样我们直接调用类方法,就可以得到一个已经被autorelease过的对象. @autoreleasepoop{ Person *p1 = [Person person]; // 这个p1对象已经被autorelease过了,不需要再调用autorelease方法 // 这个对象被存储到当前的自动释放池中 }// 当自动释放池结束,就会为存储在其中的p1对象发送release消息. 二、ARC 什么是ARC Automatic Reference Counting 自动引用计数,即ARC 顾名思义: 系统自动的帮助我们去计算对象的应用计数器的值. 在程序中使用ARC非常简单,只需要像往常一样编写代码. 只不过永远不要写retain、release、autorelease这三个关键字就好,这个ARC最基本的原则. 当ARC开启时,编译器会自动的在合适的地方插入retain、release、autorelease代码,编译器自动为对象做引用计数,而作为开发者,完全不需要担心编译器会做错. 需要特别注意: ARC是编译器机制,在编译器编译代码的时候,会在适当的位置加入retain、release、autorelease代码. 在ARC机制下,对象何时被释放: 只要没有强指针指向这个对象,这个对象就会立即回收. 强指针与弱指针 强指针: 默认情况下,我们声明一个指针,这个指针就是强指针,我们也可以使用__strong来显示的声明这是一个强指针. Person *p1; 这是一个强指针,指针默认情况下都是一个强指针. __strong Person *p2; 这也是一个强指针,使用__strong来显示声明的强指针. 弱指针: 使用__weak标识的指针就叫做弱指针. 无论强指针还是弱指针,都是指针,都可以用来存储地址,都可以通过这个指针访问对象的成员,唯一的区别就是在ARC模式下,他们用来作为回收对象的基准. 确认程序是否开启ARC机制 1). 默认情况下,Xcode开启ARC机制 2). ARC机制下,不允许调用retain/release/retainCount/autorelease方法 3). 在dealloc中不允许[super dealloc] 演示第一个ARC案例 int main(int argc, const char *argv[]){ @autoreleasepool{ Person *p1 = [Person new]; }// 当执行到这里,p1指针被回收 return 0; } ARC下的单个对象的内存管理 在ARC机制下: 当没有任何一个强指针指向它的时候,就会被立即回收. 1). 当指向对象的所有的强指针被回收的时候,对象就会被立即回收 int main(int argc, const char *argv[]){ @autoreleasepool{ Person *p1 = [Person new]; Person *p2 = p1; }// 当执行到这里,p1指针被回收,p2指针也被回收 return 0; } 2). 将所有指向对象的强指针赋值为nil的时候,对象就会被立即回收 int main(int argc, const char *argv[]){ @autoreleasepool{ Person *p1 = [Person new]; p1 = nil; }// 当执行到这里,p1指针被回收,p2指针也被回收 return 0; } 强指针与弱指针 1). 强指针与弱指针的声明 默认情况下,所有的指针都是强类型的. Person *p1 = [[Person alloc] init]; p1指针是强类型的,因为默认情况下指针都是强类型的. 不过我们可以使用__strong来显示的标识指针是强类型指针. __strong Person *p2 = [Person new]; 这时候p2指针是强类型的,其实写不写__strong都是强类型指针. 指针类型也可以是弱指针类型. 使用__weak标识指针的类型是弱类型指针. __weak Person *p3 = p2; 这时候,p3指针是1个弱类型的指针,p3弱指针也指向p2强指针指向的对象. 在操作对象的时候,通过强指针或者弱指针都可以操作,没有任何区别 2). ARC模式下的对象回收标准 ARC机制下释放对象的标准,没有任何强指针指向对象的时候,对象就会被释放.如果这个时候有弱指针指向,也会被释放。 int main(int argc, const char *argv[]){ @autoreleasepool{ __strong Person *p1 = [[Person alloc] init]; __weak Person *p2 = p1; p1 = nil;// 当执行到这里,p1指针被回收,p2指针也被回收 } return 0; } 3). 最重要的一点:不能创建对象用1个弱指针存储这个对象的指针,这样的话,刚创建出来的对象,就没有任何强指针指向,创建处理就会被回收. int main(int argc, const char *argv[]){ @autoreleasepool{ // 创建一个对象,刚创建出来就会被回收 __weak Person *p2 = [[Person alloc] init]; } return 0; } 4). 在ARC的机制下,原来指向这个对象的弱指针,会被自动设置为nil. 在ARC机制下,@property参数就不能使用retain,因为retain代表生成的setter方法是MRC的标准的内存管理代码,而我们在ARC机制下不需要那些代码. 所以在ARC机制下的setter方法,什么都不需要做. ARC机制下,关注的重点,当一个类的属性是一个OC对象的时候,这个属性应该声明为强类型的. 1). 控制@property生成的私有属性,是一个强类型还是弱类型. 使用参数strong和weak @property(nonatomic,strong)Car *car; 代表生成的_car是强类型 @property(nonatomic,weak)Car *car; 代表生成的_car是弱类型 如果不写,默认是strong 使用建议 1). 在ARC机制下,如果属性的类型是OC对象类型,使用strong 2). 在ARC机制下,如果属性的类型不是OC对象类型,使用assign 3). strong和weak都是应用在属性类型是OC对象的时候 4). 在ARC机制下,将MRC下的retain换为strong 循环引用 解决方案: 其中一方使用weak,另一方使用strong 三、 遇到的问题--MRC与ARC兼容 点击项目->Target->Build Phases->Compile Sources->选中要使用MRC编译的类,双击->填写 -fno-objc-arc 使用命令-fno-objc-arc MRC转换为ARC Edit-> Convert->To Object-C ARC...->选中要转成ARC的程序选中,点击Check->Next->Save. 分类(类别/类目)--category 默认情况一个类独占一个模块,这个时候将所有的成员都写在这个模块中,很难管理。将功能相似的方法定义在同一个模块中,这样的好处,方便维护和管理。 1). 顾名思义: 把类分开,将一个类分为多个模块 2). 如何为一个类添加分类: Target->New File->Objective-C File->Next, File: itcast File Type: Category Class: Student(选择要分的类) Next->Create. 3). 会生成一个.h和一个.m模块 a. 模块的文件名是本类名+分类名.h,本类名+分类名.m 4). 添加的分类分为声明和实现 @interface 本类名 (分类名) @end 代表不是新创建一个类,而是对已有的类添加一个分类,小括弧中写上分类的名字,因为一个类可以添加多个分类,为了区分每个类 @implementation 本类名 (分类名) @end 这是分类的实现 5). 分类的使用 a. 如果要访问分类中定义的成员,就要把分类的头文件引进来. 6). 分类的作用:就是将一个类分为多个模块 7). 注意 a. 分类中只能增加方法,不能增加属性 b. 在分类中可以写@property但是不会自动 生成私有属性,也不会自动生成getter/setter的实现,只会生成getter/setter的声明 c. 在分类的方法实现中不可以直接访问本类的私有属性,但是可以调用本类的getter/setter来访问属性 d. 当分类中有和本类同名方法的时候,优先调用分类的方法,哪怕没有引入分类的头文件,如果多个分类中有多个相同的方法,优先调用最后编译的分类 8). 什么时候需要使用分类 当一个类的方法很多很杂的时候,比较臃肿的时候,这个时候就可以使用分类,将功能相似的方法写在同一个模块当中 分类的作用在于可以将我们写的类分为多个模块 为系统自带的类写分类,这就叫做非正式协议. 1). 分类的第一个作用: 分为多个模块,方便管理 2). 分类的第二个作用: 为一个已经存在的类添加方法

优秀的个人博客,低调大师

Objective-C 学习第八天

一、延展 延展: Extension 1). 是一个特殊的分类,所以延展也是类的一部分 2). 特殊之处: a. 延展这个特殊的分类没有名字 b. 只有声明没有实现,和本类共享一个实现 延展的语法 语法: @interface 本类名 () @end 没有实现,和本类共享一个实现. 为类添加延展步骤 New File->FileType选择Extension 只有一个.h文件,文件名:本类名_取的文件名.h 这个文明中只有声明 @interface Person () @end 延展的基本使用 1). 延展的本质是一个类,作为本类的一部分 只不过是一个特殊的分类,没有名字 2). 延展只有声明,没有单独的实现,和本类共享一个实现 延展和分类的区别 1). 分类有名字,延展没有名字,是一个匿名的分类 2). 每一个分类都有单独的声明和实现,而延展只有声明,没有单独的实现,和本类共享一个实现 3). 分类中只能新增方法,而延展中任意的成语都可以写 4). 分类中可以写@property但是只会生成getter/setter声明,延展中写@property会自动生成私有属性,也会生成getter/setter的声明和实现 延展的应用场景 1). 要为类写一个私有的@property 生成的getter/setter只能在类的内部访问,不能在外部访问 其实,只让@property生成私有属性、生成getter/setter方法的实现. 2). 延展100%的情况下不会独占一个文件,都是将延展直接写在本类的实现文件当中,这个时候,写在延展中的成员,就相当于这个类的私有成员,外部无法访问,只能在本类的实现中访问 3). 当我们想为类定义私有成员的时候,就可以使用延展,将延展定义在这个类的实现当中.虽然可以定义在@implementation中,但是不要这么写,这样不规范,写一个延展,定义在延展当中。 *. 如果要为类写一个私有方法,建议将声明写在延展当中,实现写在本类的实现中 *. 如果想要为类写一个私有的@property,就直接写在延展中 4). 延展天生就是来私有化类的成员 如果类的成员只希望在类的内部访问,那么就将其定义在严重中,如果类的成员允许被外界访问,定义在本类的@interface中 二、block OC在C的基础上新增了一些数据类型 BOOL/Boolean/class/nil/SEL/id/block block是一个数据类型 既然是一个数据类型,那么就可以声明这个数据类型的变量 1). block是一个数据类型,所以我们可以声明一个block类型的变量 2). block类型的变量中专门存储一段代码,可以有参数,可以有返回值. block变量的声明 1). 虽然block变量中是用来存储一段代码的,但是一个block变量中并不是任意的一段代码都可以存进去的,而是有限定的。在声明block变量的时候,必须要指定这个block变量存储的代码是否有参数,是否有返回值,一旦指定以后,这个block变量中就只能存储这样的代码了. 声明了一个block变量,返回值是void,参数是一个int类型的,这个block变量中就只能存储无返回值,并且有一个int参数的代码段. 2). 声明block变量的语法格式: 返回值类型 (^block变量的名称)(参数列表); void (^myBlock1)(); 表示声明了一个block类型的变量,叫做myBlock1,这个变量中只能存储没有返回值没有参数的代码段. 3). 最重要 声明block变量的时候要指定这个block变量可以存储的代码段的返回值和参数描述,一旦指定,这个block变量中就只能存储这样的代码了,其他格式的代码段无法存储 初始化block变量 1). 原理: 写一个符合block要求的代码段,存储到block变量中就可以了 2). 代码段的书写格式: ^返回值类型(参数列表){ 代码段; }; 3). 写一个无参数无返回值的代码段: 这个时候我们将这段代码使用赋值符号存储到无返回值无参数要求的block变量中. void (^myBlock1)(); ^void(){ NSLog(@"我"); NSLog(@"我"); }; 等价于 void (^myBlock1)(); myBlock1 = ^void(){ NSLog(@"我"); NSLog(@"我"); }; 还等价于 void (^myBlock1)() = ^void(){ NSLog(@"我"); NSLog(@"我"); }; 4). 有返回值的代码段. 我们可以将这段代码赋值给符合要求的block变量. int (^myBlock2)(); myBlock2 = ^int(){ int num1 = 10 + 20; return num1; }; 5). 既有参数又有返回值的代码段. int (^myBlock3)(int num1, int num2); myBlock3 = ^int(int num1, int num2){ int num3 = num1 + num2; return num3; }; 6). 注意 赋值给block变量的代码段必须要符合block变量的要求,否则就会报错. 如何执行存储在block变量中的代码 语法格式: block变量名(); 有参数就传参数,有返回值就接. 关于block简写 1). 如果我们写的代码段没有返回值,那么代码段的void可以省略 void (^myBlock1)() = ^(){ NSLog(@"我"); NSLog(@"我"); }; 注意: 代码段的返回值如果是void可以省略,声明block变量的返回值不能省略. 2). 如果我们写的代码段没有参数,那么代码段的小括弧可以省略 int (^myBlock2)(); myBlock2 = ^int{ int num1 = 10 + 20; return num1; }; 强调: 指的是代码段 所以,当一个代码段既没有参数也没有返回值的时候,就只写^ void (^myBlock1)() = ^ { NSLog(@"我"); NSLog(@"我"); }; 3). 声明block变量的时候,如果有指定的参数,可以只写参数的类型而不写参数名称 int (^myBlock3)(int, int); myBlock3 = ^int(int num1, int num2){ int num3 = num1 + num2; return num3; }; 注意: 声明block的时候可以省略,在写代码段的时候类型和名城管都要写 4). 无论代码段是否有返回值,在写代码的时候都可以省略 int (^myBlock3)(int, int); myBlock3 = ^(int num1, int num2){ int num3 = num1 + num2; return num3; }; 如果代码段中没有返回任何数据,那么它会认为代码段是没有返回值的,如果代码中有返回数据,返回的数据什么类型,它就会认为这个代码段是什么类型. 建议: 不要简写,仍然按照最标准的写法来写block变量和block代码段,因为这样可以提高代码段的阅读性. typedef简化block 1). 定义block类型长的问题 2). typedef的使用场景:讲一个长类型定义为短类型 3). 使用typedef将长的block类型定义为一个短类型 typedef 返回值类型 (^新类型)(参数列表) typedef void (^NewType)(); 代表重新定义了一个类型叫做NewType,是一个block类型,无参数无返回值的block类型. 关于block块访问外部变量的问题 1). 在block代码块的内部可以取定义在外部的变量的值,定义在外部的局部变量和全局变量 2). 可以修改全局变量的值,不能修改局部变量的值. 3). 如果你希望我们定义的局部变量可以允许在block代码块的内部去修改,那么就为这个局部变量加1个__block的修饰符. block是一个数据类型,作为函数参数 1). 如何为函数定义block类型的参数 a. 在小括弧中声明一个指定格式的block变量 b. 调用的时候可以直接将符合要求的代码段传递过去 c. 通过Xcode提示可以快速的生产block代码段 2). block作为参数可以实现的效果 可以将调用者自己写的代码传递到函数的内部执行 3). block作为函数的返回值返回 当将block作为函数的返回值的时候,返回值的类型就必须要使用typedef定义的短语句 block与函数 相同点:都是封装一段代码 不同点: 1). block是一个数据类型,函数是一个函数 2). 我们可以声明block类型的变量,函数就只是函数 3). block可以作为函数的参数. 三、协议 协议: protocol. 作用: 专门用来声明一大堆方法(不能声明属性,也不能实现方法,只能用来写方法的声明). 只要某个类遵守了这个协议,就相当于拥有这个协议中的所有的方法声明. 协议的声明 @protocol 协议名称 <NSObject> 方法的声明; @end 新建一个协议的方式, NewFile->OC-File-protocol 协议的文件名:.h 并且只有一个.h文件 在协议中,只能用来声明方法 作用: 专门用来写方法声明的 类遵守协议 如果想让一个类拥有协议中定义的所有的方法声明,那么就让这个类遵守这个协议,类只要遵守这个协议,那么这个类就拥有了这个协议中定义的所有的方法的声明了. @interface 类名 : 父类名 <协议名称> @end :表示继承 <> 表示遵守的协议 当一个类,遵守了一个协议,那么就相当于这个类拥有了协议中定义的所有的方法的声明,这个类只是拥有了这个协议中的声明而已,没有实现,所以,这个类,就应该实现协议中的方法. 如果类不实现协议中的方法,也不会报错,只是编译器会报警告,但是当创建对象,来调用 这个没有的实现的协议中的方法的时候就会报错. 类是单继承,但是协议可以多遵守 一个类值能有一个父类,但是一个类可以遵守多个协议 @interface 类名 : 父类名 <协议名称1, 协议名称2...> @end 当一个类遵守了多个协议之后,就相当于这个类拥有了所有协议中定义的方法的声明,那么这个类,就应该实现所有协议中的方法 @required 与 @optional 在协议当中,如果方法的声明被@required修饰,那么这个协议的类就必须要实现这个方法,否则编译器会发出警告. 在协议当中,如果方法的声明被@optional修饰,那么遵守这个协议的类可以实现这个方法,也可以不实现这个方法,不实现编译器也不会报警告. 无论@required与@optional都可以不实现,编译器不会报错,仍然可以编译运行,唯一的区别:当遵守协议的类不实现协议中的方法的时候,@required会给出一个警告,@optional警告都没有,默认是@required. 协议可以从另外一个类继承,也可以多继承. 协议可以继承另外一个协议,A协议继承B协议,那么A协议中不仅有自己的方法的声明,还有B协议中的方法的声明。如果有一个类遵守了A协议,那么这个类就拥有了A、B协议中所有方法的声明. 协议之间继承的语法格式 @protocol A协议名称 <B协议名称> @end 代表A协议继承自B协议,A协议中既有自己方法的声明,也有B协议中的方法声明。要求所有的协议直接或者间接的从NSObject基协议继承. @protocol类型限制 1). 这个指针指向遵守了指定协议的任意对象,否则就会报警告 NSObject<协议名称> *指针名; id<协议名称> *指针名; 2). 声明一个指针变量,要求这个指针变量指向的对象必须遵守多个协议 NSObject<协议名称1, 协议名称2> *指针名; id<协议名称1, 协议名称2> *指针名; 3). 定义一个指针,指向遵守了学习协议的学生对象. Student<StudyProtocol> *stu = [Student new]; 4). 为什么 a. 遵守了某个协议的类,就相当于这个类拥有了这个协议所定义的行为 b. 因为要调用对象中的协议方法,只有遵守了这个协议,才能有协议中的方法.

优秀的个人博客,低调大师

Objective-C 学习第二天

一、 对象在内存中的存储 内存中的五大区域 栈 存储局部变量 堆 程度员手动申请的字节看空间 malloc calloc realloc函数 BSS段 存储未被初始化的全局变量, 静态变量 数据段(常量区) 存储已被初始化的全局、静态变量、常量数据 代码段 存储代码 存储代码程序 类加载 a. 在创建对象的时候 肯定需要访问类 b. 声明1个类的时候指针变量也会访问类 类加载: 在程序运行期间,当某个类第1次被访问的时候就会把类加载到代码段 只有类在第1次被访问的时候,才会做类加载. 一旦类被加载到代码段后,直到程序结束的时候才会被释放 对象在内存中究竟是如何存储的 假设下面这个写在函数中 Person *p1 = [Person new]; Person *p1 : 会在栈内存中申请1空间,在栈内存中声明1个Person类型的指针变量, p1是指针变量,只能存储地址 new做的事情: a. 在堆内存中申请1块合适大小的空间 b. 在这个控件中根据类的模板创建对象 依次声明属性,对象还有额外的一个属性,叫isa是一个指针。 isa指向所属的类在代码段当中的地址. c. 初始化对象的属性 如果属性的类型是基本数据类型,就赋值为0; 如果属性的类型是C语言的指针类型,就赋值为NULL; 如果属性的类型是OC语言的指针类型,就赋值为nil; d. 返回对象的地址 注意 a. 对象当中只有属性,而没有方法, 自己类的属性外加1个isa指针指向代码段中的类 b. 如何访问对象的属性 指针名->属性名; 根据指针 找到指针指向的对象 再找到对象中的属性来访问 c. 如何调用方法 [指针名 方法名]; 先根据指针名找到对象,对象发现要调用方法 再根据对象的isa指针找到类,然后调用类里的方法 为什么不把方法存储在方法之中 因为每个对象的方法实现代码都是一模一样的,所以只保存一份来节省空间 对象属性的默认值 如果我们创建一个对象,每一为对象的属性赋值,这个对象是有值的。 如果属性的类型是基本数据类型,就赋值为0; 如果属性的类型是C语言的指针类型,就赋值为NULL; 如果属性的类型是OC语言的指针类型,就赋值为nil; NULL与nil 1). NULL 只能可以作为指针变量的值,如果1个指针变量的值是NULL代表这个指针不指向内存中的任何一块空间,NULL其实等价于0.是一个宏 #define NULL ((void *)0) 2). nil 只能作为指针变量的值. 代表这个指针不指向内存中的任何空间. nil也等价于0 也是一个宏 所以NULL和nil是一样的 3). 使用建议 a. C指针用NULL值 int *p = NULL; b. OC的类指针用nil Person *p = nil; 4). 如果1个类指针的值为nil,代表这个指针不指向任何对象 Person *p = nil; 不能向它的属性赋值, 对象的方法不会执行. 同类型的指针变量之间是可以相互赋值的 分组导航标记 导航栏中的main.m后的No Selection点击选择方法或类 #pragma mark 分组名 在@interface和@implementation上方添加 #pragma mark - 在导航条对应的位置显示一条水平分割线 #pragma mark - 分组名 在导航条对应的位置产生一条水平分割线,再产生分组名 函数和方法对比 在C中学习的叫做函数 void test(){} 在OC类当中写的叫方法 (void)sayHi; 相同点: 都是用来封装代码,表示一个相对独立的功能 不同点: a. 语法不同. b. 定义的位置不一样 OC方法的声明只能写在@interface的大括弧外面,实现只能写在@implementation. 函数除了函数的内部和@interface大括弧之中,其他地方都可以写 c. 调用的方式不同: 函数可以直接调用, 方法必须要创建之后调用 d. 方法是属于类的, 函数是对立的 最容易犯的错 1). @interface与@implementation不能相互嵌套 2). 类必须要先声明 3). 类的声明和实现必须都有要 4). 类的声明必须要放在使用类之前,实现可以放在其后 5). 声明类的时候,声明和实现必须要同时存在. 特殊情况: 可以只有实现,没有声明. 但不可这么写 6). 属性名一定要以下划线开头, 类名首字母要大写 7). 属性不允许声明的时候赋值 8). OC方法必须要创建对象通过对象名来调用 9). 方法不能只声明不实现 unrecognized selector sent to instance 0x100420510 方法不存在或者没有实现 二、多文件开发 多文件开发 把1个类写在一个模块当中, 而一个模块至少两个文件 .h 头文件 写类的声明 .m 实现文件 写类的实现 简洁方式 NewFile->Cocoa Class即可 对象和方法 对象作为方法的参数, 对象作为方法的返回值 1). 类的本质是我们自定义的数据类型 什么是数据类型: 是在内存中开辟空间的模板 2). - (void)test:(Dog *)dog; 3). 当对象作为方法参数传递的时候,是地址传递. 对象作为方法的返回值 (Dog *)product; 对象作为类的属性 1). 一个Target中的类无法直接在另一个Target中使用 2). 属性的本质是变量 @interface Person : NSObject { Dog *dog; } @end 本类对象作为本类方法的参数 @interface Dog : NSObject{ int _age; } - (BOOL) compare: (Dog *)dog; @end @implementation Dog - (BOOL) compare: (Dog *)dog{ return _age > dog->_age; } @end

优秀的个人博客,低调大师

《天风文章》V1.0.0使用说明

一、站点部署说明 Ø 下载站点发布程序,把解压包中的全部文件上传到虚拟空间或设为本地iis虚拟目录,asp.net2.0+ access架构。 Ø 系统默认为Access数据库,如果需要改为Sql Server数据库,需要在web.config文件中更改(有注释)。 Ø ,可以更改站点的标题,以及尾部的显示。(有注释) 二、站点使用说明 Ø 首页菜单中有登陆后台的连接,登陆时,默认用户和密码都是admin(小写)。 Ø 后台内容管理一般采用上面列表,下面编辑界面的形式,如下图所示。默认进入列表显示界面,点右上角的“新增”可以增加项目;点击项目左侧的“Edit”连接,可以修改本条内容 Ø 本版本中,暂时不考虑删除功能,但可以将不需要的内容隐藏,即不在主页显示。 编辑界面示意图: 三、代码使用说明 代码实现的规划,完全如下架构图,共五个项目,除JKLib因为是本人长期积累的东西,暂时不公开外,其他四个完全开源!JKLib中,感兴趣的朋友们可以Mail 我,提供部分源码。 希望在我的作品上二次开发的朋友,能在你的作品上提一下我啊!本人也衷心希望大家能一起交流,共同提高! 重新编辑时,只需要解压缩源码,使用VS2005打开解决方案即可。4个项目的依赖关系如下: 本文转自Jack Niu博客园博客,原文链接:http://www.cnblogs.com/skywind/archive/2007/03/06/665181.html,如需转载请自行联系原作者

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册