背记知识点

  1. OC中,与alloc语义相反的方法是dealloc还是release?与retain语义相反的方法是dealloc还是release?为什么?需要与alloc配对使用的方法是dealloc还是release,为什么?

    以下是针对MRC(手动内存释放)模式:
    与alloc语义相反的方法是dealloc,与retain语义相反的方法是release。
    alloc是为对象在内存中开辟空间,而dealloc则是对象销毁时释放空间。
    retain方法是对象开辟空间以后使对象的引用计数器加1,而release是对象的引用计数器减1。
    需要与alloc配对的方法是release,因为对象创建以后,对象的引用计数器自动加1,
    而调用release方法后,对象的引用计数器归0,系统会自动调用dealloc方法释放空间。

  2. 在一个对象的方法里面:self.name = @”object”;和 _name = @”object”有什么不同吗?

    self.name = @”object”; 是通过点语法修改属性name的值。
    本质上使用的是name属性的setter方法进行的赋值操作,实际上执行的代码是

    [self setName:@”object”];

    例如:
    @property(nonatomic, strong) NSString *name;
    //根据@property关键词,系统自动生成setter方法。

    • (void)setName:(NSString *)name{
      //根据strong关键词
      [name retain]; //内存计数+1
      [_name release]; //把之前指针指向的内容内存计数-1
      _name = name; //指向新内容
      }

    _name = @“object”; 只是单纯的把‘_name’指针指向‘@”object”’字符串对象所在的地址,没有调用方法。

  3. 这段代码有什么问题吗?

-(void)setAge:(int)newAge{
self.age = newAge;
}
在age属性的setter方法中,不能通过点语法给该属性赋值。
会造成setter方法的循环调用。因为self.age = newAge;
本质上是在调用 [self setAge:newAge]; 方法。
解决循环调用的方法是方法体修改为 _age = newAge;

另外 变量名称不能使用new开头!

  1. 简要叙述面向对象的特点,特别是多态。

  2. 封装
    封装是对象和类概念的主要特性。它是隐藏内部实现,提供外部接口,可以看作是“包装”。
    封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,
    对不可信的进行信息隐藏。
    封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,
    以特定的访问权限来使用类的成员。
    好处:可以隐藏内部实现细节。通过大量功能类封装,加快后期开发速度。

  3. 继承
    面向对象编程 (OOP) 语言的一个主要功能就是“继承”。
    继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下
    对这些功能进行扩展。
    通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。
    继承的过程,就是从一般到特殊的过程。在考虑使用继承时,有一点需要注意,
    那就是两个类之间的关系应该是“属于”关系。
    例如,Employee(雇员)是一个人,Manager(领导)也是一个人,因此这两个类都可以继承Person类。
    但是 Leg(腿) 类却不能继承 Person 类,因为腿并不是一个人。

  4. 多态
    多态性(polymorphism)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,
    赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。
    简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
    不同对象以自己的方式响应相同的消息的能力叫做多态。
    意思就是假设生物类(life)都用有一个相同的 方法-eat;那人类属于生物,猪也属于生物,
    都继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法。
    也就是不同的对象以 自己的方式响应了相同的消息(响应了eat这个选择器)。
    实现多态,有二种方式,覆盖,重载。
    • 覆盖(override),是指子类重新定义父类的虚函数的做法。
    • 重载(overload),是指允许存在多个同名函数,而这些函数的参数表不同
    (或许参数个数不同,或许参数类型不同,或许两者都不同)。
    ** 这里注意:OC没有重载,因为OC只认函数名,不认参数类型。OC不允许存在多个同名函数。

总结:封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);
它们的目的都是为了——代码重用。
而多态则是为了实现另一个目的——接口重用!
多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。
在类层次中,子类只继承一个父类的数据结构和方法,则称为单重继承。
在类层次中,子类继承了多个父类的数据结构和方法,则称为多重继承。

  1. objective-c 所有对象间的交互是如何实现的?

在对象间交互中每个对象承担的角色不同,
但总的来说无非就是”数据的发送者”或”数据的接收者”两种角色。
消息的正向传递比较简单,直接拿到接受者的指针即可。
消息的反响传递可以通过委托模式,观察者模式(本质是单例模式),block语法,AppDelegagte来实现。
其中委托模式,block语法都是1对1的消息传递。 观察者模式是1对多。
AppDelegate比较特殊,这是一个生命周期与进程一致的对象。

8.数据结构:

数据结构是计算机存储、组织数据的方式.数据结构是指相互之间存在一种或多种特定关系的数据元素的集合.通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率.数据结构往往同高效的检索算法和索引技术有关.

9、 OC的类可以多继承吗?可以实现多个接口吗?Category是什么?分类中能定义成员变量或属性吗?为什么?重写一个类的方式是继承好还是类别好?为什么?

Object-c的类不可以多重继承;可以实现多个接口(协议),通过实现多个接口可以完成C++的多重继承;
Category是类别,推荐使用类别,用Category去重写类的方法,仅对本引入Category的类有效,
不会影响到其他类与原有类的关系。

Category(类别、分类)是Objective-C中特有的一个语法,可以给一个现有的类增加新方法或者重写现有方法,而不需要继承或者修改该类,并且这些方法能够被所有的子类继承。类别有如下一些用处:

将一个类的定义和实现拆分为多个部分,甚至分散到多个文件中,可以让这个类的维护变得更加简单。
当需要扩展的类实际上是一个类簇时,如果希望给类簇中的所有子类都增加功能,则只能使用类别。比如NSString。
创建类的私有API的声明。
如果类别中实现了一个已经存在的方法,会影响到所有调用这个方法的地方,并且不能再调用原始的方法实现。更加遗憾的是以后我们将没有一种可靠的方法来再次重写这个新的实现。

对 于方法的重写,无论是使用继承还是类别,都没有标准答案,必须根据具体情况具体分析。如果希望影响所有使用这个类或者子类的地方,考虑使用类别。如果只是 想在某些位置使用新功能,考虑使用继承。继承时重写的方法只会影响新创建的类型。不过考虑到上面的第三点,慎用类别的方法重写功能。

10、 #import和#include有什么区别? @class呢? #import<>和
#import”“有什么区别? #include
区分 #include

import 大部分功能和#include是一样的,但 是他处理了 重复引用的问题,我们在引用文 件的时候不用再去自己

进行重复引用处理. @class
主要是用于声明一个类,告诉编译器它后面 的名字是一 个类的名字,而这个类的定义实 现是暂时不用知道的,后 面会告诉你.也是因 为在@class仅仅只是声明一个类,所 以在 后面的实现文件里面是需要去#import这个 类,这时
候才包含了这个被引用的类的所有信息。 综上所述#include,#import与@class的区别 可以做一下
理解:
#include与#import在引用一个类的时候会 包含这个类的 所有信息包括变量方法等,但 是这样做会对编译效率造 成影响.比如有 100个类都#import了ClassA,那么在编译 的 时候这100个类都会去对ClassA处理.又比 如A被B引 用,B被C引用,C被D引用…..此时 如果A被修改,那么后面 的B,C,D…..都需要 重新进行编译.还有一个用法会引起 编译错 误的就是在ClassA中#import ClassB 在 ClassB 中#import ClassA那么在编译的时 候也会出现未知错
误。
所以一般来说,在 interface 中引用一个类, 就用@class, 它会把这个类作为一个类型 来使用,而在实现这个 interface 的文件中, 如果需要引用这个类的实体变量或
者方法之类的,还是需要 import 这个在@class 中声明的类。
注意:include 语 之后 需要加”;”(因为#include它
使 个预处 指令, 是 个语 )
#include和#import都可以 在ObjC程序中导 头 件, 但是 #include是C中的。在C中,当多个 件都导 同 个头 件时,
需要按如下 式进 定义:
#ifndef HelloObjective_C_Header_h #define HelloObjective_C_Header_h
#endi f
这样才能避免 个 件中重复导 个头 件,造成程序效 率地下。 #import是Objective-C才有的,跟#include导 头 件 的差别在于通 过#import导 头 件时会 动判断头 件 是否已经被导 过, 必
要像C中 样还要进 宏判断来确认是否已经被导 过! 知识点、
1、请问头 件中的 ifndef/define/endif 么 ?
在 个 的软件 程 ,可能会有多个 件同时包含 个头 件,
#include 指令 仅仅限于.h头 件,可以包含任何 编译 能识别 的C/C++代码 件,包
括.c、.hpp、.cpp等,甚 .txt、.abc等等 都可以

当这
些 件编译链接成 个可执 件时,就会出现 重定义的错 误。在头
件中实 #ifndef #define #endif能避免头 件的重定义。
法: 如要编写头 件test.h 在头 件开头写上两 :
#ifndef _TEST_H
#define _TEST_H// 般是 件名的 写
#endif
这样 个 程 件 同时包含两个test.h时,就 会出现重定义 的错误

分析:当第 次包含test.h时,由于没有定义_TEST_H, 条件为真,这样 就会包含(执 )#ifndef _TEST_H和 #endif之间的代码;当第 次包含 test.h时前 次已经定义 _TEST_H,条件为假,#ifndef _TEST_H和 #endif之间 的代码也就 会再次被包含,这样就避免 重定义 。主要
于防 重复定义宏和重复包含头 件
#import是Objective-C导 头 件的关键 字, #include是C/C++导 头 件的关键 字, 使 #import头 件会 动只导 次, 会重 复导 ,相当于 #include和
#pragma once; @class告诉编译 某个 类的声明, 当执 时,才去查看类的实现 件, 可以解决头 件的相互 包含; #import<> 来包含系统的头 件, #import”” 来包含 户头 件。 如:
/* 如果这 写@class,则报错。 原因是 找 到MyVC的 定义。因为 代码执 顺序是由 上 下的。 当声明协议MyVCDelegate时, MyVC还没有声明。 使 @class 名称随 写, 管是否存在。 可以 代码尝试 随意 写 个@class 如:
@class Hello;
就算项 中根本没有Hello类,这也不会报 错。 因为只 有当项 运 起来,才会真的去 检查Hello类。就算没有 这个类名,项目 也可以正常运行。在h和m中都可以声 明, 都不会报错。
是否声明 。
*/
@class MyVC;
@protocol MyVCDelegate
- (void)myVC:(MyVC *)myVC click: (id)sender;
@end
@interface MyVC : BaseVC
@end ps:iOS7之后的新特性,可以使@import
关键词来代 #import引 系统类库。
使 @import引 系统类库, 需要到build phases中先 添加添 加系统库到项 中。

11、 属性readwrite, readonly, assign, retain, copy, nonatomic各是什么作用?在哪种情况下用?

1.readwrite 是可读可写特性;需要生成getter方法和setter方法时
(补充:默认属性,将生成不带额外参数的getter和setter方法(setter方法只有一个参数))
2.readonly 是只读特性,只会生成getter方法,不会生成setter方法;不希望属性在类外改变
3.assign 是赋值特性,setter方法将传入参数赋值给实例变量;仅设置变量时;
4.retain(MRC)/strong(ARC) 表示持有特性,setter方法将传入参数先保留,
再赋值,传入参数的retaincount会+1;
5.copy 表示拷贝特性,setter方法将传入对象复制一份;需要完全一份新的变量时。
6.nonatomic 非原子操作,决定编译器生成的setter和getter方法是否是原子操作。
- atomic表示多线程安全,需要对方法加锁,保证同一时间只有一个线程访问属性,
因为有等待过程,所以影响执行效率
- 一般使用nonatomic。不加锁。效率会更高。但是线程不安全。

NSString属性什么时候用copy,什么时候用strong?
http://www.cocoachina.com/ios/20150512/11805.html

12、写一个setter方法用于完成@property(nonatomic, strong)NSString *name, 写一个setter方法用于完成@property(nonatomic, copy)NSString *name

– (void)setName:(NSString*)str //retain
 {
 if(_name != str){
  [_name release];
  _name = [str retain];
}
 }
– (void)setName:(NSString *)str //copy
 {
 if(_name != str){
  [_name release];
  _name = [str copy];
}
 }

13、 对于语句NSString *obj = [[NSData alloc] init]; obj在编译时和运行时分别是什么类型的对象?

编译时是NSString的类型;运行时是NSData类型的对象。

首先,声明 NSString *testObject 是告诉编译器,testObject是一个指向某个Objective-C对象的指针。因为不管指向的是什么类型的对象,一个指针所占的内存空间都是固定的,所以这里声明成任何类型的对象,最终生成的可执行代码都是没有区别的。这里限定了NSString只不过是告诉编译器,请把testObject当做一个NSString来检查,如果后面调用了非NSString的方法,会产生警告。

接着,你创建了一个NSData对象,然后把这个对象所在的内存地址保存在testObject里。那么运行时,testObject指向的内存空间就是一个NSData对象。你可以把testObject当做一个NSData对象来用。

编译时
编译时顾名思义就是正在编译的时候.那啥叫编译呢?就是编译器帮你把源代码翻译成机器能识别的代码.(当然只是一般意义上这么说,实际上可能只是翻译成某个中间状态的语言.比如Java只有JVM识别的字节码,C#中只有CLR能识别的MSIL.另外还有啥链接器.汇编器.为了了便于理解我们可以统称为编译器)
那编译时就是简单的作一些翻译工作,比如检查老兄你有没有粗心写错啥关键字了啊.有啥词法分析,语法分析之类的过程.就像个老师检查学生的作文中有没有错别字和病句一样.如果发现啥错误编译器就告诉你.如果你用微软的VS的话,点下build.那就开始编译,如果下面有errors或者warning信息,那都是编译器检查出来的.所谓这时的错误就叫编译时错误,这个过程中做的啥类型检查也就叫编译时类型检查,或静态类型检查(所谓静态嘛就是没把真把代码放内存中运行起来,而只是把代码当作文本来扫描下).所以有时一些人说编译时还分配内存啥的肯定是错误的说法.

运行时
所谓运行时就是代码跑起来了.被装载到内存中去了.(你的代码保存在磁盘上没装入内存之前是个死家伙.只有跑到内存中才变成活的).而运行时类型检查就与前面讲的编译时类型检查(或者静态类型检查)不一样.不是简单的扫描代码.而是在内存中做些操作,做些判断.

5、这段代码有什么问题,如何修改?

for(int i = 0; i < someLargeNumber; i++){
NSString *string = @“Abc”;
string = [string lowercaseString];
string = [string stringByAppendingString:@“xyz”];
NSLog(@“%@“, string);
}

这段代码在ARC下,内存是否添加@autoreleasepool{}没有区别,系统会自动管理内存。但是在MRC下,就需要加@autoreleasepool{},一定要添加到循环里面,不能添加到循环外面,否者和没添加是一样的。

13、 AutoReleasePool 原理

原理:

    Autorelease Pool全名叫做NSAutoreleasePool,是OC中的一个类。autorelease pool并不是天生就有的,你需要手动的去创建它。一般地,在新建一个iphone项目的时候,xcode会自动地为你创建一个Autorelease Pool,这个pool就写在Main函数里面。在NSAutoreleasePool中包含了一个可变数组,用来存储被声明为autorelease的对象。当NSAutoreleasePool自身被销毁的时候,它会遍历这个数组,release数组中的每一个成员(注意,这里只是release,并没有直接销毁对象)。若成员的retain count 大于1,那么对象没有被销毁,造成内存泄露。默认的NSAutoreleasePool 只有一个,你可以在你的程序中创建NSAutoreleasePool,被标记为autorelease的对象会跟最近的NSAutoreleasePool匹配。可以嵌套使用NSAutoreleasePool。

缺点:

    即使NSAutoreleasePool看起来没有手动release那么繁琐,但是使用NSAutoreleasePool来管理内存的方法还是不推荐的。因为在一个NSAutoreleasePool里面,如果有大量对象被标记为autorelease,在程序运行的时候,内存会剧增,直到NSAutoreleasePool被销毁的时候才会释放。如果其中的对象足够的多,在运行过程中你可能会收到系统的低内存警告,或者直接crash。

扩展:

    如果你极具好奇心,把Main函数中的NSAutoreleasePool代码删除掉,然后再自己的代码中把对象声明为autorelease,你会发现系统并不会给你发出错误信息或者警告。用内存检测工具去检测内存的话,你可能会惊奇的发现你的对象仍然被销毁了。其实在新生成一个Run Loop的时候,系统会自动的创建一个NSAutoreleasePool,该NSAutoreleasePool无法被删除。

实例:

NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
ClassA *a=[[[ClassA alloc] init] autorelease];
ClassB *b=[[[ClassB alloc] init] autorelease];
ClassC *c=[[[ClassC alloc] init] autorelease];

[pool release];

14、常见的OC的数据类型有哪些? 和C的基本数据类型有什么区别?

OC中的数据类型有NSString,NSNumber,NSArray,NSMutablearray,NSData等,这些都是class,在创建之后便是对象,而C语言的基本数据类型比如int,只是一定字节的内存空间,用于存储数据。NSInterger是基本数据类型,并不是NSNumber的子类,也不是NSObject的子类。NSInterger是基本数据类型int或者long的别名,它的区别在不NSInteger会根据系统是32还是64为来决定本身是int还是long类型。

NSInteger类型的定义是

#if LP64 || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE)
|| TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif

可以看到,在64位操作系统上,NSInteger是 C语言的long类型。
在32位操作系统上,则是int类型。

15、id声明的对象有什么特性?

id 声明的对象具有运行时的特性,即可以指向任意类型的objcetive-c的对象;
可以作为返回值,也可以声明对象。
例如
- (id)initWithName:(NSString *)name;
id obj = [NSObject new];

现在我们使用苹果推荐使用的“instancetype”类型代替id类型作为返回值
- (instancetype)initWithName:(NSString *)name;

instancetype和id的区别在于, id可以声明对象 也可以作为返回值,
instancetype只能作为返回值。

在iOS开发中经常遇到instancetype和使用id的区别问题,比如:

  • (instancetype)initWithFrame:(CGRect)frame;
  • (id)initWithFrame:(CGRect)frame;

这个方法最直观的区别就是返回类型不一样,一个是instancetype,另一个是id

http://blog.sina.com.cn/s/blog_139cc81e90102vvw3.html

http://blog.csdn.net/kuizhang1/article/details/18048829

17、你对@interface中的成员变量和@property声明的属性的理解。

@interface AA: NSObject{
NSString *_name; //成员变量
}
@property NSString *sex; //属性

如上所示:
属性拥有setter和getter方法 外加_sex成员变量。
_name只是成员变量, 没有setter和getter方法。

18、 do while 和while do的区别?

while do是先判断while中的表达式的真假,再执行循环。
do while先进行循环一次,再判断while中的表达式的真假。

19、用预处理指令#define声明一个常数,用以表明一年中有多少秒(忽略闰年问题)。

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)

20、浅拷贝和深拷贝的区别?

浅拷贝:

Making a shallow copy:

NSArray *shallowCopyArray = [someArray copyWithZone:nil];

NSDictionary *shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:NO];

Deep Copies:这两种方法

If you create a deep copy of a collection in this way, each object in the collection is sent a copyWithZone: message. If the objects in the collection have adopted the NSCopying protocol, the objects are deeply copied to the new collection, which is then the sole owner of the copied objects. If the objects do not adopt the NSCopying protocol, attempting to copy them in such a way results in a runtime error. However, copyWithZone: produces a shallow copy.

Making a deep copy:

NSArray *deepCopyArray=[[NSArray alloc] initWithArray:someArray copyItems:YES];

A true deep copy:

NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

当然在ios中并不是所有的对象都支持copy,mutableCopy,遵守NSCopying 协议的类可以发送copy消息,遵守NSMutableCopying 协议的类才可以发送mutableCopy消息。假如发送了一个没有遵守上诉两协议而发送 copy或者 mutableCopy,那么就会发生异常。但是默认的ios类并没有遵守这两个协议。如果想自定义一下copy 那么就必须遵守NSCopying,并且实现 copyWithZone: 方法,如果想自定义一下mutableCopy 那么就必须遵守NSMutableCopying,并且实现 mutableCopyWithZone: 方法。

id temp = [array objectAtIndex:0];
  [array removeObjectAtIndex:0];
  如果你再要使用temp就会出错,因为这个时候obj已经被释放了。

由于在程序中经常会遇到集合类的传值,所以,简单的retain未必够用,需要对集合内容的拷贝,也就是深拷贝。

1、 系统的非容器类对象:

  这里指的是NSString,NSNumber等等一类的对象。
  NSString *string = @”origion”;
  NSString *stringCopy = [string copy];
  NSMutableString *stringMCopy = [string mutableCopy];
  
再看下面的例子:

NSMutableString *string = [NSMutableString stringWithString: @"origion"];
NSString *stringCopy = [string copy];
NSMutableString *mStringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];
[mStringCopy appendString:@"mm"];//error
[string appendString:@" origion!"];
[stringMCopy appendString:@"!!"];

以上四个NSString对象所分配的内存都是不一样的。但是对于mStringCopy其实是个imutable对象,所以上述会报错。
对于系统的非容器类对象,我们可以认为,如果对一不可变对象复制,copy是指针复制(浅拷贝)和mutableCopy就是对象复制(深拷贝)。如果是对可变对象复制,都是深拷贝,但是copy返回的对象是不可变的。

2、 系统的容器类对象

指NSArray,NSDictionary等。对于容器类本身,上面讨论的结论也是适用的,需要探讨的是复制后容器内对象的变化。
//copy返回不可变对象,mutablecopy返回可变对象
NSArray *array1 = [NSArray arrayWithObjects:@”a”,@”b”,@”c”,nil];
NSArray *arrayCopy1 = [array1 copy];
//arrayCopy1是和array同一个NSArray对象(指向相同的对象),包括array里面的元素也是指向相同的指针。

NSMutableArray *mArrayCopy1 = [array1 mutableCopy];
//mArrayCopy1是array1的可变副本,指向的对象和array1不同,但是其中的元素和array1中的元素指向的是同一个对象。mArrayCopy1还可以修改自己的对象

[mArrayCopy1 addObject:@”de”];
[mArrayCopy1 removeObjectAtIndex:0];
array1和arrayCopy1是指针复制,而mArrayCopy1是对象复制,mArrayCopy1还可以改变期内的元素:删除或添加。但是注意的是,容器内的元素内容都是指针复制。
下面用另一个例子来测试一下。

NSArray *mArray1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *mArrayCopy2 = [mArray1 copy];

NSMutableArray *mArrayMCopy1 = [mArray1 mutableCopy];
//mArrayCopy2,mArrayMCopy1和mArray1指向的都是不一样的对象,但是其中的元素都是一样的对象——同一个指针
//一下做测试
NSMutableString *testString = [mArray1 objectAtIndex:0];
//testString = @"1a1";//这样会改变testString的指针,其实是将@“1a1”临时对象赋给了testString

[testString appendString:@” tail”];//这样以上三个数组的首元素都被改变了。

但是里面的元素指针地址都是一样的,无论是哪种拷贝。里面的元素都是浅复制。下面两个方法才是深复制,第二个才是真正意义的深复制。

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Collections/Articles/Copying.html

NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"],[NSString stringWithString:@"b"],@"c",nil];
NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES];
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject: array]];

trueDeepCopyArray是完全意义上的深拷贝,而deepCopyArray则不是,对于deepCopyArray内的不可变元素其还是指针复制。或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。

拷贝构造:

当然在 ios 中并不是所有的对象都支持copy,mutableCopy,遵守NSCopying协议的类可以发送copy消息,遵守NSMutableCopying协议的类才可以发送mutableCopy消息。

假如发送了一个没有遵守上诉两协议而发送copy或者 mutableCopy,那么就会发生异常。但是默认的ios类并没有遵守这两个协议。如果想自定义一下copy 那么就必须遵守NSCopying,并且实现 copyWithZone: 方法,如果想自定义一下mutableCopy 那么就必须遵守NSMutableCopying,并且实现 mutableCopyWithZone: 方法。

如果是我们定义的对象,那么我们自己要实现NSCopying , NSMutableCopying这样就能调用copy和mutablecopy了。举个例子:

@interface MyObj : NSObject《NSCopying, NSMutableCopying》{
NSMutableString *_name;
NSString * _imutableStr ;
int _age;
}
@property (nonatomic, retain) NSMutableString *name;
@property (nonatomic, retain) NSString *imutableStr;
@property (nonatomic) int age;
copy拷贝构造:

– (id)copyWithZone:(NSZone *)zone{
MyObj *copy = [[[self class] allocWithZone :zone] init];
copy->name = [_name copy];
copy->imutableStr = [_imutableStr copy];
copy->age = age;
return copy;
}
mutableCopy拷贝构造:

– (id)mutableCopyWithZone:(NSZone *)zone{
MyObj *copy = NSCopyObject(self, 0, zone);
copy->name = [_name mutableCopy];
copy->age = age;
return copy;
}

http://www.jianshu.com/p/e6a7cdcc705d

注意点:
(1)当使用mutableCopy时,不管源对象是否可变,副本是可变的,并且实现真正意义上的拷贝。
当我们使用copy一个可变对象时,副本对象是不可变的。

(2)要想实现对象的自定义拷贝,必须实现NSCopying,NSMutableCopying协议,实现该协议的copyWithZone方法和mutableCopyWithZone方法。深拷贝和浅拷贝的区别就在于copyWithZone方法的实现。

字符串之所以用copy是因为:
NSMutableString *string1 = [NSMutableString stringWithFormat:@”大家好”];

FatherClass *father = [FatherClass new];
father.name = string1;

// 不能改变person.name的值,因为其内部copy新的对象
[string1 appendString:@” 哈哈”];

如果是copy,则father.name = @”大家好”;string1 = @”大家好哈哈”。如果是strong,father.name = string1= @”大家好哈哈” ;

22、类别的作用?继承和类别在实现中有何区别?

类别主要有3个作用:

(1)将类的实现分散到多个不同文件或多个不同框架中。

(2)创建对私有方法的前向引用。

(3)向对象添加非正式协议。

category可以在不获悉,不改变原来代码的情况下往里面添加新的方法,只能添加,不能删除修改。并且如果类别和原来类中的方法产生名称冲突,则类别将覆盖原来的方法,因为类别具有更高的优先级。
继承可以增加,修改或者删除方法,并且可以增加属性。

还有就是如果使用类别,声明了相同的方法名,则会对原来的类有影响,如果是继承,就算用相同的方法名,但是如果使用父类,不会影响到原来的方法。

类扩展 (Class Extension也有人称为匿名分类)

作用:

能为某个类附加额外的属性,成员变量,方法声明
一般的类扩展写到.m文件中
一般的私有属性写到类扩展

使用格式:
@interface Mitchell()
//属性
//方法
@end

可以在类别的.h中声明属性,但是分类只会生成这个属性的set、get方法声明,也就是不会有实现。当调用这个属性并赋值时,程序会崩溃。

//一般的时候,Extension都是放在.m文件中@implementation的上方。
@interface MyClass ()
@property (retain, readwrite) float value;
@end
使用Extension需要注意的点:
(1) Extension中的方法必须在@implementation中实现,否则编译会报错。(不一定,我试过了,只声明,不一定非得实现)

如果想在分类中添加属性,就把属性写在.m中,然后通过runtime机制生成该属性的set、get方法。然后利用方法返回这个属性。如果不生成set、get方法,在其中调用self.属性,程序会崩溃。这个属性可以写在匿名分类中,还可以写在.h中,但是要通过runtime实现set、get方法。

static const char *PersonNameKey = “PersonNameKey”;

– (void)setValueww:(NSString *)value
{
objc_setAssociatedObject(self, PersonNameKey, value, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

– (NSString *)value
{
return objc_getAssociatedObject(self, PersonNameKey);
}

这个方法有四个参数,分别是:源对象,关联时的用来标记是哪一个属性的key(因为你可能要添加很多属性),关联的对象和一个关联策略。

id object : 源对象 -指定我们需要绑定的对象,e.g ,给一个字符串添加一个内容
const void * key : 设置一个静态常亮,也就是Key 值,通过这个我们可以找到我们关联对象的那个数据值
id value 这个是我们打点调用属性的时候会自动调用set方法进行传值
objc_AssociationPolicy policy : 这个是关联策略,这几个管理策略

OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {

// 第一个关联策略是基于基本类型的,也就是我们常用的assign 属性
OBJC_ASSOCIATION_ASSIGN = 0, /*< Specifies a weak reference to the associated object. /
//关联策略是基于对象类型的,如我们正常的是retain nonatomic (非原子操作)类型 ,retain : 保留一个引用指针,内存地址不修改,指向同一块内存地址
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
* The association is not made atomically. */
//这个相当于属性中对一些对象或者字符串进行的copy 这个是拷贝一个新对象,内存地址不在一起,还是使用的非原子类型,非原子类型我们也称之为线程不安全的操作,但是对于OC里面的数据操作,我们尽量避开原子操作,原子操作是线程安全的,会影响我们对数据的写入操作
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied.
* The association is not made atomically. */
// 简单的指针引用, retain 操作
OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object.
* The association is made atomically. */
//把简单的对象拷贝到一个新的内存地址
OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied.
* The association is made atomically. */
};

23、OC是动态运行时语言是什么意思?什么是动态识别,动态绑定?

动态:

主要是将数据类型的确定由编译时,推迟到了运行时。

这个问题其实浅涉及到两个概念,运行时和多态。 简单来说,运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。

现在,让我来想想OC的动态语言特性。OC的动态特性表现为了三个方面:动态类型、动态绑定、动态加载。之所以叫做动态,是因为必须到运行时(run time)才会做一些事情。

(1)动态类型

动态类型,说简单点就是id类型。动态类型是跟静态类型相对的。像内置的明确的基本类型都属于静态类型(int、NSString等)。静态类型在 编译的时候就能被识别出来。所以,若程序发生了类型不对应,编译器就会发出警告。而动态类型就编译器编译的时候是不能被识别的,要等到运行时(run time),即程序运行的时候才会根据语境来识别。所以这里面就有两个概念要分清:编译时跟运行时。

id obj = someInstance;

if ([obj isKindOfClass:someClass]) {

someClass classSpecifiedInstance = (someClass )obj;

}

(2)动态绑定

动态绑定(dynamic binding)貌似比较难记忆,但事实上很简单,只需记住关键词@selector/SEL即可。先来看看“函数”,对于其他一些静态语言,比如 c++,一般在编译的时候就已经将将要调用的函数的函数签名都告诉编译器了。静态的,不能改变。而在OC中,其实是没有函数的概念的,我们叫“消息机 制”,所谓的函数调用就是给对象发送一条消息。这时,动态绑定的特性就来了。OC可以先跳过编译,到运行的时候才动态地添加函数调用,在运行时才决定要调 用什么方法,需要传什么参数进去。这就是动态绑定,要实现他就必须用SEL变量绑定一个方法。最终形成的这个SEL变量就代表一个方法的引用。这里要注意 一点:SEL并不是C里面的函数指针,虽然很像,但真心不是函数指针。SEL变量只是一个整数,他是该方法的ID,@selector()就是取类方法的编号。以前的函数调用,是根据函数名,也就是 字符串去查找函数体。但现在,我们是根据一个ID整数来查找方法,整数的查找字自然要比字符串的查找快得多!所以,动态绑定的特定不仅方便,而且效率更 高。

由于OC的动态特性,在OC中其实很少提及“函数”的概念,传统的函数一般在编译时就已经把参数信息和函数实现打包到编译后的源码中了,而在OC中最常使 用的是消息机制。调用一个实例的方法,所做的是向该实例的指针发送消息,实例在收到消息后,从自身的实现中寻找响应这条消息的方法

(3)动态加载

根据需求加载所需要的资源,这点很容易理解,对于iOS开发来说,基本就是根据不同的机型做适配。最经典的例子就是在Retina设备上加载@2x的图片,而在老一些的普通屏设备上加载原图。

1、什么是动态语言?
动态语言,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。比如众所周知的ECMAScript(JavaScript)便是一个动态语言。除此之外如Ruby、Python等也都属于动态语言,而C、C++等语言则不属于动态语言。
有三个名词容易混淆:
Dynamic Programming Language (动态语言或动态编程语言)
Dynamically Typed Language (动态类型语言)
Statically Typed Language (静态类型语言)
所谓的动态类型语言,意思就是类型的检查是在运行时做的。
2、静态类型
而静态类型语言的类型判断是在运行前判断(如编译阶段),比如C#、Java就是静态类型语言,静态类型语言为了达到多态会采取一些类型鉴别手段,如继承、接口,而动态类型语言却不需要,所以一般动态语言都会采用dynamic typing,常出现于脚本语言中.需要明确说明一点,那就是,是不是动态类型语言与这门语言是不是类型安全的完全不相干的,不要将它们联系在一起!
优缺点:
静态类型语言的主要优点在于其结构非常规范,便于调试,方便类型安全;缺点是为此需要写更多的类型相关代码,导致不便于阅读、不清晰明了。动态类型语言的优点在于方便阅读,不需要写非常多的类型相关的代码;缺点自然就是不方便调试,命名不规范时会造成读不懂,不利于理解等。顺便说一下,现在有这样一种趋势,那就是合并动态类型与静态类型在一种语言中,这样可以在必要的时候取长补短,Boo就是一个很好的试验性例子。
3、Objective-C的动态运行性
objective-c语言是C语言的一个子类,所以Objective-C是一个静态语言,但是Objective-C的三大特性之一的多态性让其拥有了动态性。
oc的动态性让程序可以在运行时判断其该有的行为,而不是像c等静态语言一样在编译构建时就确定下来。它的动态性主要体现在一下三个方面:

1。动态类型。 如id类型。实际上静态类型因为其固定性和可预知性而使用得更加广泛。静态类型是强类型,而动态类型属于弱类型。运行时决定接收者。

这里补充讲一下强、弱类型:语言有无类型、强类型和弱类型三种。无类型的不做任何检查,甚至不区分指令和数据;弱类型的检查很弱,仅能区分指令和数据;强类型的严格在编译期进行检查。强类型语言在没有强制类型转化前,不允许两种不同类型的变量相互操作

2。 动态绑定。让代码在运行时判断需要调用什么方法,而不是在编译时。与其他面向对象语言一样,方法调用和代码并没有在编译时连接在一起,而是在消息发送时才进行连接。运行时决定调用哪个方法。

3。 动态载入。让程序在运行时添加代码模块以及其他资源。用户可以根据需要加载一些可执行代码和资源,而不是在启动时就加载所有组件。可执行代码中可以含有和程序运行时整合的新类。

对象是运行时类的一个实例。在类里声明了的实例变量和方法,它的每个实例都在内存中拥有同样的实例变量,以及指向那些方法的指针。在oc中对象永远是通过指针来引用的。

24、SEL

SEL就是对方法的一种包装。包装的SEL类型数据它对应相应的方法地址,找到方法地址就可以调用方法。在内存中每个类的方法都存储在类对象中,每个方法都有一个与之对应的SEL类型的数据,根据一个SEL数据就可以找到对应的方法地址,进而调用方法。

SEL就是对方法的一种包装。包装的SEL类型数据它对应相应的方法地址,找到方法地址就可以调用方法

1.方法的存储位置

在内存中每个类的方法都存储在类对象中
每个方法都有一个与之对应的SEL类型的数据
根据一个SEL数据就可以找到对应的方法地址,进而调用方法
SEL类型的定义: typedef struct objc_selector *SEL
2.SEL对象的创建

SEL s1 = @selector(test1); // 将test1方法包装成SEL对象 
SEL s2 = NSSelectorFromString(@”test1”); // 将一个字符串方法转换成为SEL对象
3.SEL对象的其他用法

// 将SEL对象转换为NSString对象
NSString *str = NSStringFromSelector(@selector(test));

 Person *p = [Person new];

// 调用对象p的test方法
[p performSelector:@selector(test)];
/********************* Person.h文件 ************************/

import

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值