iOS Runtime Apple iOS آپریٹنگ سسٹم پر ایپلیکیشن رن ٹائم ماحول ہے، جس میں Objective-C Runtime، Swift Runtime، Cocoa Touch فریم ورکس اور Automatic Reference Counting (ARC) کے ذریعے میموری مینجمنٹ میکانزم شامل ہیں۔ iOS Runtime ڈائنامک میتھڈ بائنڈنگ (message passing)، کلاس لوڈنگ، میموری مینجمنٹ اور iOS فریم ورکس کے ذریعے ہارڈویئر کے ساتھ تعامل کا ذمہ دار ہے۔ Apple ڈیولپر دستاویزات کے مطابق، کارکردگی کی اصلاح، ڈیبگنگ اور مستحکم iOS ایپلیکیشنز تیار کرنے کے لیے runtime کو سمجھنا ضروری ہے۔
اہم نکات
iOS Runtime سسٹم اجزاء کا ایک مجموعہ ہے جو iOS چلانے والے Apple آلات پر ایپلیکیشن کے عمل کو یقینی بناتا ہے۔ اس میں Objective-C Runtime (libobjc.A.dylib لائبریری)، Swift Runtime (libswiftCore.dylib)، Core Foundation، Cocoa Touch فریم ورکس (UIKit، Foundation)، ڈائنامک لوڈر dyld اور میموری مینجمنٹ، تھریڈز اور انٹر پروسیس کمیونیکیشن کے لیے رن ٹائم ماحول شامل ہے۔
آرکیٹیکچرل طور پر، iOS Runtime تین سطحوں پر کام کرتا ہے۔ نچلی ترین سطح پر — Mach-O بائنری فارمیٹ اور dyld، جو قابل عمل فائل اور لائبریریاں لوڈ کرتا ہے۔ درمیانی سطح — Objective-C Runtime اور Swift Runtime، میتھڈ ڈسپیچ اور آبجیکٹ مینجمنٹ کے ذمہ دار۔ بالائی سطح — Cocoa Touch فریم ورکس (UIKit، Foundation، Core Data، Metal)، جو ڈیولپر کو APIs فراہم کرتے ہیں۔
iOS Runtime کو سمجھنا ڈیولپر کو پیچیدہ مسائل حل کرنے کی اجازت دیتا ہے: A/B ٹیسٹنگ اور اینالیٹکس کے لیے میتھڈ سوئزلنگ (Method Swizzling)، ڈائنامک کلاس لوڈنگ، ARC کو سمجھ کر میموری کی اصلاح، retain cycles اور میموری لیک کی ڈیبگنگ، dyld کے ذریعے ایپلیکیشن لانچ ٹائم کی اصلاح۔ Runtime علم کے بغیر، سسٹم کی سطح پر پروفائلنگ اور اصلاح ناممکن ہے۔
| جزو | لائبریری | مقصد |
|---|---|---|
| Objective-C Runtime | libobjc.A.dylib | Message passing، ڈائنامک کلاسز، سوئزلنگ |
| Swift Runtime | libswiftCore.dylib | Value types، generics، protocol witnesses |
| Core Foundation | CoreFoundation.framework | CFType، toll-free bridging |
| dyld | dyld (usr/lib/dyld) | Mach-O لوڈنگ، لائبریری لنکنگ |
| libSystem | libSystem.B.dylib | POSIX threads، libc، libdispatch (GCD) |
iOS کے لیے ایپلیکیشنز Mach-O فارمیٹ (Mach Object) میں کمپائل ہوتی ہیں۔ Mach-O فائل میں ہیڈر، لوڈ کمانڈز اور سیگمنٹس ہوتے ہیں: __TEXT (کوڈ، مستقل)، __DATA (گلوبل متغیرات، Objective-C میٹا ڈیٹا)، __LINKEDIT (علامتیں، ری لوکیشن ٹیبلز)۔ dyld پہلی ہدایت پر عمل کرنے سے پہلے Mach-O کو پارس کرتا ہے اور انحصار لوڈ کرتا ہے۔
Objective-C Runtime iOS Runtime کا سب سے طاقتور حصہ ہے۔ ابتدائی بائنڈنگ (early binding) والے C++ کے برعکس، Objective-C میسج پاسنگ کے ذریعے دیر سے بائنڈنگ (late binding) استعمال کرتا ہے۔ میتھڈ کال [receiver message] براہ راست فنکشن کال میں نہیں، بلکہ objc_msgSend(receiver, @selector(message)) میں کمپائل ہوتی ہے، جو آبجیکٹ کی کلاس میں میتھڈ کے نفاذ کو ڈائنامک طور پر ڈھونڈتی ہے۔
ہر Objective-C آبجیکٹ اپنی کلاس کی طرف isa پوائنٹر محفوظ کرتا ہے۔ کلاس میں میتھڈ لسٹ، میتھڈ کیش اور سپرکلاس کا پوائنٹر ہوتا ہے۔ objc_msgSend وراثت کی زنجیر کو traverse کرتا ہے: کلاس کیش چیک کرتا ہے، پھر میتھڈ لسٹ، پھر سپرکلاس کی طرف جاتا ہے۔ اگر میتھڈ نہ ملے تو فارورڈنگ متحرک ہوتی ہے: resolveInstanceMethod، forwardingTargetForSelector اور forwardInvocation۔
Method Swizzling ایک تکنیک ہے جو runtime میں IMP (نفاذ پوائنٹر) کے تبادلے کے ذریعے میتھڈ کے نفاذ کو فوری طور پر تبدیل کرتی ہے۔ یہ A/B ٹیسٹنگ، اینالیٹکس (خودکار اسکرین ٹریکنگ) اور مانیٹرنگ کے لیے استعمال ہوتی ہے۔ شدید ضرورت کے بغیر پروڈکشن کے لیے تجویز نہیں کی جاتی، کیونکہ یہ OS اپ ڈیٹس سے متصادم ہو سکتی ہے۔
// viewDidLoad ٹریکنگ کے لیے Method Swizzling
#import
@implementation UIViewController (Tracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewDidLoad);
SEL swizzledSelector = @selector(swizzled_viewDidLoad);
Method originalMethod = class_getInstanceMethod(
class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(
class, swizzledSelector);
BOOL didAddMethod = class_addMethod(
class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod)
);
if (didAddMethod) {
class_replaceMethod(
class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod)
);
} else {
method_exchangeImplementations(
originalMethod, swizzledMethod);
}
});
}
- (void)swizzled_viewDidLoad {
// ایونٹ ٹریکنگ
NSLog(@"View Did Load: %@", self.class);
// اصلی نفاذ کو کال کرنا
[self swizzled_viewDidLoad];
}
@end UIViewController (Tracking) زمرہ ایپلیکیشن میں تمام UIViewControllers میں viewDidLoad کو swizzled_viewDidLoad سے بدل دیتا ہے۔ dispatch_once ایک بار کے سوئزلنگ کی ضمانت دیتا ہے۔ class_addMethod ڈبل سوئزلنگ اور سپرکلاسز کے ساتھ تصادم کو روکتا ہے۔ یہ کنٹرولر سورس کوڈ میں ترمیم کیے بغیر اینالیٹکس میں خودکار اسکرین ویو ٹریکنگ کے لیے استعمال ہوتا ہے۔
جدید iOS (arm64) میں، Apple نے isa پوائنٹر کو بہتر بنایا: یہ صرف ایک کلاس ایڈریس نہیں ہے، بلکہ ایک بٹ فیلڈ (non-pointer isa) ہے جس میں میموری مینجمنٹ فلیگز اور کلاس کی معلومات ہوتی ہیں۔ ٹیگڈ پوائنٹرز ایک اور اصلاح ہے: چھوٹی NSNumber، NSDate اور NSString ویلیوز ہیپ پر آبجیکٹ کے طور پر نہیں، بلکہ براہ راست پوائنٹر میں محفوظ ہوتی ہیں، جس سے malloc اور retain/release کا اوور ہیڈ ختم ہوتا ہے۔ ٹیگڈ پوائنٹر isa کے کم سے کم اہم بٹ سے پہچانا جاتا ہے۔
Swift Runtime Objective-C Runtime سے بنیادی طور پر مختلف ہے: Swift بطور ڈیفالٹ کلاس میتھڈز کے لیے vtable اور value types اور ایکسٹینشن میتھڈز کے لیے direct call کے ذریعے سٹیٹک ڈسپیچ (static dispatch) استعمال کرتا ہے۔ ڈائنامک ڈسپیچ (dynamic dispatch) صرف @objc یا dynamic سے نشان زدہ میتھڈز کے لیے استعمال ہوتا ہے۔ یہ Objective-C کے مقابلے میں 40% تک کارکردگی میں بہتری فراہم کرتا ہے۔
Value types (struct، enum) Swift میں Objective-C سے ایک اہم فرق ہے۔ یہ اسٹیک پر یا کسی دوسرے آبجیکٹ کے اندر محفوظ ہوتے ہیں، retain/release استعمال نہیں کرتے اور حوالہ گنتی کے لیے ARC میں حصہ نہیں لیتے۔ Struct میں isa پوائنٹر نہیں ہے اور اسے objc_msgSend کے ذریعے نہیں بھیجا جا سکتا۔ Protocol witnesses پروٹوکولز کے لیے vtable کے مشابہ ہیں، جو existential containers کے لیے ڈائنامک ڈسپیچ کو قابل بناتے ہیں۔
Swift Runtime میں تجرید (mangled symbols کے ذریعے reified generics) کے ساتھ generics اور string، array، dictionary، set کو بہتر بنانے کے لیے COW (Copy-on-Write) بھی شامل ہے۔ کسی مجموعہ کو کاپی کرتے وقت، حقیقی کاپی صرف اس وقت ہوتی ہے جب کاپیوں میں سے ایک میں ترمیم کی جائے۔ یہ فنکشنز کے درمیان مجموعہ منتقل کرتے وقت اوور ہیڈ کو کم سے کم کرتا ہے۔
import Foundation
// Swift: سٹیٹک ڈسپیچ (کلاس کے لیے vtable)
class Animal {
func makeSound() { print("...") } // vtable
}
class Dog: Animal {
override func makeSound() { print("Woof") } // vtable override
}
// @objc dynamic: Objective-C Runtime ڈسپیچ
class Cat: Animal {
@objc dynamic override func makeSound() {
print("Meow")
} // objc_msgSend
}
// Struct — کوئی runtime ڈسپیچ نہیں
struct Cow {
func makeSound() { print("Moo") } // direct call
}
// Protocol with protocol witness
protocol SoundMaker {
func makeSound()
}
struct Duck: SoundMaker {
func makeSound() { print("Quack") }
}
// existential container کا استعمال
let soundMakers: [SoundMaker] = [Dog(), Cow(), Duck()]
for maker in soundMakers {
maker.makeSound() // protocol witness dispatch
}
// کارکردگی کی جانچ
func testDispatch() {
let dog = Dog()
let cat = Cat()
var cow = Cow()
let start = CFAbsoluteTimeGetCurrent()
for _ in 0..<1000000 {
dog.makeSound() // vtable: ~3ns
cat.makeSound() // objc_msgSend: ~15ns
cow.makeSound() // direct: ~1ns
}
let elapsed = CFAbsoluteTimeGetCurrent() - start
print("Elapsed: (elapsed) sec")
}مثال Swift میں تین قسم کے ڈسپیچ کو ظاہر کرتی ہے: class (Dog) کے لیے vtable، @objc dynamic (Cat) کے لیے objc_msgSend اور struct (Cow) کے لیے direct call۔ existential containers ([SoundMaker]) میں protocol witnesses اوور ہیڈ شامل کرتے ہیں۔ عملی طور پر، Swift جہاں ممکن ہو سٹیٹک ڈسپیچ کا انتخاب کرتا ہے، جو C کے قریب کارکردگی فراہم کرتا ہے۔
Swift Runtime Objective-C Runtime کے ساتھ مکمل مطابقت کے لیے ڈیزائن کیا گیا ہے۔ NSObject سے وراثت پانے والی کوئی بھی Swift کلاس خود بخود Objective-C Runtime میں رجسٹر ہو جاتی ہے اور objc_msgSend کے ذریعے کال کی جا سکتی ہے۔ @objc وصف Swift میتھڈ کو Objective-C سے قابل رسائی بناتا ہے۔ String برج: Swift String Objective-C API (toll-free bridging) میں منتقل ہونے پر خود بخود NSString میں برج ہو جاتا ہے۔
ARC (Automatic Reference Counting) iOS میں ایک میموری مینجمنٹ سسٹم ہے جو کمپائل وقت پر کام کرتا ہے۔ کمپائلر (Clang) آبجیکٹ کی زندگی کا تجزیہ کرتا ہے اور خود بخود retain/release/autorelease کالز داخل کرتا ہے۔ ڈیولپر کو انہیں دستی طور پر کال کرنے کی ضرورت نہیں ہے — iOS 5 سے پہلے کے Manual Retain-Release (MRR) کے برعکس۔ ARC Objective-C اور Swift آبجیکٹ کی سطح پر کام کرتا ہے، لیکن value types (struct، enum) کے لیے نہیں۔
ہر Objective-C اور Swift کلاس آبجیکٹ کا ایک حوالہ شمار (retain count) ہے، جو non-pointer isa کے اندر extra_rc فیلڈ میں محفوظ ہے۔ جب کوئی آبجیکٹ بنایا جاتا ہے، retain count = 1 ہے۔ retain پر کاؤنٹر بڑھتا ہے، release پر گھٹتا ہے۔ جب کاؤنٹر 0 تک پہنچتا ہے، آبجیکٹ dealloc (Objective-C) یا deinit (Swift) کے ذریعے ڈی لوکیٹ ہو جاتا ہے۔ ARC تھریڈ سے محفوظ ہے: retain/release ایٹمی آپریشنز (OSAtomicIncrement32/OSAtomicDecrement32) استعمال کرتے ہیں۔
Retain cycles ARC کا سب سے بڑا مسئلہ ہے۔ اگر آبجیکٹ A کے پاس B کا strong حوالہ ہے، اور B کے پاس A کا strong حوالہ ہے، تو دونوں آبجیکٹ کبھی ڈی لوکیٹ نہیں ہوں گے کیونکہ ان کے حوالہ شمار کبھی صفر تک نہیں پہنچیں گے۔ حل weak حوالہ جات ہیں (Objective-C میں __weak، Swift میں weak) یا unowned حوالہ جات۔ Weak حوالہ جات retain count نہیں بڑھاتے اور آبجیکٹ کے ڈی لوکیٹ ہونے پر خود بخود صفر (nil) ہو جاتے ہیں۔
import Foundation
// Retain cycle کی مثال
class Parent {
var child: Child?
deinit { print("Parent deallocated") }
}
class Child {
var parent: Parent? // strong — retain cycle بناتا ہے!
deinit { print("Child deallocated") }
}
var parent: Parent? = Parent()
var child: Child? = Child()
parent?.child = child
child?.parent = parent // چکر: Parent -> Child -> Parent
parent = nil
child = nil
// deinit کال نہیں ہوا — میموری لیک!
// اصلاح: weak
class WeakChild {
weak var parent: Parent? // weak — retain count نہیں بڑھاتا
deinit { print("WeakChild deallocated") }
}
// اصلاح: unowned (ضمانتی زندگی کے لیے)
class UnownedChild {
unowned let parent: Parent
init(parent: Parent) { self.parent = parent }
deinit { print("UnownedChild deallocated") }
}
// Instruments کے ذریعے تصدیق
func profileMemory() {
// 1. Instruments > Leaks چلائیں
// 2. آبجیکٹ بنانے والی کارروائی کریں
// 3. لیک کے لیے Leaks چیک کریں
// 4. Allocations میں dealloc کے بغیر آبجیکٹ تلاش کریں
for _ in 0..<1000 {
let p = Parent()
let c = WeakChild()
p.child = c as? Child
// c.parent = p — شامل نہیں کر رہے، weak
}
}Parent اور Child کے درمیان retain cycle کی مثال: دونوں ایک دوسرے کے strong حوالہ جات رکھتے ہیں، ARC کاؤنٹر صفر نہیں کر سکتا۔ اصلاح Child میں weak parent ہے۔ weak parent کے ڈی لوکیٹ ہونے پر خود بخود صفر ہو جاتا ہے۔ unowned ان صورتوں کے لیے ہے جب parent کی زندگی child سے زیادہ لمبی ہونے کی ضمانت ہو (مثال کے طور پر، viewController اور view)۔ retain cycles کو جلد پکڑنے کے لیے Instruments > Leaks استعمال کریں۔
Autorelease pool واضح ملکیت کے بغیر بنائے گئے آبجیکٹ کے لیے تاخیری ریلیز میکانزم ہے۔ Swift اور Objective-C میں @autoreleasepool { } ایک پول بناتا ہے جو بلاک کے آخر میں خالی ہو جاتا ہے، پول میں ہر آبجیکٹ کو release بھیجتا ہے۔ یہ لوپس (ہزاروں عارضی آبجیکٹ بنانے) اور RunLoop کے بغیر بیک گراؤنڈ تھریڈز پر انتہائی اہم ہے۔ UIKit RunLoop ہر تکرار پر مرکزی autorelease pool کو خود بخود خالی کرتا ہے۔
dyld (dynamic link editor) سسٹم لوڈر ہے جو iOS ایپلیکیشن لانچ کرتے وقت Mach-O قابل عمل فائلوں اور متعلقہ ڈائنامک لائبریریوں (dylib) کو لوڈ کرنے کا ذمہ دار ہے۔ dyld /usr/lib/dyld پر واقع ہے اور libSystem کا حصہ ہے۔ لوڈنگ کے عمل میں کئی مراحل شامل ہیں: Mach-O پارسنگ، انحصار لوڈنگ (Library Loader، LC_LOAD_DYLIB)، ایڈریس ری لوکیشن (ASLR)، Objective-C Runtime ابتداء اور main() کال۔
ایپلیکیشن لانچ کا وقت dyld پر بہت زیادہ منحصر ہے: جتنی زیادہ ڈائنامک لائبریریاں اور Objective-C کلاسیں، اتنا ہی لمبا pre-main time۔ Apple +load میتھڈز کی تعداد کم سے کم کرنے کی تجویز کرتا ہے (یہ main سے پہلے عمل میں آتے ہیں)، انہیں +initialize (سست ابتداء) سے بدل دیں۔ 2020 سے، Apple iOS پر پہلے سے تیار شدہ dyld کیش استعمال کرتا ہے: سسٹم لائبریریاں ایک کیش میں پہلے سے لنک ہوتی ہیں، جس سے لوڈنگ تیز ہوتی ہے۔
import Foundation
// DYLD_PRINT_STATISTICS کے ذریعے لانچ وقت کی پیمائش
// Xcode میں: Edit Scheme > Run > Arguments > Environment Variables
// DYLD_PRINT_STATISTICS = 1
// DYLD_PRINT_STATISTICS_DETAILS = 1
// Pre-main time کی پروگرامی پیمائش
@main
struct AppMain {
static func main() {
let launchStart = CFAbsoluteTimeGetCurrent()
// UIApplicationMain یہاں ہوتا ہے
AppDelegate.main()
let launchEnd = CFAbsoluteTimeGetCurrent()
let preMainTime = launchEnd - launchStart
print("Pre-main time: (preMainTime) sec")
}
}
// اصلاح: +load کو +initialize سے بدلنا
class OptimizedClass {
// ❌ +load main سے پہلے عمل میں آتا ہے
// override class func load() { }
// ✅ +initialize پہلی رسائی پر عمل میں آتا ہے
static let shared = OptimizedClass()
private init() {
// یہاں ابتداء
}
}
// dylib تعداد کی اصلاح
// سٹیٹک لائبریریوں کا انضمام LC_LOAD_DYLIB کی تعداد کم کرتا ہے
// صرف استعمال شدہ Objective-C کلاسز کو لنک کرنے کے لیے -ObjC فلیگ استعمال کریں
// Xcode: Build Settings > Mach-O Type > Static Librarypre-main time کی پیمائش کے لیے، Xcode سکیما میں DYLD_PRINT_STATISTICS استعمال کریں۔ آؤٹ پٹ کل وقت، dylib لوڈنگ کا وقت، rebase/bind کا وقت، Objective-C سیٹ اپ کا وقت اور انیشیالائزر کا وقت دکھاتا ہے۔ ہدف کی قدریں: کولڈ اسٹارٹ کے لیے کل < 400ms، وارم اسٹارٹ کے لیے < 200ms۔ اصلاح: لائبریریوں کو ضم کریں، +load کو +initialize سے بدلیں، Objective-C کلاسز کی تعداد کم کریں (Swift استعمال کریں)، ڈائنامک فریم ورکس کی کم سے کم تعداد۔
dyld shared cache iOS پر پہلے سے لنک شدہ سسٹم لائبریریوں کا کیش ہے۔ تمام سسٹم dylib (UIKit, Foundation, CoreGraphics) ایک فائل میں جمع ہیں: /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64۔ یہ ہر سسٹم لائبریری کو الگ سے لوڈ کرنے کی ضرورت ختم کرتا ہے — dyld کیش تک رسائی حاصل کرتا ہے، جس سے اسٹارٹ اپ نمایاں طور پر تیز ہوتا ہے۔ 10+ ڈائنامک فریم ورکس والی ایپلیکیشنز سب سے زیادہ تاخیر کا سامنا کرتی ہیں، کیونکہ کسٹم dylib dsc میں شامل نہیں ہیں۔
اکثر پوچھے گئے سوالات
iOS Runtime iOS پر ایپلیکیشن رن ٹائم ماحول ہے، جس میں Objective-C Runtime (libobjc.dylib)، Swift Runtime (libswiftCore.dylib)، Cocoa Touch فریم ورکس، dyld (ڈائنامک لوڈر) اور ARC (میموری مینجمنٹ) شامل ہیں۔ یہ Objective-C کے لیے message passing، Swift کے لیے سٹیٹک ڈسپیچ، Mach-O فائل لوڈنگ اور خودکار میموری مینجمنٹ فراہم کرتا ہے۔
Objective-C Runtime دیر سے بائنڈنگ کے ساتھ objc_msgSend (message passing) کے ذریعے ڈائنامک بائنڈنگ استعمال کرتا ہے۔ Swift Runtime کارکردگی کے لیے سٹیٹک ڈسپیچ (کلاسز کے لیے vtable، struct کے لیے direct call) استعمال کرتا ہے۔ @objc dynamic Swift کلاسز کے لیے Objective-C Runtime کو فعال کرتا ہے۔ Swift struct میں isa پوائنٹر نہیں ہے اور یہ retain/release استعمال نہیں کرتا۔
ARC (Automatic Reference Counting) کمپائل ٹائم میموری مینجمنٹ ہے۔ Clang کمپائلر خود بخود retain/release کالز داخل کرتا ہے۔ ہر آبجیکٹ کا ایک حوالہ شمار ہے؛ جب یہ صفر تک پہنچتا ہے، dealloc کہا جاتا ہے۔ Retain cycles (باہمی strong حوالہ جات) weak/unowned حوالہ جات سے روکے جاتے ہیں۔ لیک کا پتہ لگانے کے لیے Instruments > Leaks استعمال کریں۔
dyld Mach-O فائلوں کے لیے ڈائنامک لوڈر ہے۔ یہ قابل عمل فائل اور تمام منحصر dylib لوڈ کرتا ہے، ری لوکیشن (ASLR) کرتا ہے، Objective-C Runtime شروع کرتا ہے اور main() کال کرتا ہے۔ Pre-main time dylib اور +load میتھڈز کی تعداد پر منحصر ہے۔ پیمائش کے لیے DYLD_PRINT_STATISTICS استعمال کریں۔ اصلاح: لائبریریوں کو ضم کریں، +load کو +initialize سے بدلیں۔
Method Swizzling Objective-C Runtime کے class_getInstanceMethod اور method_exchangeImplementations کے ذریعے میتھڈ کے IMP (نفاذ پوائنٹر) کو فوری طور پر تبدیل کرنے کی تکنیک ہے۔ یہ A/B ٹیسٹنگ، اینالیٹکس (خودکار اسکرین ٹریکنگ) اور مانیٹرنگ کے لیے استعمال ہوتی ہے۔ شدید ضرورت کے بغیر پروڈکشن میں تجویز نہیں کی جاتی۔ Swift میں اسے @objc dynamic + Method Swizzling سے بدل دیا جاتا ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں