1.对属性修饰符的理解
MRC下
assign
主要用于修饰基本数据类型,setter方法也只是简单的赋值,例如NSInter,CGFloat 如果用来修饰对象,并不持有对象,那么对象的引用计数不变,如果这个时候对象被释放了,他就可能成为野指针,不手动置为nil,在堆上很容易造成崩溃,而如果修饰的不是对象,那么栈上的内存系统会自动处理,不会造成野指针
retain
修饰对象,不能修饰基本数据类型 ,引用计数+1,适用于NSObject及子类
ARC下
Strong
对应的setter方法是先release,然后再把参数retain,最后成员变量赋值,类似于retain
表示指向并拥有对象,其修饰的对象引用计数会+1,该对象只要引用计数不为0则不会被销毁,当然强行置为nil可以销毁他
Copy
对应的setter方法是先release,然后copy参数内容,创建一个新的内存地址,因此修改之前的地址不会修改到副本,减少对上下文的依赖,copy一般用于修饰具有可变应用类型的不可变对象身上,例如NSArray,NSString,NSDictionary,,为什么呢?看下例子
@property(nonatomic,strong) NSString *testStrongName;
@property(nonatomic,copy) NSString * testCopyName;
self.testStrongName = @"test1";
self.testCopyName = @"test1";
NSLog(@"strong====%@ p==== %p",self.testStrongName,self.testStrongName);
NSLog(@"copy======%@ p==== %p",self.testCopyName,self.testCopyName);
NSMutableString *mStr = [[NSMutableString alloc]initWithString:@"TestM2"];
self.testStrongName = mStr;
self.testCopyName = mStr;
NSLog(@"mStr==== %p",mStr);
[mStr appendString:@"111"];//mstr不改地址
NSLog(@"mStr==== %p",mStr);
mStr = [NSMutableString stringWithFormat:@"test2"];//mstr改地址
NSLog(@"mStr==== %p",mStr);
[mStr appendString:@"111"];
NSLog(@"strong====%@ p==== %p",self.testStrongName,self.testStrongName);
NSLog(@"copy======%@ p==== %p",self.testCopyName,self.testCopyName);
//打印结果为
2019-01-22 11:42:33.286277+0800 OC-test1[18667:237712] [default] Unable to load
Info.plist exceptions (eGPUOverrides)
2019-01-22 11:42:33.338443+0800 OC-test1[18667:237660] strong====test1 p====
0x100002058
2019-01-22 11:42:33.338476+0800 OC-test1[18667:237660] copy======test1
p==== 0x100002058
2019-01-22 11:42:33.338495+0800 OC-test1[18667:237660] mStr====
0x600000c053e0
2019-01-22 11:42:33.338511+0800 OC-test1[18667:237660] mStr====
0x600000c053e0
2019-01-22 11:42:33.338535+0800 OC-test1[18667:237660] mStr====
0x600000c05410
2019-01-22 11:42:33.338569+0800 OC-test1[18667:237660] strong====TestM2111
p==== 0x600000c053e0
2019-01-22 11:42:33.338584+0800 OC-test1[18667:237660] copy======TestM2
p==== 0x5f8f2f5af9a6eb4d
可以看到,本身是不可变类型NSString,但是它会有可变类型的属性在,如果你用Strong修饰他,当该指针指向了NSMutableString类型的数据,Strong只是把源数据的多了一个新的指针指向而已,当源数据改变,那么原来设置的name不可变类型,就可以变了。这就是上面遇到修改之前的数据改变了现有的数据,上下文依赖了,因此copy来减少上下文依赖,如果本身是不可变类型NSstring,那么Strong也一样,如果变成可变类型了,那么减少上下文依赖,copy就会对可变类型执行深copy,从而让数据源更干净
strong和copy都会使引用计数加1,但strong使两个指针指向同一个内存地址,copy则会在内存里拷贝一份对象,两个指针指向不同的内存地址
针对copy这个属性,以string类型为例,copy就是普通的指针copy(浅copy),而mutableCopy就是内容copy(深copy),如果是mutableString类型的话,那么copy就全是内容copy了 如果是容器类型,例如NSArray和NSDictionary,copy属性操作的指针针对容器本身,而容器内部的都还是同一个指针引用,并不会根据容器的copy或者strong而变化,指针根据自己的属性修饰符有关
- weak 不会保留传入的属性,不会使对象的引用计数+1,类似assign,但是当对象被释放时,weak自动置nil,这就是weak区别assgin最大的区别 小知识:当属性是readwrite的时候,如果重写了getter的方法,那么setter方法是系统提供的,那么系统顺便帮我们实现了成员变量,但是如果你重写了setter和getter方法,那么这个时候你把系统的两个方法都实现了,那么系统就需要你自己去实现成员变凉了,不然是无法获取到的,你可以再implementation或者extension里面进行实现,或者在h文件实现,前两者是@private的,后者是共有的
weak 和 assign的理解
1.assign适用于基本数据类型,weak是适用于NSObject对象,并且是一个弱引用。 2.assign其实也可以用来修饰对象。那么我们为什么不用它修饰对象呢?因为被assign修饰的对象(一般编译的时候会产生警告:Assigning retained object to unsafe property; object will be released after assignment)在释放之后,指针的地址还是存在的,也就是说指针并没有被置为nil,造成野指针。对象一般分配在堆上的某块内存,如果在后续的内存分配中,刚好分到了这块地址,程序就会崩溃掉。 3.那为什么可以用assign修饰基本数据类型?因为基础数据类型一般分配在栈上,栈的内存会由系统自己自动处理,不会造成野指针。 4.weak修饰的对象在释放之后,指针地址会被置为nil。所以现在一般弱引用就是用weak。weak使用场景: 5.在ARC下,在有可能出现循环引用的时候,往往要通过让其中一端使用weak来解决,比如: delegate代理属性,通常就会声明为weak。 自身已经对它进行一次强引用,没有必要再强引用一次时也会使用weak。比如:自定义 IBOutlet控件属性一般也使用weak,当然也可以使用strong。
__block与__weak的区别
__block是用来修饰一个变量,这个变量就可以在block中被修改
__block:使用 __block修饰的变量在block代码块中会被retain(ARC下会retain,MRC下不会retain)
__weak:使用__weak修饰的变量不会在block代码块中被retain
同时,在ARC下,要避免block出现循环引用 __weak typedof(self)weakSelf = self;
这两个都是和block相关,block就是OC对闭包的实现,闭包就是匿名函数,或者理解为指向函数的指针
block变量定义时为什么用copy?block是放在哪里的?
block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈(stack)上,可能被随时回收,而不是在堆(heap)上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。通过copy可以把block拷贝(copy)到堆,保证block的声明域外使用。
特别需要注意的地方就是在把block放到集合类当中去的时候,如果直接把生成的block放入到集合类中,是无法在其他地方使用block,必须要对block进行copy。
[array addObject:[[^{
NSLog(@"hello!");
} copy] autorelease]];
nil,Nil,NULL和NSNull理解
nil
nil 是 ObjC 对象的字面空值,对应 id 类型的对象,或者使用 @interface 声明的 ObjC 对象。
例如:
NSString *someString = nil;
NSURL *someURL = nil;
id someObject = nil;
if (anotherObject == nil) // do something
定义:
#ifndef nil
# if __has_feature(cxx_nullptr)
# define nil nullptr
# else
# define nil __DARWIN_NULL
# endif
#endif
// __DARWIN_NULL in _types.h
#define __DARWIN_NULL ((void *)0)
Nil
Nil 是 ObjC 类类型的书面空值,对应 Class 类型对象。
例如:
Class someClass = Nil;
Class anotherClass = [NSString class];
定义声明和 nil 是差不多的,值相同:
#ifndef Nil
# if __has_feature(cxx_nullptr)
# define Nil nullptr
# else
# define Nil __DARWIN_NULL
# endif
#endif
NULL
NULL 是任意的 C 指针空值。
例如:
int *pointerToInt = NULL;
char *pointerToChar = NULL;
struct TreeNode *rootNode = NULL;
定义:
// in stddef.h
#define NULL ((void*)0)
NSNull
NSNull 是一个代表空值的类,是一个 ObjC 对象。实际上它只有一个单例方法:+[NSNull null],一般用于表示集合中值为空的对象。
例子说明:
// 因为 nil 被用来用为集合结束的标志,所以 nil 不能存储在 Foundation 集合里。
NSArray *array = [NSArray arrayWithObjects:@"one", @"two", nil];
// 错误的使用
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:nil forKey:@"someKey"];
// 正确的使用
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:[NSNull null] forKey:@"someKey"];
定义:
NSNull.h
#import <Foundation/NSObject.h>
@interface NSNull : NSObject <NSCopying, NSSecureCoding>
+ (NSNull *)null;
@end
NIL 或 NSNil
ObjC 不存在这两个符号!
转载参考
iOS基础面试知识点总结 - Deft_MKJing的博客 - CSDN博客
nil,Nil,NULL和NSNull理解(ios)
网友评论