Подтвердить что ты не робот

Модификаторы ARC __bridge demystified

Недавно меня спросил один из моих друзей о новых модификаторах мостов, которые стали активными в ARC. Он спросил меня, знаю ли я, какие из них использовать в определенное время, и какая разница между различными модификаторами __bridge. Он спросил меня: "Как они работают, когда я их использую, как их использовать и как они работают" под капотом "?

Примечание. Предполагалось, что это вопрос типа "Поделитесь своими знаниями", когда я сам ответил на этот вопрос, но я не уверен, что правильно его настроил.

4b9b3361

Ответ 1

Поскольку я узнал, что они представляют и как они действуют совсем недавно, я хочу поделиться с кем-либо еще, кто хочет узнать о модификаторах __bridge в ARC, которые могут быть источником путаницы из-за того, что используется бесплатный мост чтобы быть выполненным с помощью простого броска.

Раньше было, что если бы вы хотели, например, направить ваш объект NSArray на CFArrayRef для любых целей, это можно сделать с помощью простого приведения:

     NSArray* myArray = [NSArray alloc]initWithObjects:....]; //insert objects

     CFArrayRef arrayRef = (CFArrayRef) myArray;        

В этом случае, возможно, у вас был NSArray цветов и ему нужно было передать его в CFArrayRef для его использования с CoreGraphics для рисования градиента.

Однако с ARC это больше не будет работать для вас, и вы получите эту ошибку:

enter image description here

Хорошо, что это значит!!!

Ну, оказывается, ARC не нравится, когда вы делаете этот вид актеров, и даже дадите вам несколько решений "Исправить", у которых, похоже, есть такое же ключевое слово __ bridge в них, так что дайте им право!

В ARC у нас есть 3 основных модификатора __bridge:

__ мост

__ bridge_retained (который является партнером функции CFBridgingRetain)

__ bridge_transfer (который взаимодействует с функцией CFBridgingRelease)

Итак, мы начнем с __bridge. Что это? __bridge - это еще один способ сказать: "Эй, компилятор, просто дай мне мой штопочный литой объект!". Компилятор будет рад сделать это и вернуть вам литой объект по вашему вкусу!

Однако, поскольку вам нужен "свободно" подобранный объект, YOU по-прежнему несут ответственность за освобождение памяти для первоначально выделенного объекта. В этом случае, если бы я сделал это:

    NSArray* myArray = [NSArray alloc]init];
    CFArrayRef arrayRef = (__bridge CFArrayRef) myArray;

Я все еще ответственен за освобождение памяти myArray, потому что это был первоначально выделенный объект. Помните, что __bridge просто сообщает компилятору выполнить бросок!! И поскольку я компилирую в ARC, я не должен явно ссылаться на [-release] на объект myArray, ARC сделает это для меня!

Обратите внимание, что модификатор __bridge работает в обоих направлениях! (Следовательно, беспошлинное мостовое соединение), и вы можете так же легко сбрасывать объект CF объекту NS таким же образом (то есть без сбоев!) Так:

CFArrayRef arrayRef; // allocate this arrayRef and give it a value later on
//... amazing code.....
NSArray* myArray = (__bridge NSArray*)arrayRef;

Но так как объект CF будет первоначально выделенным объектом, я должен вызвать CFRelease (независимо);

Теперь перейдем к __ bridge_retained и ее партнеру в преступлении CFBridgingRetain(). Этот модификатор __bridge специально предназначен для передачи права собственности на объект NS TO A OB OBECT (Таким образом, мы будем ожидать, что это будет CFRelease (независимо от того, что это объект типа CF)

Смысл, если бы я снова выполнил свой старый сценарий, но на этот раз с __bridge_retained:

 NSArray* myArray = [NSArray alloc]initWithObjects:....]; //insert objects

 CFArrayRef arrayRef = (__bridge_retained) myArray;

объект arrayRef теперь имеет явное право собственности на память, которая раньше принадлежала указателю myArray. Поскольку теперь тип CF имеет право собственности, я должен сам его освободить, используя CFRelease (независимо);

Какую роль играет во всем этом хаосе функция CFBridgingRetain()? Он играет ту же самую ту же роль, что и актеры, о которых мы только что говорили! Давайте взглянем на прототип функции для CFBridgingRetain:

    CFTypeRef CFBridgingRetain(id x);

Мы видим, что это в значительной степени просто упрощает целое (__bridge_retained) понятие в одну функцию! Мы возвращаем объект CF после "ввода" объекта типа NS! Радикальный! Да, я знаю, это потрясающе! Слишком много прохлады, чтобы занять одно место! И да, он также выполняет передачу "прав собственности" памяти.. как здорово!

И последнее, но не в последнюю очередь, __ bridge_transfer и всемогущий CFBridgingRelease()!

__ bridge_transfer работает почти как противоположность __bridge_retained. Модификатор __bridge_transfer переносит право собственности на тип объекта CF на тип объекта NS.

Итак, позвольте обратиться к примеру, который использовался во всем этом, чтобы его рассеять:

 NSArray* myArray = [NSArray alloc]initWithObjects:....]; //insert objects

 CFArrayRef arrayRef = (__bridge_retained) myArray;

 // at this point, arrayRef holds the ownership
 // Let add this new line to change things up a bit:

 NSArray* otherArray = (__bridge_transfer NSArray*)arrayRef;

Так что же делает эта удивительная небольшая программа, которую мы только что писали?

Шаг 1. Мы выделили NSArray

Шаг 2: Мы передали собственное имя массива объекту arrayRef

//Прежде чем перейти к шагу 3, давайте понять, что в данный момент arrayRef является владельцем

Шаг 3: Мы повторно передаем право собственности, которое USED будет принадлежать arrayRef, обратно в NSArray *

Потому что на данный момент указатель otherArray является владельцем, казалось бы, что-то вроде natural, чтобы сказать [otherArray release], когда мы закончим, не так ли? Ну вот, где ARC запускается и позаботится о выпуске этого массива для вас!

И ты знал, что это становится круче? Этот модификатор __bridge потрясающий партнер в преступлении:      CFBridgingRelease()

делает это намного круче! CFBridgingRelease имеет этот прототип функции:

   id CFBridgingRelease(CFTypeRef x);

И мы видим, что это то же самое, что и при использовании __bridge_transfer. И эта функция также передает право собственности на объект NS! Это просто фантастика!

Использование функций CFBridgingXXX, возможно, может иметь немного больше смысла в первую очередь из-за того, что многие программисты objective-c все еще имеют понятие правила NARC:

Все, что было вызвано с помощью:

N ew

A lloc

R etain

C opy

должен иметь балансировочный вызов -отсылка

Таким образом:

     NSArray* myArray = [[NSArray alloc]init]; 
                                       // there the A of NARC! 
                                       //(cleaned by ARC)
     CFArrayRef arrayRef = CFBridgingRetain(myArray); // there the R of NARC!!
     //NSArray* other = CFBridgingRelease(arrayRef); // cleaned up by ARC

Может облегчить процесс обучения методам __bridge из-за того, что сохранить был сопоставлен с выпуском

Если все это все еще может запутать, подумайте об этом так: вы указатель на любой тип объекта NS. Ради последовательности, скажем, вы указатель NSArray, который прямо сейчас не указывает ни на что. Таким образом, вы можете себе представить, что вы, как указатель на ноль, стоите в ванной комнате с выключенными огнями. (Отключенные огни означают, что вы ничего не указываете).

Затем, позже в коде, ваш программист решает назначить вас новому NSArray. то есть он говорит это:

      you = [[NSArray alloc]init];

Вдруг огни в ванной, в которой ты стоял, включились! Вы указываете на объект! Теперь при обычном выполнении программы, когда вы закончите использовать объект, вы отпустите его. Поэтому в этом случае, когда вы закончите пользоваться ванной комнатой, вы выключите свет.

Но программа, в которой вы находитесь, к сожалению, не очень "нормальная". Программист решил использовать некоторые объекты CoreFoundation! BLEH!

И он пишет эту строку кода:

    CFArrayRef other = (__bridge_retained CFArrayRef) you;

Итак, теперь случается так, что другой человек ходит в ванную на том же, когда вы уходите. Из-за вежливости вы не выключаете свет, потому что там есть другой человек, использующий уборную и отвечающий за то, что выключил свет, когда он/она ушел.

В этом случае, поскольку новый владелец туалета является объектом CF, программист должен его вручную освободить.

Но что, если он/она должен был написать это:

   CFArrayRef ref = (__bridge CFArrayRef) you;

что происходит здесь, что другой человек просто вступил в ту же уборную, что и вы, даже не спрашивая! Как грубо! Кроме того, он ожидает, что вы тоже убережете его! Итак, вы, будучи джентльменом/леди, выключите свет, когда вы закончите.

Однако, поскольку вы являетесь объектом типа NS, ARC приходит и очищает его для вас.

И наконец, что, если программист пишет это:

    you = (__bridge_transfer NSArray*)arrayRef;

Здесь происходит полная противоположность первого сценария. Вместо того, чтобы покидать уборную в то же самое время, когда кто-то входит, вы тот, кто входит, а другой покидает

Применяются те же правила управления памятью. Поскольку вы взяли на себя "владение" туалетом, вы должны вручную отключить свет. И поскольку вы являетесь объектом типа NS, ARC сделает это за вас... снова:) Не является такой ARC такой красотой!

Я знаю, что это может показаться немного пугающим и запутанным сначала, но просто проработайте свой путь через него, прочитайте его снова, и вы узнаете, насколько невероятным этот механизм ARC работает!

Спасибо всем за чтение! Надеюсь, это помогло:)

Благодаря @rob mayoff и @Krishnabhadra для  вся дополнительная помощь и предложения!