GeekBand 第二周

字符串 NSString

OC的字符串具有共享性,和恒定性。

NSString共享机制

<pre><code>
//其实这两个变量都指向了同一个地址

NSString *str1 = @"Hello World!";

NSString *str4 = @"Hello World!";
</pre></code>为了避免内存浪费,编译器会做优化,将对象的指针指向同一个地方。但是,这种情况只会在字面量初始化时出现

字符串的初始化:

<pre><code>NSString *str1 =@"Hello World!";//字面量初始化

NSString*str2 = [[NSStringalloc]initWithCString:"Hello
World!" encoding:NSUTF8StringEncoding];

//初始化器初始化

NSString*str3 = [NSStringstringWithCString:"Hello
World!" encoding:NSUTF8StringEncoding];

//工厂初始化

//其中,工厂初始化方法,
是一个类方法,通过类方法内部返回一个新的对象
</pre></code>

字符串基本操作

<pre><code>
[str1 stringByAppendingString: @"Hello World"] //加字符串

[str1 stringByReplacingCharactersInRange: @"Hello World"]//替换字符串

[str1 isEqualToString:str2]// 比较值是否相等

str1 == str2 比较指针是否相等
</pre></code>

<pre><code>
for(int i =0 ;i < [str1 length];i
++){

NSLog(@"%c",[str1 characterAtIndex:i]); //遍历字符串

}

str1 = str1.uppercaseString;//大写

str1 = str1.lowercaseString;//小写

str1 = str1.capitalizedString;//首字母大写

NSRange range = [str1 rangeOfString:@"Hello"];//查找字符串某处是否包含其它字符,返回location和length。

NSLog(@"location: %lu,length:%lu",range.location,range.length);

NSString* substr = [str1 substringFromIndex:6];//从索引6开始取他的子字符串

BOOLyOrN = [str1 hasPrefix:@"Hello"];//判断是否有这个字符串前缀

NSString* format=[NSString stringWithFormat:@"[%d,
%d]",100,200];//格式化字符串方法
</pre></code>


NSString恒定机制

<pre><code>[str1 stringByAppendingString:@" Yes!"];

//伪更改,不会去真正的更改str1所指的堆上的值本身,只会返回一个新值,

str1=[str1 stringByAppendingString:@" Yes!"];

//所以要重新给str1赋值。

</pre></code> 如果在st1的字符串后面再加一点字符串,这个字符串也不会更改,这是字符串的恒定性,无法改变字符串本身。

还有一个可变字符串 NSMutableString

NSMutableString具有可变性,它的值是可以被更改的,因此它也不具备共享性。

NSMutableString内存模型

注意:

它是NSString的子类
所以当用NSMutableString定义一个字符串空间mstr1
我们可以把mstr1赋值给NSString类型的str1
因为mstr1的值是NSMutableString类型,具有可变性,
所有当用appendString去更改mstr1的值时,str1的值也会跟着改变,
这就违反了NSString的恒定性。
所以针对这个就要用拷贝属性[copy str1]去赋值

<pre><code>
NSMutableString mustr3 =[NSMutableStringstringWithCapacity:100];//手动分配空间

[mustr3 appendString:@"Hello Objective"]; //增加字符串内容

[mustr3 insertString:@"-C" atIndex:mustr3.length];//在指定位置增加内容

[mustr3 setString:@"Hi Objective"];//重新设置字符串

NSRange replaceRange = NSMakeRange(0, 2);//取指定区间的字符串

[mustr3 replaceCharactersInRange:replaceRange withString:@"Hello"];//替换字符串

NSRange deleteRange = NSMakeRange(5, 10);//删除指定区间的字符串

[mustr3 deleteCharactersInRange:deleteRange];//删除字符串
</pre></code>

以上这些操作都会直接改变字符串的值
Sizeof(str1) 指的是指针的大小
Str1.length指的是字符串值的长度

集合类型

数组 Array

OC的数组被定义为一个class,和C的数组不同,
而当访问越界时,会报错。

数组的初始化有三种方法:

<pre><code>
NSArray *array1=[NSArray arrayWithObjects:@"Shanghai",@"Beijing",@"New York",@"Paris", nil];

//工厂方法,会返回一个新的值

NSArray *array2=[[NSArray alloc] initWithObjects:@"Shanghai",@"Beijing",@"New York",@"Paris", nil];

//初始化器方法

NSArray *array3=@[@"Shanghai",@"Beijing",@"New York",@"Paris"];

//字面量方法
</pre></code>

在工厂和初始化器最后的nil表示输入结束。

array里的元素必须是对象(NSObject的子类)
如果想在array里放值,那么必须用NSNumber封装成类对象再放入array
NSNumber *numberObject1 =[NSNumber numberWithInteger:number ];

NSInteger是整数值类型,跟随CPU架构变换长度

当用字面量初始化时要加@ ,用u结尾。
NSNumber *numberObject2=@300u;//使用字面常量初始
<pre><code>
Point point;

point.h=100;

point.v=200;

NSValue *pointObject= [NSValue value:&point withObjCType:@encode(Point)];

//使用NSValue将struct包装为对象 再放入数组
</pre></code>

如果想放空值得话,可以用下面的方法
NSNull *nsnull = [NSNull null];

数组

因为每一个数组元素的对象类型可以不一样,所以在取元素的时候,可能存在类型不安全

数组具有常量性:
数组的 长度元素指针 都不可以被更改(一但这个数组初始化完毕后,长度和元素指针全都不可以更改) 但是元素指针所指向的对象内部可以更改

可变数组 NSMutableArray

NSMutableArray是NSArray的子类
它的初始化方式也和NSArray一样

因为是可变数组,它可以执行以下操作

[muArray1 addObject:p5];在数组结尾增加一个元素
[muArray1 removeObjectAtIndex:2];移除指定索引上的元素
[muArray1 insertObject:p6 atIndex:1];在指定索引上插入元素
muArray1[0]=p7;替换指定索引的元素

和可变字符串一样,可变数组在初始化后,会分配一个缓存容量Capacity,一般大于实际元素数量,当实际容量大于Capacity时,Capacity会以两倍的方式增长。
最好的方法是在初始化初期就预估分配合理的空间给数组。
intcount=100; NSMutableArray*muArray2=[NSMutableArrayarrayWithCapacity:count];

在实际操作过程中,尽量避免使用removeObjectAtIndex:insertObject: atIndex:
因为会改变数组序列,涉及大量的内存拷贝操作,代价太大。

数组的遍历

<pre><code>
for( BLNPoint* point inarray5)

{

point.x++;

point.y++;

}

//快速遍历法 for-in 所有遍历方法中,速度最快,因为它直接访问内存,优化了索引检查。
</pre></code>如果不知道数组元素的类型的话,要写成id*point或者NSObject* point

<pre><code>NSEnumerator *enumerator = [array5 objectEnumerator];

BLNPoint* item;

while(item = [enumerator nextObject])

{

item.x++;

item.y++;

}

//迭代枚举法,相比于for-in会慢一点
</pre></code>array5 上面会有一个 objectEnumerator 方法得到一个NSEnumerator对象类型,那这个对象类型去调用 nextObject 方法 ,再把返回的值赋给 用NSEnumerator 创建的对象 item,当没有元素的时候,他的值就是nil,也就不会再继续while循环。

<pre><code>for(inti=0; i<array5.count; i++)

{

NSLog(@"array5[%d],%@",i,array5[i]);

}

for循环遍历,最慢的方法。
</pre></code>

所有遍历方法中,推荐使用for-in。

数组查找:
<pre><code>BLNPoint* target=[[BLNPointalloc] initWithX:33WithY:63];

NSUIntegerindex1=[array5 indexOfObject:target];

// 在 array5 中查找是否有和 tearget 值相同的元素,并且返回索引

NSUIntegerindex2=[array5 indexOfObjectIdenticalTo:p3];

// 这个方法只能用数组内的元素名查找,并返回索引

NSLog(@"find at%lu", index1);

NSLog(@"find at
%lu", index2);
</pre></code>

数组排序:

<pre><code>NSArray* sortArray1=[array1 sortedArrayUsingSelector:@selector(compare:)];</pre></code>我们指定了一个方法 compare: 它可以实现数组里元素的比较
因为无法改变原数组,所以要取它的返回值给 sortArray1
它是通过返回一个新数组来表达排序的结果

set集合

NSSet和数组不一样,是一个无序集合,存储的对象不能重复
被定义为class ,引用类型,拷贝时有引用语句
有常量集合NSSet,可变集合NSMutableSet,和数组一样。

初始化方式也和数组类似
<pre><code>NSSet*set1 =

[NSSetsetWithObjects:@"Shanghai",@"Beijing",@"New York",@"Paris", nil];

NSMutableSet*set2 =

[NSMutableSetsetWithObjects:@"Shanghai",@"Beijing",@"New York",@"Paris", nil];

</pre></code>
Capacity set容量⬇️
<pre><code>int count=100;

NSMutableSet*muArray2=[NSMutableSetinitWithCapacity:count];
</pre></code>

set的一下操作
<pre><code>
[set2 addObject:@"London"];//加元素

[set2 removeObject:@"Beijing"];//移除元素

NSLog(@"set2 count:%lu", set2.count);

</pre></code>

<pre><code>
if([set2 containsObject:@"Shanghai"])//判断是否包含某个对象
</pre></code>

字典

Dictionary 字典 是一个存储Key-Value的无序集合
Key唯一,value可重复
ArraySet 一样,
有常量字典NSDictionary 和可变字典NSMutableDictionary

初始化方式也和数组差不多

字面常量初始化:
valueKey之前,中间用:隔开<pre><code>
NSDictionary *dictionary1 = @{

                            @"Shanghai": p1,             
                            @"Beijing": p2,               
                            @"NewYork" : p3,         
                            @"Paris": p4 };

</pre></code>

工厂方法:
Valuekey之后,中间用" ,"隔开
<pre><code>
NSMutableDictionary*dictionary2 =

[NSMutableDictionarydictionaryWithObjectsAndKeys:

                             p1,@"Shanghai",             
                             p2,@"Beijing",   
                             p3,@"New York",
                             p4,@"Paris",
                             nil];

</pre></code>

<pre><code>
BLNPoint* result1=[dictionary1 objectForKey:@"Beijing"];

BLNPoint* result2=dictionary1[@"Shanghai"];
</pre></code>可以用这种方式查询Key来得到对应的value

** tip **:所有的可变集合都是其对应的一个常量集合的子类。

自动引用计数ARC

ARC 是OC的默认内存管理机制,针对堆上的对象,由编译器自动生成操作ARC指令(retinarelease)来管理对象的创建和释放。
retinarelease在后期的高级编程内容里会深入了解

受ARC管理的有:

  • OC的对象指针
  • Block 指针
  • Attribute((NSObject))定义的typedef

不受ARC管理的有:

  • 值类型(如C语言的结构)
  • 使用其他方式分配的堆对象(如malloc)
  • 非内存资源

实际上,指针本身不受ARC管理,因为ARC管理的是堆上的对象,而指针是存放在栈上的。所有ARC管理的实际上是指针所指向的那个对象。
而值类型的对象也是存放在栈上,所以也不受ARC管理。

引用计数管理

引用计数+1的操作 (retain操作)
当将对象执行对其他对象的

  • 赋值
  • 传参
  • 加入集合操作

引用计数-1 (release操作)

  • 全局(局部)变量被赋值为nil 或其他值
  • 属性被赋值为nil或其他值
  • 将对象从集合中删除
引用计数

自动释放池 AutoRelease Pool

release 会导致对象立即释放,如果频繁的release,会造成琐碎的内存管理负担。这时候,用autorelease可以将release的调用推迟到autorelease被释放时。

AppkitUIkit框架在每一次事件迭代时,都会将这个代码放入autoreleasepool中。大多数情况都不需要人为干预。

main函数内,编译器都会在开始加上一个autorelease的块当程序运行到autorelease结束时,所有引用了release的对象都会被立即释放

<pre><code>
int main(int argc, const char * argv[]) {

                 @autoreleasepool{ 
                           NSLog(@"-------ARC Demo: ----------");
                           arcDemo();
                   //当函数内执行完后,会引用release,然后这个释放信号会暂存在autorelease内   
                           NSLog(@"-------Autorelease Pool Demo: ----------");    
                           poolDemo();
                  }
                   //当这个autorelease执行完后,就会被释放,随之存在其中的release也会被立即释放。
      return0;

}
</pre></code>每一个事件处理,都是一个autoreleasepool的建立和释放

需要手工管理autorelease pool的情况

  1. 编写的程序不基于UI框架(如命令行程序)
  • 在循环中创建了大量临时变量,需要提早释放,避免临时对象聚集而导致内存峰值过高。
  • 在主线程之外创建了新的线程,需要自己手工添加autorelease pool 块
  • 可以嵌套使用

<pre><code>
void poolDemo(){

@autoreleasepool{  
        for (int i = 0; i < 10; i++) {
              __unused  BLNRectangle *rect = [[BLNRectangle alloc]init];
        }
     }

}
</pre></code>

__unused 修饰符,通知编译器,如果这个变量未被使用就不参与编译(消除黄色警告团)

协议protocol

协议是一种约定,它只提供外部描述,不提供具体实现,所以只在.h文件内写,不用去写.m文件

协议里可以放

  1. 属性
  • 实例方法
  • 类方法
  • 初始化器和析构器(一般不放)

但是不能放实例变量。编译器会自动生成setter和getter方法,但是不会合成实例变量。

定义协议

<pre><code>@protocol Drawable

//内容

@end
</pre></code>

使用协议

<pre><code>
@interface BLNPoint : NSObject<Drawable>//把协议名放中括号里

//内容

@end
</pre></code>

** 必选协议required **
** 可选协议optional **

协议里的成员默认都是@required的,当一个类遵守协议时,必须要实现协议内的所有成员。.h文件里不用去再次声明协议内的方法,只需要在.m文件内实现就可以了。
** 但是属性必须再次声明 **。如果不声明的话,就没法自动合成实例变量。
如果要自己写一个@required的话,那个在这个@required后面的成员就是必选成员了
与之相对的@optional表示的是可选协议。他之后的成员会被认为成可选协议

如果遵守协议但是没有去实现协议方法,会出现警告运行时就会报错。
协议类型变量被赋值非协议类型对象时,会出现警告
比如这样赋值就会报错process1(@"x");process1是协议类型的函数

协议可以作为变量声明类型,但是不能创建实例void process1(id<Drawable> obj)

有时不确定这个对象是否遵守了协议,那么可以用这段代码来验证这个对象背后的类型是否遵守了协议。
<pre><code>
void process2(id obj){

        if([obj conformsToProtocol:@protocol(AProtocol) ]) {
        [obj methodA]; 

}
</pre></code>

一个协议也可以继承多个协议。

实现子协议的类型,也必须实现父类协议的成员。

继承协议

也可以遵守多个协议
<pre><code>
@interface ClassC : NSObject<AProtocol,CProtocol>

@end
</pre></code>

常用的协议

NSObject:包含对象的常用操作,相等、字符串表示、哈希。
NSCopying:支持复制的类型必须遵守该协议。
NSMutableCopying:在NSCopying的基础上,支持复制数据的可变性。
NSFastEnumeration:实现快速美剧for-in的类型采用该协议。
NSCoding:支持将对象图进行编码/解码以支持对象序列化

类别与扩展

类别category

类别就不知道源代码的情况下,想给原有的类再添加了一些成员,并去实现他们

定义类

@interface BLNPoint : NSObject

定义类别

@interface BLNPoint(Drawing)

** tip **:category的实现文件不写在class的实现文件内,而是写在category的实现文件内

命名规范:

文件名 == class名+category

category可以添加的成员

  1. 类方法
  • 实例方法
  • 重写基类方法

category不能添加的成员

  1. 属性
  2. 实例变量
  3. 已存在的同名方法(已经被定义过的)

加了属性的话,编译能通过,但是在运行的时候就会报错(坑爹啊这是)
不能加属性,实际上是因为不能加属性背后的那个实例变量。
虽然不能加属性,但是可以去定义一个setter和一个getter访问器方法。
-(void)setWeight:(NSInteger)weight;
-(NSInteger)weight;
调用class内的实例变量

适用场景:
  1. 在没有源代码的情况下,向已经封装的类里添加方法
  2. 在一些特殊场合下
  • 对复杂的大型文件分割实现

都一下位置可以添加category

  • 自己创建的
  • 系统的
  • 第三方库

扩展extension

category最大的区别在于,这是在有class的源代码的情况下在class内添加,可以看做是一个没有名字的class,不用定义在 .h文件,直接在需要扩展的.m文件内定义.
<pre><code>
@interface Circle ()//加个小括号,写在.m 文件里

{

NSString* _name;

}

@property (readwrite )NSInteger radius;//修改读写属性

@property
NSIntegercenter;//添加属性

-(float)getDiameter;//实例方法

+(void)process:(Circle*) circle;//类方法

@end
</pre></code>
如果在主接口定义了文件的读写属性,那么在扩展内可以更改读写属性,
有一点需要注意的是,只能往更高的权限更高,** 不能把权限改低。 **

注意:

最重要的一点区别,扩展的成员只针对类(那个 .m文件)内可以访问,在类外不能访问扩展!

扩展的主要用途就是信息隐藏,可以把一些外部无需访问,但是类内又要用到的成员私有化。

类的主接口用于 ** 对外公开访问 **
类的扩展用于 ** 对内访问 **

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,924评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,781评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,813评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,264评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,273评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,383评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,800评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,482评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,673评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,497评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,545评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,240评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,802评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,866评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,101评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,673评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,245评论 2 341

推荐阅读更多精彩内容

  • @字符串处理 NSString是一个Unicode编码,16位的字符序列,是类,引用类型。 初始化方法有字面常量初...
    5君阅读 697评论 0 51
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,117评论 29 470
  • 37.cocoa内存管理规则 1)当你使用new,alloc或copy方法创建一个对象时,该对象的保留计数器值为1...
    如风家的秘密阅读 827评论 0 4
  • 多线程、特别是NSOperation 和 GCD 的内部原理。运行时机制的原理和运用场景。SDWebImage的原...
    LZM轮回阅读 2,001评论 0 12
  • iOS面试小贴士 ———————————————回答好下面的足够了------------------------...
    不言不爱阅读 1,955评论 0 7