1、什么情況使用 weak 關(guān)鍵字,相比 assign 有什么不同
在ARC中,為了防止出現(xiàn)循環(huán)引用,一方需要使用weak來修飾,比如delegate,而assign用于修飾基本數(shù)據(jù)類型、結(jié)構(gòu)體。
區(qū)別:用weak修飾的屬性指向的內(nèi)容被清空時(shí)(引用計(jì)數(shù)器為0),系統(tǒng)會自動將屬性賦值為nil,而assign不會,所以如果用assign修飾了對象并且當(dāng)對象指向內(nèi)容被清空時(shí),再次訪問對象會發(fā)生野指針錯誤,下面上代碼:
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic, strong) id strongPointer;
@property(nonatomic, weak) id weakPointer;
@property(nonatomic, assign) id assignPointer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.strongPointer = [NSDate date];
self.weakPointer = self.strongPointer;
self.assignPointer = self.strongPointer;
NSLog(@"%@ %@ %@", self.strongPointer, self.weakPointer, self.assignPointer);
self.strongPointer = nil;
NSLog(@"%@", self.strongPointer);
NSLog(@"%@", self.weakPointer);
NSLog(@"%@", self.assignPointer); ///<---
}
@end
當(dāng)運(yùn)行到箭頭指向行時(shí),會出現(xiàn)壞內(nèi)存訪問:
那這時(shí)候有人又要問了:為什么用assign修飾基本數(shù)據(jù)類型的時(shí)候,不會發(fā)生類似的壞內(nèi)存訪問的錯誤呢?
那是因?yàn)?,對象一般分配?code>堆內(nèi)存,如果之后系統(tǒng)訪問到了這塊內(nèi)存空間,便會出錯,而基本數(shù)據(jù)類型,分配在棧內(nèi)存
,系統(tǒng)會自動管理?xiàng)?nèi)存,不會出現(xiàn)野指針錯誤。
被用weak和assign修飾的屬性指向的對象的引用計(jì)數(shù)器不變。
2、copy和mutableCopy
iOS 集合的深復(fù)制與淺復(fù)制 這本篇文章講的非常好,自己總結(jié)如下:
淺拷貝
:復(fù)制指向?qū)ο蟮闹羔?,即指針拷貝?br>
深拷貝
:拷貝整個(gè)對象的內(nèi)容,即內(nèi)容拷貝。
非集合類對象(NSString、NSNumber等)
- immutable對象:
NSString *str = @"Hello";
NSLog(@"str : %p", str);
NSString *copyStr = [str copy];
NSLog(@"copyStr : %p", copyStr);
NSMutableString *mutCopyMutStr = [str mutableCopy];
NSLog(@"mutCopyMutStr : %p", mutCopyMutStr);
結(jié)果如下:
可以看到copyStr
和str
內(nèi)存地址一樣,mutCopyMutStr
和str
內(nèi)存地址不一樣
- mutable對象:
NSMutableString *mutStr = [NSMutableString stringWithString:@"Hello"];
NSLog(@"mutStr : %p", mutStr);
NSString *copyStr = [mutStr copy];
NSLog(@"copyStr : %p", copyStr);
NSMutableString* mutCopyMutStr = [mutStr mutableCopy];
NSLog(@"mutCopyMutStr : %p", mutCopyMutStr);
結(jié)果如下:
可以看到,copyStr
、mutCopyMutStr
和mutStr
的內(nèi)存地址都不一樣
集合類對象
- immutable對象:
NSArray *array = @[@"1", @"2", @"3"];
NSLog(@"array : %p", array);
NSArray *copyArray = [array copy];
NSLog(@"copyArray : %p", copyArray);
NSMutableArray *mutCopyArray = [array mutableCopy];
NSLog(@"mutCopyArray : %p", mutCopyArray);
結(jié)果如下:
可以看到,copyArray
和array
的內(nèi)存地址一樣,mutCopyArray
和array
的內(nèi)存地址不一樣,
- mutable對象:
NSMutableArray *mutArray = [NSMutableArray arrayWithObjects:@"1", @"2", @"3", nil];
NSLog(@"mutArray : %p", mutArray);
NSArray *copyMutArray = [mutArray copy];
NSLog(@"copyMutArray : %p", copyMutArray);
NSMutableArray *mutCopyMutArray = [mutArray mutableCopy];
NSLog(@"mutCopyMutArray : %p", mutCopyMutArray);
結(jié)果如下:
可以看到,copyMutArray
、mutCopyMutArray
和mutArray
的內(nèi)存地址都不一樣
結(jié)論:
可以用一張圖來說明,原鏈接在這里
個(gè)人覺得可以這樣總結(jié):
- 首先判斷是copy還是mutableCopy,如果是mutableCopy,那不管被拷貝對象是mutableObject還是immutableObject,都屬于深拷貝,即內(nèi)容拷貝,產(chǎn)生新對象,并且返回的是mutableObject,要用可變的數(shù)據(jù)類型來接收。
- 如果是copy,就得判斷被拷貝對象,如果被拷貝對象是mutableObject,那也是深拷貝,即內(nèi)容拷貝;如果被拷貝對象是immutableObject,那是淺拷貝,即指針拷貝。但不管是那種,返回的都是immutableObject
- 注意點(diǎn)就是,淺拷貝對應(yīng)的拷貝方法只可能是copy;但深拷貝不僅僅與拷貝方法有關(guān),還與被拷貝對象的類型有關(guān)。
3、@property中NSString、NSArray、NSDictionary等數(shù)據(jù)類型為什么要用copy來修飾,用strong會有什么問題
以NSString
為例來說明問題:
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSString *str_strong;
@property (nonatomic, copy) NSString *str_copy;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *mutStr = [[NSMutableString alloc] initWithString:@"Hello"];
self.str_strong = mutStr;
self.str_copy = mutStr;
NSLog(@"str_strong : %@ str_copy : %@", self.str_strong, self.str_copy);
[mutStr appendString:@" World"];
NSLog(@"str_strong : %@ str_copy : %@", self.str_strong, self.str_copy);
NSLog(@"mutStr : %p str_strong : %p str_copy : %p", mutStr, self.str_strong, self.str_copy);
}
@end
因?yàn)?code>NSString、NSArray、NSDictionary等類,均有可變的子類:NSMutableString、NSMutableArray、NSMutableDictionary
,如果用strong
來修飾,則自身和被拷貝對象指向的是同一塊內(nèi)存地址,如果被拷貝的可變對象發(fā)生改變,那自身的值也會發(fā)生變化,使用copy
即可避免這種問題,因?yàn)樵谶@種情況下,copy
進(jìn)行的是深拷貝,即內(nèi)容拷貝,開辟了新的內(nèi)存空間。
4、如何實(shí)現(xiàn)自定義類的copy方法,如何重寫用copy修飾的對象的setter方法
實(shí)現(xiàn)自定義類的copy方法
自定義QLCar
類:
#import <Foundation/Foundation.h>
@interface QLCar : NSObject <NSCopying, NSMutableCopying>
@property (nonatomic, copy) NSString *brand;
@end
遵守NSCopying, NSMutableCopying
協(xié)議
#import "QLCar.h"
@implementation QLCar
- (id)copyWithZone:(NSZone *)zone {
QLCar *car = [[[self class] allocWithZone:zone] init];
car.brand = _brand;
return car;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
QLCar *car = [[[self class] allocWithZone:zone] init];
car.brand = _brand;
return car;
}
@end
驗(yàn)證:
QLCar *c = [[QLCar alloc] init];
c.brand = @"Mercedes";
self.car = [c copy];
NSLog(@"%@", self.car.brand);
NSLog(@"c : %p car : %p",c, self.car);
如何重寫用copy修飾的對象的setter方法
#import "ViewController.h"
#import "QLCar.h"
@interface ViewController ()
@property (nonatomic, copy) QLCar *sedan;
@end
@implementation ViewController
- (void)setSedan:(QLCar *)sedan {
_sedan = [sedan copy];
}
@end
5、@property 后面可以有哪些修飾符
- 原子性/非原子性:
atomic/nonatomic
,如果不指定nonatomic,則默認(rèn)為atomic - 讀寫權(quán)限:
readonly(只讀)
、readwrite(可讀可寫)
- 內(nèi)存管理:
weak
、strong
、assign
、copy
、retain
、unsafe_unretained
- Access Method:
@property (nonatomic, assign, getter=isHidden) BOOL hidden;
- Nullability:
nonnull
、nullable
、null_resettable
、null_unspecified
6、多線程類
- 有ABC三個(gè)任務(wù),C必須等AB先執(zhí)行完再執(zhí)行,AB兩個(gè)同時(shí)執(zhí)行,ABC必須都在子線程執(zhí)行
方案A(waitUntilFinished)
- (void)viewDidLoad {
[super viewDidLoad];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
[self printNumber:1];
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
[self printNumber:2];
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
[self printNumber:3];
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[op1, op2] waitUntilFinished:YES];
[queue addOperation:op3];
}
- (void)printNumber:(NSInteger)number {
for (NSInteger i = 0; i < 1000; i++) {
NSLog(@"%ld %@", number, [NSThread currentThread]);
}
}
方案B(addDependency)
- (void)viewDidLoad {
[super viewDidLoad];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
[self printNumber:1];
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
[self printNumber:2];
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
[self printNumber:3];
}];
[op3 addDependency:op1];
[op3 addDependency:op2];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
}
- (void)printNumber:(NSInteger)number {
for (NSInteger i = 0; i < 1000; i++) {
NSLog(@"%ld %@", number, [NSThread currentThread]);
}
}
7、Push、Present中的控制器生命周期
- Push(A的導(dǎo)航控制器push出B)
-
Present(A控制器present出B)
8、runtime
- Method Swizzling(方法交換)
//自定義Car類.h文件
@interface Car : NSObject
- (void)mercedes;
- (void)bmw;
+ (void)audi;
+ (void)toyota;
@end
//自定義Car類.m文件
@interface Car()
{
CGFloat _price;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) CGFloat speed;
@end
@implementation Car
- (void)mercedes {
NSLog(@"mercedes");
}
- (void)bmw {
NSLog(@"bmw");
}
+ (void)audi {
NSLog(@"audi");
}
+ (void)toyota {
NSLog(@"toyota");
}
- (void)privateMethod1 {
}
- (void)privateMethod2 {
}
@end
#import "ViewController.h"
#import "Car.h"
#import <objc/runtime.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Car *myCar = [[Car alloc] init];
Method m1 = class_getInstanceMethod([Car class], @selector(mercedes));
Method m2 = class_getInstanceMethod([Car class], @selector(bmw));
method_exchangeImplementations(m1, m2);
[myCar mercedes];
[myCar bmw];
Method m3 = class_getClassMethod([Car class], @selector(audi));
Method m4 = class_getClassMethod([Car class], @selector(toyota));
method_exchangeImplementations(m3, m4);
[Car audi];
[Car toyota];
}
@end
- get ivarList(獲取成員變量列表)
#import "ViewController.h"
#import "Car.h"
#import <objc/runtime.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
unsigned int num = 0;
Ivar *ivarList = class_copyIvarList([Car class], &num);
for (int i = 0; i<num; i++) {
Ivar ivar = ivarList[i];
const char *name = ivar_getName(ivar);
NSLog(@"%s", name);
}
free(ivarList);
}
- get methodList(獲取方法列表)
#import "ViewController.h"
#import "Car.h"
#import <objc/runtime.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
unsigned int num = 0;
Method *methodList = class_copyMethodList([Car class], &num);
for (int i = 0; i<num; i++) {
Method method = methodList[i];
SEL methodSel = method_getName(method);
NSString *name = NSStringFromSelector(methodSel);
NSLog(@"%@", name);
}
free(methodList);
}