Oui, après la planification de NSStream dans RunLoop, son nombre de références augmente. Je pense que ce code est suffisant pour prouver:
NSInputStream* nStream = [[NSInputStream alloc] initWithFileAtPath:path];
NSLog(@"Stream retain count A is %ld", CFGetRetainCount((__bridge CFTypeRef)nStream));
NSValue* val = [NSNumber valueWithPointer:(__bridge const void * _Nullable)(nStream)];// not increment reference counter
NSLog(@"Stream retain count B is %ld", CFGetRetainCount(val.pointerValue));
[nStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
NSLog(@"Stream retain count C is %ld", CFGetRetainCount(val.pointerValue));
nStream = nil;
NSLog(@"Stream retain count D is %ld", CFGetRetainCount(val.pointerValue));
Et la sortie de la console:
Stream retain count A is 1
Stream retain count B is 1
Stream retain count C is 3
Stream retain count D is 2
Ainsi, en ajoutant à NSRunLoop augmente les références numériques par 2. Après l'annulation de la contre-valeur de référence forte originale reste positive, et cela empêche la désallocation de l'objet stream.
Parce que l'objet existe encore, répondra à ce code:
[(__bridge const NSInputStream*)val.pointerValue open];
[(__bridge const NSInputStream*)val.pointerValue close];
Mais la prochaine ligne causera accident - Il est maintenant retiré de flux NSRunLoop. Toutes les références à cela ont été supprimées - ce qui reste est la valeur de pointeur vers - maintenant désalloué - objet (se comporte de manière similaire au pointeur assigned
). Appel à l'objet désalloué signifie toujours EXC_BAD_ACCESS
...
NSLog(@"Stream retain count E is %ld",
CFGetRetainCount(val.pointerValue));//crash here
Vous pouvez également trouver une analogie avec le comportement de NSTimer après avoir ajouté à NSRunLoop. NSTimer a la méthode 'invalidate' pour retirer de NSRunLoop. L'équivalent de NSStream est 'close' –