- PVSM.RU - https://www.pvsm.ru -
Из этой главы, да и из всей этой книги понятно, что самые лакомые куски программирования под iOS включены в публичные фреймворки, но не в SDK. Неофициальная политика Apple насчет этого проста: вы можете всё это использовать, но только на свой страх и риск. Ваш код может сломаться при следующем обновлении прошивки. Вам самим придётся искать компромисс между риском и прибылью.
Erica Sadun, The iPhone Developer's CookBook
Допустим, вам нужно сделать что-то, выходящее за рамки официальной документации. Например, изменить уровень подсветки экрана (до iOS 5 этого не было в документированной части SDK). Известно, что программисты Apple как правило дают функциям и переменным осмысленные и выразительные названия, этим мы и воспользуемся для поиска в SDK. Для этого выберем несколько слов, относящихся к теме, например, brightness, level, screen. Запустим скрипт LookSDKForSymbol.sh (это моя обертка над nm
; об этом скрипте и других используемых инструментах написано далее, в разделе «Инструменты» [3]) с ключевыми словами в качестве параметров. Скрипт выдаёт найденные в объектном файле символы (т.е. названия классов, функций, переменных). Пример выдачи:
$ LookSDKForSymbol.sh light level
U _UIBacklightLevelChangedNotification
Found in ./System/Library/CoreServices/SpringBoard.app/SpringBoard001b43c4 t -[UIApplication backlightLevel]
001b4360 t -[UIApplication setBacklightLevel:]
0025ce54 t -[UIDevice _backlightLevel]
0025ce40 t -[UIDevice _setBacklightLevel:]
… и ещё несколько десятков символов
Большую часть результатов можно сразу отбросить, например -[UIApplication backlightLevel]
возвращает значение подсветки, а не устанавливает его.
Оставшиеся, если их не более нескольких десятков, можно попытаться скормить гуглу. Случается, что кто-то уже занимался исследованием API, связанных с найденными символами, и в этом случае задача, считай, решена. В более сложных случаях приходится заниматься реверс-инжинерингом, то есть выяснять, как работают найденные функции, как использовать найденных оповещения и тому подобное.
Символьные строки, выдаваемые утилитой, делятся на следующие категории:
___{число}{вызывающая данный блок функция}_block_invoke_{число2}
Например:
___22-[AXSystemServer init]_block_invoke_0
_SBMenuButtonPressedNotification
.kCFUserNotificationAlternateButtonTitleKey
.Дальнейшие действия зависят от категории символа.
class-dump-z Foundation > $/iOS_private_headers/Foundation.h
В большинстве случаев, сгенерированного заголовчного файла достаточно: в нем должны быть довольно хорошо описаны иерархии наследования классов, структуры, методы и т.д, чтобы потратив немного времени можно разобраться с API и использовать его в своём предложении.
К сожалению, иногда информации содержащейся в заголовочном файле недостаточно, чтобы заставить код работать, и тогда приходится анализировать ассемблерный код, сгенерированный otool.
Hint по дизассемблированию Objective-C кода: почти наверняка вы столкнетесь с вызовами функций типа objc_msgSend (отправка сообщения объекту). В качестве первого параметра всегда идет указатель на объект, а вторым — указатель на селектор (selector), т.е. указатель на строку, являющуюся названием метода (остальные «обычные» аргументы идут третьим, четвертым и т.д. аргументами). Определить, что за сообщение отправляется в данном случае, поможет hexdump
.
kLockdownDeviceColorKey
-> функция lockdown_copy_value(...)
;
1. Найти в интернете, как это не банально. Мне довольно часто попадались китайский сайты, были корейский и японский сайты с очень полезной информацией. Обычно самого кода уже достаточно, чтобы понять что происходит и как используется данная функция, данный класс и т.д. Спасибо многословности и выразительности Objective-C!
2. Для многих простых функций, можно попытаться угадать сигнатуру. Внимание, это может быть довольно опасно.
Использование некоторые простые функции, таких как GSEventSetBackLightLevel, самоочевидно.
void GSEventSetBackLightLevel(float level);
Для многих других я использовал следующий трюк (на примере функции SBGetRingerSwitchState):
SInt32 ret = 5, out1 = 1, out2 = 2, out3 = 3, out4 = 4;
void *libHandle = dlopen(SPRINGBOARD_SERVICES_PATH, RTLD_LAZY);
SInt32 (*SBGetRingerSwitchState)(SInt32*,SInt32*,SInt32*,SInt32*) = dlsym(libHandle, "SBGetRingerSwitchState");
ret = SBGetRingerSwitchState(&out1, &out2, &out3, &out4);
NSLog(@"%x %x %x %x %x", ret, out1, out2, out3, out4);
В результате работы этого кода выяснилось, что
1) функция возвращала значение 0x10000003
, не зависящее от реального положения переключателя.
2) Переменная out2
изменила свое значение на self. Возвращаемое значение также не зависит от переключателя.
3) Остальные переменные не изменили свое значение.
Из 1) я сделал вывод функция возвращает значене типа kern_return_t
, так как 0x10000003
соответствует системной ошибке MACH_SEND_INVALID_DEST
. По видимому, ошибка указывала на неправильный порт [в данном случае порт — это абстракци ядра mach (mach kernel), характеризующая права и приоритет процесса]. Как правило, если в вызове функции используется номер порта, то он идет первым аргументом. Из 2) следует, что через второй аргумент функция возвращает некое значение по ссылке.
В результате этих нехитрых действий получается следующая сигнатура:
kern_return_t SBGetRingerSwitchState(mach_port_t port, SInt32 *state);
Кстати, если в названии функции присутствует слово get, то согласно naming conventions Objective-C эта функция должна возвращать значение по ссылке. Это также видно из приведенного примера.
3. Дизассемблирование. На примере все той же SBGetRingerSwitchState. Используем otool:
$ otool -p _SBGetRingerSwitchState -tV -arch armv6 SpringBoardServices | less
000038cc b5f0 push {r4, r5, r6, r7, lr}
000038ce af03 add r7, sp, #12
000038d0 b092 sub sp, #72
000038d2 aa06 add r2, sp, #24 // значение регистра r2 затирается
000038d4 9205 str r2, [sp, #20]
000038d6 ac08 add r4, sp, #32 // … как и регистра r4
000038d8 ab0f add r3, sp, #60 // … и r3
000038da 9304 str r3, [sp, #16]
000038dc 9103 str r1, [sp, #12] // значение r1 сохраняется в стеке
000038de 4925 ldr r1, [pc, #148] (0x3974)
000038e0 6011 str r1, [r2, #0]
000038e2 6020 str r0, [r4, #0] // значение r0 также сохраняется в стеке
…
Из этого кода, используя даже поверхностные знания arm-ассемблера, можно предположить, что функция принимает два аргумента типа «слово» (word)
Выходит, что у функции два аргумента. Идем дальше, в самый конец.
…
00003964 9e04 ldr r6, [sp, #16]
00003966 6836 ldr r6, [r6, #0]
00003968 9903 ldr r1, [sp, #12]
0000396a 600e str r6, [r1, #0]
// примерно соответствует (в терминах языка си): *r1 = r6; т.е. по адресу, хранящемуся в r1 записывается значение из r6;
// Это значит, что функция возвращает значение по ссылке
0000396c 462e mov r6, r5
0000396e 4630 mov r0, r6
// результат выполнения функции помещается в r0
00003970 b012 add sp, #72
00003972 bdf0 pop {r4, r5, r6, r7, pc}
…
В сухом остатке получаем:
int SBGetRingerSwitchState(int arg1, int* arg2);
Продолжая анализировать этот асcемблерный код, уточняем типы и приходим к окончательному варианту:
kern_return_t SBGetRingerSwitchState(mach_port_t port, SInt32 *state);
Понятно, что недокументированные API вовсе не обязательно работают на всех устройствах одинаково. По моему опыту, чаще всего ничего не меняется, и API работает одинаково на всех устройствах и всех прошивках. Так, например, все функции расширения UIDevice-IOKitExtensions [5] (кроме определения IMEI) работают одинаково хорошо на всех устройствах и всех прошивках. Какие изменения могут произойти при обновлении iOS?
Вот несколько практических вариантов.
void GSEventSetBacklightLevel(float level); // работает во всех версиях iOS
-[UIDevice setBrightness: (CGFloat)brightness]; // появилось в iOS 5.0
Для того чтобы избежать проблем совместимости, соблюдайте простые правила: проверяйте наличие функций (например, с помощью -[NSObject respondsToSelector:]
), классов (NSClassFromString(@"SomeClass")
вернет nil
в случае отсутствия класса SomeClass
) и т.д., а также заранее подумайте, что должна делать программа в случае, если API отсутствует. При использовании динамической линковки библотек следует также всегда проверять возвращаемые значения dlsym(...) и dlopen(...) на равенство NULL.
Определение положения бокового переключателя вибро (a.k.a. Ring/Silent switch, Mute switch)
Одной из задач, которые стояли передо мной, было определение положения бокового переключателя, который в оригинале называется ring/silent switch. Этот переключатель используется для переключения между «тихим» и обычном/«громким» режимами в айфоне и айпаде. Поиск по StackOverflow дал решение:
#import <AudioToolbox/AudioToolbox.h>
...
/*
Возвращаемое значение:
0: тихий режим
1: обычный режим
*/
int switchState()
{
// ...
// Инициализируем и активируем аудиосессию, устанавливаем
// категорию в значение kAudioSessionCategoryAmbient
// ...
UInt32 size = sizeof(CFStringRef));
CFStringRef route;
AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &size, &route);
// Получаем описание текущего аудиовыхода
CFIndex len = CFStringGetLength(route);
return (len > 0); // Если результат - пустая строка, значит телефон находится в "тихом" режиме
}
Которое, впрочем, не работает в iOS 5. Не сработало и использование более нового API (kAudioSessionProperty_AudioRouteDescription) которое дает расширенную информацию об аудиовходах и -выходах. (AUDIOROUTE)
Мои дальнейшие поиски по StackOverflow вывели меня на этот пост [6]. В нем описывается библиотечная функция AudioServicesAddSystemSoundCompletion(), чьё нестандартное поведение рассматривалось разработчиками как баг.
#import <AudioToolbox/AudioToolbox.h>
...
void playSound()
{
AudioServicesAddSystemSoundCompletion(MySoundId, NULL, NULL, MyAudioServicesSystemSoundCompletionProc,
AudioServicesPlaySystemSound(MySoundId);
}
void MyAudioServicesSystemSoundCompletionProc (SystemSoundID ssID, void *clientData)
{
// Проигрывание звука завершено
NSLog(@"Playback has been finished");
}
Нестандартное поведение заключается в том, что вызов колбэка MyAudioServicesSystemSoundCompletionProc состоится в конце проигрывания звука в обычном режиме, но сразу после вызова AudioServicesPlaySystemSound в «тихом» режиме. Это создает лазейку для определения текущего состояния переключателя. Если, например, длина аудиофайла что мы проигрываем равна 1 с, то разница во времени вызова MyAudioServicesSystemSoundCompletionProc() в «тихом» и громком режиме составляет 1 c. На этом я построил свое второе, асинхронное решение для определения положения бокового переключателя. Вот оно:
#import <AudioToolbox/AudioToolbox.h>
#import "MuteSwitchTet.h"
...
enum MuteSwitchStates {
kMuteSwitchUndefined = -1,
kSoundless = 0,
kSound = 1
};
@implementation MuteSwitchTest
...
void MyAudioServicesSystemSoundCompletionProc (SystemSoundID ssID, void *clientData)
{
// "тихий" режим
MuteSwitchTest *self = (MuteSwitchTest*)clientData;
[NSObject cancelPreviousPerformRequestsWithTarget:self];
self.muteSwitchState = kSoundless;
}
- (void) cancelSystemSoundCompletion
{
// "громкий" режим
AudioServicesRemoveSystemSoundCompletion(SoundID);
self.muteSwitchState = kSound;
}
- (void) startPlayback
{
AudioServicesAddSystemSoundCompletion(SoundID, NULL, NULL, MyAudioServicesSystemSoundCompletionProc, self);
AudioServicesPlaySystemSound(SoundID);
[self performSelector:@selector(cancelSystemSoundCompletion) withObject:nil afterDelay:0.1];
}
...
@end
Хотя это новое решение и было рабочим, оно не устраивало меня по нескольким причинам. Во-первых, оно было асинхронным и работало с ощутимой задержкой (около 1/10 секунды). Снижение задержки вело к ложным срабатываниям. Во-вторых, был побочный эффект — сам проигрываемый звук, который звучал достаточно громко чтобы смутить пользователя. Позже я искусственно выкрутил громкость в ноль в аудиоредакторе. В-третьих, это был уже слишком похоже на грязный хак, хотя это, например, не помешало создателям VSSilentSwitch [7] продавать свое решение, по всей видимости основанное на том же эффекте.
Примерно через месяц я вернулся к этой проблеме. Я начал использовать команду nm для поиска символов в объектных файлах, на её основе я написал простейший shell-скрипт, листинг которого можно найти ниже (В разделе «Инструменты»). Скрипт запускается с одним, двумя или тремя параметрами, каждый из которых представляет ключевое слово.
$ sh ~/Documents/LookSDKForSymbol.sh RingerSwitch
# часть результатов опущена
0000d738 S _kGSRingerSwitchCapability
Found in ./System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices
000038cc T _SBGetRingerSwitchState
0000370c T _SBGetRingerSwitchState
Found in ./System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices
Функция с названием SBGetRingerSwitchState
выглядела многообещающе.
Для получения нужного порта использовалась функция:
mach_port_t SBSSpringBoardServerPort();
из того же фреймворка.
Вот что получилось в итоге:
@implementation MuteSwitchTest
...
- (int) switchState
{
// Функции SBSSpringBoardServerPort и SBGetRingerSwitchState
// загружается при инициализации объекта MuteSwitchTest
mach_port_t port = SBSSpringBoardServerPort();
SInt32 state;
SBGetRingerSwitchState(port, &state);
return (int)state;
}
Определение IMEI
IMEI (International Mobile Equipment Identity) — уникальный идентификационный
код, присваиваемый каждому телефону, своего рода MAC-адрес телефона (хотя MAC-адрес у телефона также есть)
Я уже и не помню, как я вышел проект Эрики Садун uidevice-extension [5], но по мере того, как я с ним разбирался он всё больше казался мне этакой программистской «золотой жилой».
Одна из категорий, UIDeviсe(IOKit_Extensions) содержит функции для определения IMEI. Я протестировал эти функции на iPhone 4 c iOS 5.1 и iPad c iOS 4.3, всё работало и я перешел к другим задачам. Но в ходе бета-тестирования выяснилось, что функция для определения IMEI не работает на новых устройствах: iPad 2, the new iPad и iPhone 4S. Для выяснения причин я отправился на StackOverflow, где мои опасения подтвердились. Поиски привели меня тогда к фреймворку под названием CoreTelephony.
$ nm -g ./CoreTelephony | grep -i imei
U _kCFAbsoluteTimeIntervalSince1970
00053b28 S _kCTMobileEquipmentInfoIMEI
00053ad4 S _kCTPostponementInfoIMEI
00053ac4 S _kCTPostponementStatusErrorDefaultIMEI
$ nm -g ./CoreTelephony | grep MobileEquipment
000260e4 T __CTServerConnectionCopyMobileEquipmentInfo
00053b34 S _kCTMobileEquipmentInfo1xIMSI
00053b20 S _kCTMobileEquipmentInfoCurrentMobileId
00053b24 S _kCTMobileEquipmentInfoCurrentSubscriberId
00053b40 S _kCTMobileEquipmentInfoERIVersion
00053b2c S _kCTMobileEquipmentInfoICCID
00053b28 S _kCTMobileEquipmentInfoIMEI
00053b30 S _kCTMobileEquipmentInfoIMSI
00053b38 S _kCTMobileEquipmentInfoMEID
00053b44 S _kCTMobileEquipmentInfoMIN
00053b3c S _kCTMobileEquipmentInfoPRLVersion
Можно предположить что функция (_CTServerConnectionCopyMobileEquipmentInfo(...)) возвращает словарь(CFDictionaryRef) c ключами вида kCTMobileEquipmentInfo* и соответствующими им значениями. К счастью, на этот раз мне не пришлось восстанавливать сигнатуру. Поиск в гугле по запросу _CTServerConnectionCopyMobileEquipmentInfo привел меня на эту страничку [8], и вскоре функция для определения IMEI была готова.
// Заголовочный файл с декларациями недокументированных функций и констант
#include "CoreTelephony.h"
...
NSString* CTGetIMEI
{
struct CTResult it;
NSMutableDictionary *dict;
CTServerConnectionRef conn;
conn = _CTServerConnectionCreate(kCFAllocatorDefault, ConnectionCallback, NULL);
_CTServerConnectionCopyMobileEquipmentInfo(&it, conn, &(CFMutableDictionaryRef)dict);
CFRelease(conn);
[dict autorelease];
return [dict objectForKey: kCTMobileEquipmentInfoIMEI];
}
Этот метод определения IMEI работает на всех устройствах.
Позже я нашел еще один метод определения IMEI (через lockdownd).
Использование недокументированных оповещений: нажатия кнопок громкости.
Изначально я наивно полагал, что любая символьная константа, заканчивающаяся на «Notification» является названием системного оповещения и её можно использовать, просто зарегистрировав наблюдателя (observer) с помощью [NSNotificationCenter defaultCenter].
$ sh ~/Documents/LookSDKForSymbol.sh notification$ volume change
001dbe60 S _MPAVControllerVolumeDidChangeNotification
001dbe64 S _MPAVControllerVolumeMutedDidChangeNotification
001dc4f8 S _MPMusicPlayerControllerVolumeDidChangeNotification
001dc314 S _MPVolumeViewRouteButtonChangedNotification
001dc310 S _MPVolumeViewVisibilityChangedNotification
Found in ./System/Library/Frameworks/MediaPlayer.framework/MediaPlayer000d6d24 D _AVController_EffectiveVolumeDidChangeNotification
000d6d60 D _AVController_VolumeDidChangeNotification
000d6fec D _AVSystemController_CurrentRouteHasVolumeControlDidChangeNotification
000d6ffc D _AVSystemController_EffectiveVolumeDidChangeNotification
000d6fdc D _AVSystemController_SystemVolumeDidChangeNotification
Found in ./System/Library/PrivateFrameworks/Celestial.framework/Celestial
… и еще около десятка из других фреймворков
Написав тестовую программу, я принялся проверять, какие оповещения приходили в ответ на нажатия клавиш громкости.
Из составленного мной довольно большого списка опопвещений приходили только вот эти 2:
AVController_EffectiveVolumeDidChangeNotification
AVController_VolumeDidChangeNotification
Недостаток этих оповещений в том, что
1) Нельзя напрямую определить, какая из двух кнопок была нажата
2) Нельзя отследить, когда нажата и когда отпущена каждая из кнопок
Ищу по другим ключевым словам:
$ sh ~/Documents/LookSDKForSymbol.sh volume button
001b221c t -[UIApplication setWantsVolumeButtonEvents:]
003cce5c t _SBSetWantsVolumeButtonEvents$shim
0054478c S __UIApplicationVolumeDownButtonDownNotification
00544790 S __UIApplicationVolumeDownButtonUpNotification
00544784 S __UIApplicationVolumeUpButtonDownNotification
00544788 S __UIApplicationVolumeUpButtonUpNotification
Found in ./System/Library/Frameworks/UIKit.framework/UIKit
… и еще несколько десятков из разных фреймворкрв
Четыре оповещения из UIKit сработали не сразу: необходимо было подать связанную с ними команду.
[[UIApplication sharedApplication] setWantsVolumeButtonEvents: YES];
После этого стали приходить оповещения о нажатиях соответствующих кнопок.
Побочный эффект: вызов данной функции приводит к тому, что кнопки громкости больше не регулируют громкость, так что по завершении работы с кнопками следует вызвать
[[UIApplication sharedApplication] setWantsVolumeButtonEvents: NO];
Использование недокументированных оповещений: отслеживание статуса SIM-карты
Работаем по проверенной схеме:
$ sh ~/Documents/LookSDKForSymbol.sh notification$ SIM
…
00052560 S _kCTSIMSupportSIMInsertionNotification
00052564 S _kCTSIMSupportSIMStatusChangeNotification
…
000525bc S _kCTSIMSupportSIMTrayStatusNotification
…
Found in ./System/Library/Frameworks/CoreTelephony.framework/CoreTelephony
…
Found in ./System/Library/PrivateFrameworks/FTServices.framework/FTServices
$
Наиболее подходящими мне показались оповещения под названиями:
1) kCTSIMSupportSIMInsertionNotification
2) kCTSIMSupportSIMStatusChangeNotification
3) kCTSIMSupportSIMTrayStatusNotification
Простейшая тестовая программа показала, что оповещения под названием (1) приходили только в момент вставки сим-карты (я мог бы догадаться и раньше по названию), (2) приходили именно тогда когда мне нужно (при вставке и вынимании), оповещения (3) не приходили вообще. Позже я узнал, что оповещения (3) относятся к специальному центру оповещений под названием CTTelephonyCenter. Об использовании CTTelephonyCenter можно прочитать здесь [9].
Оповещения о статусе SIM:
#include "CoreTelephony.h"
- (void) notificationCallback:(NSNotification)notification
{
...
}
- (void) startUpdateSIMStatus
{
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(notificationCallback:)
name:kCTSIMSupportSIMStatusChangeNotification
object:nil
];
}
- (void) stopUpdateSIMStatus
{
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:kCTSIMSupportSIMStatusChangeNotification
object:nil];
}
www.iphonedevwiki.net/index.php/AudioServices [10] — здесь описаны недокументированные константы типа SystemSoundID для проигрывания стандартных коротких (< 30сек) звуков, таких как нажатие кнопок. Можно и самому их все найти, просто перебирая значения от 1000 в цикле.
Еще можно проигрывать стандартные рингтоны:
- (void) playDefaultRingTone
{
NSURL *defaultRingTone = [NSURL URLWithString:@"/System/Library/CoreServices/SpringBoard.app/ring.m4r"];
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:defaultRingTone error:nil];
[player play];
}
Как известно, у объектов класса UIView обычно есть родительский вид (superview) и могут быть дочерние виды (subviews). Верхушкой (как мы дальше увидим, верхушками) этой иерархии являются объект(ы) UIWindow. Что если пройти по всей иерархии? Тут есть тольк одна тонкость: как это не странно, в программе может быть больше одного объекта типа UIWindow.
Для получения __всех__ окон я использовал недокументированную функцию
+ [UIWindow allWindowsIncludingInternalWindows: onlyVisibleWindows:]
При помощи неё мне удалось обнаружить что в самом обычном приложении может быть до четырех окон! (UIWindow)
1) обычное окно программы
2) окно для статус-бара (его также можно получить при помощи — [UIApplication statusBarWindow] )
3) окно для UIAlertView (Родительский вид для объектов типа UIAlertView).
4) окно для экранной клавиатуры.
Какую пользу мы можем из этого извлечь?
Очевидно, что с объектами последних трех типов мы можем обращаться так же, как и с первым.
В частности, мы можем:
— изменять внешний вид UIAlertView, добавлять текстовые поля, переключатели и т.д.
— изменять внешний вид статус-бара, добавлять свои индикаторы и удалять стандартные.
— изменять внешний вид экранной клавиатуры: например, изменять вид кнопок, добавить свои кнопки, переключатели и т.д
Но это еще не все, есть также неожиданные побочные последствия. Часть графических элементов может быть напрямую связана с тем или иным более глубоким функционалом.
@interface UIStatusBarSignalStrengthItemView : UIStatusBarItemView {
@private
int _signalStrengthRaw;
int _signalStrengthBars;
BOOL _enableRSSI;
BOOL _showRSSI;
}
-(id)_stringForRSSI;
-(float)extraRightPadding;
-(void)touchesEnded:(id)ended withEvent:(id)event;
-(id)contentsImageForStyle:(int)style;
-(BOOL)updateForNewData:(id)newData actions:(int)actions;
@end
Так, например, получив доступ к UIStatusBarSignalStrengthItemView мы имеем возможность вполне легально получать значения RSSI (мощность принимаемого сигнала) сотовой сети, и отображать в любой удобной форме.
Полный листинг кода, рекурсивно описывающего все видимые объекты в программе. Вызывается через [UIView completeDescription]:
@interface UIView (RecursiveDescription)
- (void) recursiveDescription;
+ (void) completeDescription
@end
@implementation UIView (RecursiveDescription)
- (void) recursiveDescription
{
NSLog(@"______________________________________");
NSLog(@"%@", self);
NSArray *subviews_ = [self subviews];
if ([subviews_ count] > 0) {
for (UIView *subview_ in subviews_) {
[subview_ recursiveDescription];
}
}
}
- (void)completeDescription
{
NSArray *windows = [UIWindow allWindowsIncludingInternalWindows:YES onlyVisibleWindows:NO];
for (UIView *view in windows) {
[view recursiveDescription];
}
}
@end
Подсистема MIG (MIG-subsystem, Mach Interface Generator) — интерфейс взаимодействия с ядром операционной системы (так называемый «Mach kernel»). Реализация MIG-subsystem для Mac OS X лежит где-то здесь: www.opensource.apple.com/source/xnu/xnu-1228.0.2/libsyscall/mach/ [11]. Смотри также список известных низкоуровневых сообщений: www.iphonedevwiki.net/index.php/MIG_subsystem [12].
IORegistry, I/O registry — реестр ввода-вывода; древовидная структура, описывающая аппаратное обеспечение iPhone и взаимодействие с аппаратными компонентами. Примеры использования IORegistry можно найти в проектах Эрики Садун (см. UIDevice-IOKitExtensions [5]).
nm — UNIX-утилита, выводящая таблицу символов объектнового файла.
На основе nm я написал простой (и довольно тупой) bash-скрипт, который ищет по всем библиотекам и объектным файлам внутри iOS SDK.
LookSDKForSymbol.sh:
#!/bin/bash
SDK=/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk
PrivateFrameworks=$SDK/System/Library/PrivateFrameworks
Frameworks=$SDK/System/Library/Frameworks
Lib=$SDK/usr/lib
cd $SDK
for i in $(find -f .); do
test -f $i && nm $i | c++filt | grep -i "$1" | grep -i "$2" | grep -i "$3" && echo "Found in $i
";
done
c++filt — восстановление (demangling [13]) имён. Только для С++; имена objective-C и просто С идут сразу в человекочитаемом виде.
otool — стандартная утилита для анализа и дизассемблирования объектных файлов.
hexdump — дамп он дамп и есть :-)
class-dump-z — суперполезная утилита. Позволяет генерировать заголовочный файл из объектного файла. Описание всех структур, протоколов, классов, категорий, их методов, свойств и так далее.
Репозиторий на Google Code [14] — здесь можно ознакомиться с более подробным описанием и скачать исходный код этого проекта.
Hex-Rays ARM Decompiler — плагин для HEX-Rays IDA, декомпилятор ARM кода. Официальный сайт. [15]
Утилиты Эрики Садун — на сайте Эрики есть несколько полезных утилит, например, утилита для анализа оповещений.
www.iphonedevwiki.net/ [16]
Единственная известная мне wiki, относящаяся к jailbreak разработке. Хотя информация и устарела
(iOS 3.x, iOS 4.x), но все равно это крайне полезный ресурс.
1. Джей Фримен (Jay Freeman, saurik)
www.saurik.com [17]
2. Эрика Садун (Erica Sadun) — автор книг «The iPhone Developer's CookBook» и «The iOS 5 Developer's Cookbook: Core Concepts and Essential Recipes for iOS Programmers», а также многих полезных утилит. В её книгах описываются некоторые недокументированные возможности публичных фреймворков.
ericasadun.com/ [18]
Некоторые проекты Эрики Садун:
github.com/erica/iOS-5-Cookbook [19]
github.com/erica/iphone-3.0-cookbook- [20]
github.com/erica/uidevice-extension [21]
3. KennyTM / networkpx — создатель class-dump-z, активный участник StackOverflow.com в вопросах, связанных с недокументированными API
networkpx.blogspot.com/ [22]
code.google.com/p/networkpx/ [23]
github.com/kennytm [24]
stackoverflow.com/users/224671/kennytm [25]
github.com/kennytm/iphone-private-frameworks [26]
Информация сильно устарела (iOS 3).
«The iPhone Developer's CookBook» (также «The iOS 5 Developer's Cookbook: Core Concepts and Essential Recipes for iOS Programmers») — книга о разработке под iOS, с уклоном в недокументированные возможности iOS.
Русский перевод:
translated.by/you/iphone-developers-cookbook-development-native-applications-for-the-iphone/into-ru/trans/ [27]
Автор: Trahman
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ios/13474
Ссылки в тексте:
[1] «Как Apple узнаёт, что ты используешь приватные API?»: http://stackoverflow.com/questions/2842357/how-does-apple-know-you-are-using-private-api
[2] Ниже: #a1
[3] «Инструменты»: #Instruments
[4] «Как узнать сигнатуру неизвестной функции?»: #Signature
[5] UIDevice-IOKitExtensions: #uidevice-extension
[6] этот пост: http://stackoverflow.com/questions/8289141/audioservicesaddsystemsoundcompletion-callback-not-getting-called-in-silent-mode
[7] VSSilentSwitch: http://cocoacontrols.com/platforms/ios/controls/vssilentswitch
[8] эту страничку: http://zhiwei.li/text/2012/02/iphone%E5%8F%96imei%E7%9A%84%E5%87%BD%E6%95%B0%E6%9E%9C%E7%84%B6%E4%B8%8D%E9%9D%A0%E8%B0%B1/
[9] здесь: http://blogs.oreilly.com/iphone/2008/08/iphone-notifications.html
[10] www.iphonedevwiki.net/index.php/AudioServices: http://www.iphonedevwiki.net/index.php/AudioServices
[11] www.opensource.apple.com/source/xnu/xnu-1228.0.2/libsyscall/mach/: http://www.opensource.apple.com/source/xnu/xnu-1228.0.2/libsyscall/mach/
[12] www.iphonedevwiki.net/index.php/MIG_subsystem: http://www.iphonedevwiki.net/index.php/MIG_subsystem
[13] demangling: http://en.wikipedia.org/wiki/Name_mangling
[14] Репозиторий на Google Code: http://code.google.com/p/networkpx/wiki/class_dump_z
[15] Официальный сайт.: http://www.hex-rays.com/products/decompiler/compare_arm0.shtml
[16] www.iphonedevwiki.net/: http://www.iphonedevwiki.net/
[17] www.saurik.com: http://www.saurik.com
[18] ericasadun.com/: http://ericasadun.com/
[19] github.com/erica/iOS-5-Cookbook: https://github.com/erica/iOS-5-Cookbook
[20] github.com/erica/iphone-3.0-cookbook-: https://github.com/erica/iphone-3.0-cookbook-
[21] github.com/erica/uidevice-extension: https://github.com/erica/uidevice-extension
[22] networkpx.blogspot.com/: http://networkpx.blogspot.com/
[23] code.google.com/p/networkpx/: http://code.google.com/p/networkpx/
[24] github.com/kennytm: https://github.com/kennytm
[25] stackoverflow.com/users/224671/kennytm: http://stackoverflow.com/users/224671/kennytm
[26] github.com/kennytm/iphone-private-frameworks: https://github.com/kennytm/iphone-private-frameworks
[27] translated.by/you/iphone-developers-cookbook-development-native-applications-for-the-iphone/into-ru/trans/: http://translated.by/you/iphone-developers-cookbook-development-native-applications-for-the-iphone/into-ru/trans/
Нажмите здесь для печати.