├── apptest
├── en.lproj
│ └── InfoPlist.strings
├── Images.xcassets
│ ├── AppIcon.appiconset
│ │ ├── icon-29.png
│ │ ├── icon-40.png
│ │ ├── icon-58.png
│ │ ├── icon-76.png
│ │ ├── icon-80.png
│ │ ├── icon-87.png
│ │ ├── icon-120.png
│ │ ├── icon-152.png
│ │ ├── icon-167.png
│ │ ├── icon-180.png
│ │ └── Contents.json
│ └── LaunchImage.launchimage
│ │ ├── launch-1024x768.png
│ │ ├── launch-640x1136.png
│ │ ├── launch-640x960.png
│ │ ├── launch-768x1024.png
│ │ ├── launch-1536x2048.png
│ │ ├── launch-2048x1536.png
│ │ └── Contents.json
├── apptest-Prefix.pch
├── apptest-Info.plist
└── main.m
├── app_packages
├── README
└── rubicon
│ ├── __init__.py
│ └── objc
│ ├── __init__.py
│ ├── types.py
│ ├── core_foundation.py
│ └── objc.py
├── app
├── README
└── apptest
│ └── __main__.py
├── apptest.xcodeproj
├── xcuserdata
│ └── fujun.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── apptest.xcscheme
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── fujun.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── project.pbxproj
├── README.md
└── LICENSE
/apptest/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/app_packages/README:
--------------------------------------------------------------------------------
1 | This directory exists so that 3rd party packages can be installed here.
2 |
--------------------------------------------------------------------------------
/app/README:
--------------------------------------------------------------------------------
1 | Your application code should be placed in this directory.
2 |
3 | The native code will be looking for a apptest/__main__.py file as the entry point.
--------------------------------------------------------------------------------
/apptest/Images.xcassets/AppIcon.appiconset/icon-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/AppIcon.appiconset/icon-29.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/AppIcon.appiconset/icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/AppIcon.appiconset/icon-40.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/AppIcon.appiconset/icon-58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/AppIcon.appiconset/icon-58.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/AppIcon.appiconset/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/AppIcon.appiconset/icon-76.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/AppIcon.appiconset/icon-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/AppIcon.appiconset/icon-80.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/AppIcon.appiconset/icon-87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/AppIcon.appiconset/icon-87.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/AppIcon.appiconset/icon-120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/AppIcon.appiconset/icon-120.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/AppIcon.appiconset/icon-152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/AppIcon.appiconset/icon-152.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/AppIcon.appiconset/icon-167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/AppIcon.appiconset/icon-167.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/AppIcon.appiconset/icon-180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/AppIcon.appiconset/icon-180.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/LaunchImage.launchimage/launch-1024x768.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/LaunchImage.launchimage/launch-1024x768.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/LaunchImage.launchimage/launch-640x1136.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/LaunchImage.launchimage/launch-640x1136.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/LaunchImage.launchimage/launch-640x960.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/LaunchImage.launchimage/launch-640x960.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/LaunchImage.launchimage/launch-768x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/LaunchImage.launchimage/launch-768x1024.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/LaunchImage.launchimage/launch-1536x2048.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/LaunchImage.launchimage/launch-1536x2048.png
--------------------------------------------------------------------------------
/apptest/Images.xcassets/LaunchImage.launchimage/launch-2048x1536.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest/Images.xcassets/LaunchImage.launchimage/launch-2048x1536.png
--------------------------------------------------------------------------------
/apptest.xcodeproj/xcuserdata/fujun.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/apptest.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/apptest.xcodeproj/project.xcworkspace/xcuserdata/fujun.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Forkong/Python-iOS/HEAD/apptest.xcodeproj/project.xcworkspace/xcuserdata/fujun.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/app_packages/rubicon/__init__.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | try:
3 | # If we're on iOS, we won't have pkg-resources; but then,
4 | # we won't need to register the namespace package, either.
5 | # Ignore the error if it occurs.
6 | __import__("pkg_resources").declare_namespace(__name__)
7 | except ImportError:
8 | print('Rubicon namespace package not registered!')
9 |
--------------------------------------------------------------------------------
/apptest/apptest-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header
3 | //
4 | // The contents of this file are implicitly included at the beginning of every source file.
5 | //
6 |
7 | #import
8 |
9 | #ifndef __IPHONE_3_0
10 | #warning "This project uses features only available in iOS SDK 3.0 and later."
11 | #endif
12 |
13 | #ifdef __OBJC__
14 | #import
15 | #import
16 | #endif
17 |
--------------------------------------------------------------------------------
/apptest.xcodeproj/xcuserdata/fujun.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | apptest.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 60796EE119190F4100A9926B
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app_packages/rubicon/objc/__init__.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function, absolute_import, division, unicode_literals
2 |
3 | __version__ = '0.1.3'
4 |
5 | from .objc import (
6 | objc, send_message, send_super,
7 | get_selector,
8 | ObjCClass, ObjCInstance, NSObject,
9 | objc_ivar, objc_rawmethod, objc_method, objc_classmethod
10 | )
11 |
12 | from .core_foundation import at, to_str, to_number, to_value, to_set, to_list
13 |
14 | from .types import (
15 | text,
16 | NSInteger, NSUInteger,
17 | CGFloat,
18 | NSPointEncoding, NSSizeEncoding, NSRectEncoding, NSRangeEncoding,
19 | CGPoint, NSPoint,
20 | CGSize, NSSize,
21 | CGRect, NSRect,
22 | CGSizeMake, NSMakeSize,
23 | CGRectMake, NSMakeRect,
24 | CGPointMake, NSMakePoint,
25 | NSTimeInterval,
26 | CFIndex, UniChar, unichar, CGGlyph,
27 | CFRange, NSRange,
28 | NSZeroPoint
29 | )
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Python-iOS
2 | ----
3 | ### What is this?
4 | ----
5 |
6 | 这是一个使用Python开发iOS程序的demo。详细介绍可以参考这篇博客:
7 |
8 | **[http://ifujun.com/shi-yong-pythonkai-fa-ioscheng-xu/](http://ifujun.com/shi-yong-pythonkai-fa-ioscheng-xu/)**
9 |
10 | 主要使用了:
11 | - [pybee](http://pybee.org/)开源的编译脚本[Python-iOS-support](https://github.com/pybee/Python-iOS-support)。
12 | - [rubicon-objc](https://github.com/pybee/rubicon-objc),一个连接Python和objc的桥梁。
13 | - [cookiecutter](http://cookiecutter.rtfd.org/),一个可以在模板中快速创建工程的一个工具。
14 | - [Python-iOS-template](https://github.com/pybee/Python-iOS-template),[pybee](http://pybee.org/)开源的使用Python开发iOS工程的模板。
15 |
16 | ### How to install it?
17 | ----
18 |
19 | 1. clone下工程。
20 | 2. [下载](https://github.com/pybee/Python-iOS-support/releases)一个可用版本的`Python.framework`和`OpenSSL.framework`。
21 | 3. 将下载好的`Python.framework`和`OpenSSL.framework`放置于工程根目录上,也就是和`xcodeproj`文件同级目录。
22 | 4. 编译运行。
23 |
24 | 运行结果:
25 |
26 | 
27 |
28 | demo中只修改了rootViewController的背景,然后在上面添加了一个label。
29 |
30 | 当然,它还能做很多事情。
31 |
32 | ### License
33 | ----
34 | MIT.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Fujun
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/apptest/Images.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "portrait",
5 | "idiom" : "iphone",
6 | "extent" : "full-screen",
7 | "minimum-system-version" : "7.0",
8 | "filename" : "launch-640x960.png",
9 | "scale" : "2x"
10 | },
11 | {
12 | "extent" : "full-screen",
13 | "idiom" : "iphone",
14 | "subtype" : "retina4",
15 | "filename" : "launch-640x1136.png",
16 | "minimum-system-version" : "7.0",
17 | "orientation" : "portrait",
18 | "scale" : "2x"
19 | },
20 | {
21 | "orientation" : "portrait",
22 | "idiom" : "ipad",
23 | "extent" : "full-screen",
24 | "minimum-system-version" : "7.0",
25 | "filename" : "launch-768x1024.png",
26 | "scale" : "1x"
27 | },
28 | {
29 | "orientation" : "landscape",
30 | "idiom" : "ipad",
31 | "extent" : "full-screen",
32 | "minimum-system-version" : "7.0",
33 | "filename" : "launch-1024x768.png",
34 | "scale" : "1x"
35 | },
36 | {
37 | "orientation" : "portrait",
38 | "idiom" : "ipad",
39 | "extent" : "full-screen",
40 | "minimum-system-version" : "7.0",
41 | "filename" : "launch-1536x2048.png",
42 | "scale" : "2x"
43 | },
44 | {
45 | "orientation" : "landscape",
46 | "idiom" : "ipad",
47 | "extent" : "full-screen",
48 | "minimum-system-version" : "7.0",
49 | "filename" : "launch-2048x1536.png",
50 | "scale" : "2x"
51 | }
52 | ],
53 | "info" : {
54 | "version" : 1,
55 | "author" : "xcode"
56 | }
57 | }
--------------------------------------------------------------------------------
/apptest/apptest-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ${PRODUCT_NAME}
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleIdentifier
12 | com.ifujun.apptest.${PRODUCT_NAME:rfc1034identifier}
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | ${PRODUCT_NAME}
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1.0
25 | LSRequiresIPhoneOS
26 |
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/apptest/__main__.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import, division, print_function, with_statement
2 |
3 | import sys
4 |
5 | from ctypes import cdll, c_int, c_void_p, util
6 | from rubicon.objc import ObjCClass, objc_method
7 | from rubicon.objc.core_foundation import from_value, to_value
8 |
9 | __all__ = ('UIKit', 'ObjCClass', 'objc_method', 'NSObject',
10 | 'UIScreen', 'UIView', 'UIViewController','UIResponder',
11 | 'UIWindow', 'UIColor', 'UILabel')
12 |
13 | UIKit = cdll.LoadLibrary(util.find_library('UIKit'))
14 | UIKit.UIApplicationMain.restypes = (c_int, c_void_p, c_void_p,
15 | c_void_p)
16 | UIKit.UIApplicationMain.restype = c_int
17 |
18 | for item in __all__:
19 | if item not in globals():
20 | globals()[item] = ObjCClass(item)
21 |
22 |
23 | class ObjCAppDelegate(NSObject):
24 | @objc_method('@B')
25 | def application_didFinishLaunchingWithOptions_(self, launchOptions):
26 | self.__dict__['delegate'] = PyAppDelegate()
27 | return 0
28 |
29 |
30 | class PyAppDelegate(object):
31 | def __init__(self):
32 | self.window = UIWindow.alloc().initWithFrame_(UIScreen.mainScreen().bounds)
33 |
34 | view_controller = UIViewController.alloc().init()
35 | view_controller.view.setBackgroundColor_(UIColor.colorWithWhite_alpha_(0.5, 1))
36 |
37 | label = UILabel.alloc().initWithFrame_(view_controller.view.bounds)
38 |
39 | label.setText_("test, this is a label")
40 | label.setTextAlignment_(1)
41 | label.setTextColor_(UIColor.colorWithRed_green_blue_alpha_(30/255., 70/255., 137/255., 1))
42 | view_controller.view.addSubview_(label)
43 |
44 | self.window.rootViewController = view_controller
45 | self.window.makeKeyAndVisible()
46 |
47 |
48 | if __name__ == '__main__':
49 | sys.exit(UIKit.UIApplicationMain(0, None, None, from_value('ObjCAppDelegate')))
50 |
--------------------------------------------------------------------------------
/apptest/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "29x29",
5 | "idiom" : "iphone",
6 | "filename" : "icon-58.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "29x29",
11 | "idiom" : "iphone",
12 | "filename" : "icon-87.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "40x40",
17 | "idiom" : "iphone",
18 | "filename" : "icon-80.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "40x40",
23 | "idiom" : "iphone",
24 | "filename" : "icon-120.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "60x60",
29 | "idiom" : "iphone",
30 | "filename" : "icon-120.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "60x60",
35 | "idiom" : "iphone",
36 | "filename" : "icon-180.png",
37 | "scale" : "3x"
38 | },
39 | {
40 | "size" : "29x29",
41 | "idiom" : "ipad",
42 | "filename" : "icon-29.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "29x29",
47 | "idiom" : "ipad",
48 | "filename" : "icon-58.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "40x40",
53 | "idiom" : "ipad",
54 | "filename" : "icon-40.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "40x40",
59 | "idiom" : "ipad",
60 | "filename" : "icon-80.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "76x76",
65 | "idiom" : "ipad",
66 | "filename" : "icon-76.png",
67 | "scale" : "1x"
68 | },
69 | {
70 | "size" : "76x76",
71 | "idiom" : "ipad",
72 | "filename" : "icon-152.png",
73 | "scale" : "2x"
74 | },
75 | {
76 | "size" : "83.5x83.5",
77 | "idiom" : "ipad",
78 | "filename" : "icon-167.png",
79 | "scale" : "2x"
80 | }
81 | ],
82 | "info" : {
83 | "version" : 1,
84 | "author" : "xcode"
85 | }
86 | }
--------------------------------------------------------------------------------
/app_packages/rubicon/objc/types.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function, absolute_import, division
2 |
3 | from ctypes import *
4 |
5 | import platform, struct
6 |
7 | __LP64__ = (8*struct.calcsize("P") == 64)
8 | __i386__ = (platform.machine() == 'i386')
9 | __x86_64__ = (platform.machine() == 'x86_64')
10 |
11 | PyObjectEncoding = b'{PyObject=@}'
12 |
13 | def encoding_for_ctype(vartype):
14 | typecodes = {c_char:b'c', c_int:b'i', c_short:b's', c_long:b'l', c_longlong:b'q',
15 | c_ubyte:b'C', c_uint:b'I', c_ushort:b'S', c_ulong:b'L', c_ulonglong:b'Q',
16 | c_float:b'f', c_double:b'd', c_bool:b'B', c_char_p:b'*', c_void_p:b'@',
17 | py_object:PyObjectEncoding}
18 | return typecodes.get(vartype, b'?')
19 |
20 | try:
21 | text = unicode
22 | except NameError:
23 | text = str
24 |
25 |
26 | # Note CGBase.h located at
27 | # /System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Headers/CGBase.h
28 | # defines CGFloat as double if __LP64__, otherwise it's a float.
29 | if __LP64__:
30 | NSInteger = c_long
31 | NSUInteger = c_ulong
32 | CGFloat = c_double
33 | NSPointEncoding = b'{CGPoint=dd}'
34 | NSSizeEncoding = b'{CGSize=dd}'
35 | NSRectEncoding = b'{CGRect={CGPoint=dd}{CGSize=dd}}'
36 | NSRangeEncoding = b'{_NSRange=QQ}'
37 | else:
38 | NSInteger = c_int
39 | NSUInteger = c_uint
40 | CGFloat = c_float
41 | NSPointEncoding = b'{CGPoint=ff}'
42 | NSSizeEncoding = b'{CGSize=ff}'
43 | NSRectEncoding = b'{CGRect={CGPoint=ff}{CGSize=ff}}'
44 | NSRangeEncoding = b'{NSRange=II}'
45 |
46 | NSIntegerEncoding = encoding_for_ctype(NSInteger)
47 | NSUIntegerEncoding = encoding_for_ctype(NSUInteger)
48 | CGFloatEncoding = encoding_for_ctype(CGFloat)
49 |
50 | # Special case so that NSImage.initWithCGImage_size_() will work.
51 | CGImageEncoding = b'{CGImage=}'
52 |
53 | NSZoneEncoding = b'{_NSZone=}'
54 |
55 | # from /System/Library/Frameworks/Foundation.framework/Headers/NSGeometry.h
56 | class NSPoint(Structure):
57 | _fields_ = [
58 | ("x", CGFloat),
59 | ("y", CGFloat)
60 | ]
61 | CGPoint = NSPoint
62 |
63 | class NSSize(Structure):
64 | _fields_ = [
65 | ("width", CGFloat),
66 | ("height", CGFloat)
67 | ]
68 | CGSize = NSSize
69 |
70 | class NSRect(Structure):
71 | _fields_ = [
72 | ("origin", NSPoint),
73 | ("size", NSSize)
74 | ]
75 | CGRect = NSRect
76 |
77 | def NSMakeSize(w, h):
78 | return NSSize(w, h)
79 |
80 | CGSizeMake = NSMakeSize
81 |
82 | def NSMakeRect(x, y, w, h):
83 | return NSRect(NSPoint(x, y), NSSize(w, h))
84 |
85 | CGRectMake = NSMakeRect
86 |
87 |
88 | def NSMakePoint(x, y):
89 | return NSPoint(x, y)
90 |
91 | CGPointMake = NSMakePoint
92 |
93 | # NSDate.h
94 | NSTimeInterval = c_double
95 |
96 | CFIndex = c_long
97 | UniChar = c_ushort
98 | unichar = c_wchar # (actually defined as c_ushort in NSString.h, but need ctypes to convert properly)
99 | CGGlyph = c_ushort
100 |
101 | # CFRange struct defined in CFBase.h
102 | # This replaces the CFRangeMake(LOC, LEN) macro.
103 | class CFRange(Structure):
104 | _fields_ = [
105 | ("location", CFIndex),
106 | ("length", CFIndex)
107 | ]
108 |
109 | # NSRange.h (Note, not defined the same as CFRange)
110 | class NSRange(Structure):
111 | _fields_ = [
112 | ("location", NSUInteger),
113 | ("length", NSUInteger)
114 | ]
115 |
116 | NSZeroPoint = NSPoint(0,0)
117 |
118 | CFTypeID = c_ulong
119 | CFNumberType = c_uint32
120 |
--------------------------------------------------------------------------------
/apptest.xcodeproj/xcuserdata/fujun.xcuserdatad/xcschemes/apptest.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/apptest/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // A main module for starting Python projects under iOS.
4 | //
5 |
6 | #import
7 | #import
8 | #include
9 | #include
10 |
11 | int main(int argc, char *argv[]) {
12 | int ret = 0;
13 | unsigned int i;
14 | NSString *tmp_path;
15 | NSString *python_home;
16 | wchar_t *wpython_home;
17 | const char* main_script;
18 | wchar_t** python_argv;
19 |
20 | @autoreleasepool {
21 |
22 | NSString * resourcePath = [[NSBundle mainBundle] resourcePath];
23 |
24 | // Special environment to prefer .pyo; also, don't write bytecode
25 | // because the process will not have write permissions on the device.
26 | putenv("PYTHONOPTIMIZE=1");
27 | putenv("PYTHONDONTWRITEBYTECODE=1");
28 |
29 | python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil];
30 | NSLog(@"PythonHome is: %@", python_home);
31 | wpython_home = _Py_char2wchar([python_home UTF8String], NULL);
32 | Py_SetPythonHome(wpython_home);
33 |
34 | // iOS provides a specific directory for temp files.
35 | tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil];
36 | putenv((char *)[tmp_path UTF8String]);
37 |
38 | NSLog(@"Initializing Python runtime");
39 | Py_Initialize();
40 |
41 | // Set the name of the main script
42 | main_script = [[[NSBundle mainBundle] pathForResource:@"Library/Application Support/com.ifujun.apptest.apptest/app/apptest/__main__"
43 | ofType:@"py"]
44 | cStringUsingEncoding:NSUTF8StringEncoding];
45 |
46 | if (main_script == NULL) {
47 | NSLog(@"Unable to locate apptest main module file");
48 | exit(-1);
49 | }
50 |
51 | // Construct argv for the interpreter
52 | python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * argc);
53 |
54 | python_argv[0] = _Py_char2wchar(main_script, NULL);
55 | for (i = 1; i < argc; i++) {
56 | python_argv[i] = _Py_char2wchar(argv[i], NULL);
57 | }
58 |
59 | PySys_SetArgv(argc, python_argv);
60 |
61 | // If other modules are using threads, we need to initialize them.
62 | PyEval_InitThreads();
63 |
64 | // Start the main.py script
65 | NSLog(@"Running %s", main_script);
66 |
67 | @try {
68 | FILE* fd = fopen(main_script, "r");
69 | if (fd == NULL) {
70 | ret = 1;
71 | NSLog(@"Unable to open main.py, abort.");
72 | } else {
73 | ret = PyRun_SimpleFileEx(fd, main_script, 1);
74 | if (ret != 0) {
75 | NSLog(@"Application quit abnormally!");
76 | } else {
77 | // In a normal iOS application, the following line is what
78 | // actually runs the application. It requires that the
79 | // Objective-C runtime environment has a class named
80 | // "PythonAppDelegate". This project doesn't define
81 | // one, because Objective-C bridging isn't something
82 | // Python does out of the box. You'll need to use
83 | // a library like Rubicon-ObjC [1], Pyobjus [2] or
84 | // PyObjC [3] if you want to run an *actual* iOS app.
85 | // [1] http://pybee.org/rubicon
86 | // [2] http://pyobjus.readthedocs.org/
87 | // [3] https://pythonhosted.org/pyobjc/
88 |
89 | UIApplicationMain(argc, argv, nil, @"PythonAppDelegate");
90 | }
91 | }
92 | }
93 | @catch (NSException *exception) {
94 | NSLog(@"Python runtime error: %@", [exception reason]);
95 | }
96 | @finally {
97 | Py_Finalize();
98 | }
99 |
100 | PyMem_RawFree(wpython_home);
101 | if (python_argv) {
102 | for (i = 0; i < argc; i++) {
103 | PyMem_RawFree(python_argv[i]);
104 | }
105 | PyMem_RawFree(python_argv);
106 | }
107 | NSLog(@"Leaving");
108 | }
109 |
110 | exit(ret);
111 | return ret;
112 | }
113 |
--------------------------------------------------------------------------------
/app_packages/rubicon/objc/core_foundation.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function, absolute_import, division
2 |
3 | from ctypes import *
4 | from ctypes import util
5 | from decimal import Decimal
6 |
7 | from .objc import ObjCInstance, send_message, get_class, get_selector, objc
8 | from .types import *
9 |
10 | ######################################################################
11 |
12 | # CORE FOUNDATION
13 |
14 | cf = cdll.LoadLibrary(util.find_library('CoreFoundation'))
15 |
16 | kCFStringEncodingUTF8 = 0x08000100
17 |
18 | CFAllocatorRef = c_void_p
19 | CFStringEncoding = c_uint32
20 |
21 | cf.CFStringCreateWithCString.restype = c_void_p
22 | cf.CFStringCreateWithCString.argtypes = [CFAllocatorRef, c_char_p, CFStringEncoding]
23 |
24 | cf.CFRelease.restype = c_void_p
25 | cf.CFRelease.argtypes = [c_void_p]
26 |
27 | cf.CFStringGetLength.restype = CFIndex
28 | cf.CFStringGetLength.argtypes = [c_void_p]
29 |
30 | cf.CFStringGetMaximumSizeForEncoding.restype = CFIndex
31 | cf.CFStringGetMaximumSizeForEncoding.argtypes = [CFIndex, CFStringEncoding]
32 |
33 | cf.CFStringGetCString.restype = c_bool
34 | cf.CFStringGetCString.argtypes = [c_void_p, c_char_p, CFIndex, CFStringEncoding]
35 |
36 | cf.CFStringGetTypeID.restype = CFTypeID
37 | cf.CFStringGetTypeID.argtypes = []
38 |
39 | cf.CFAttributedStringCreate.restype = c_void_p
40 | cf.CFAttributedStringCreate.argtypes = [CFAllocatorRef, c_void_p, c_void_p]
41 |
42 | # Core Foundation type to Python type conversion functions
43 |
44 | def CFSTR(string):
45 | return ObjCInstance(c_void_p(cf.CFStringCreateWithCString(
46 | None, string.encode('utf-8'), kCFStringEncodingUTF8)))
47 |
48 | # Other possible names for this method:
49 | # ampersat, arobe, apenstaartje (little monkey tail), strudel,
50 | # klammeraffe (spider monkey), little_mouse, arroba, sobachka (doggie)
51 | # malpa (monkey), snabel (trunk), papaki (small duck), afna (monkey),
52 | # kukac (caterpillar).
53 | def at(string):
54 | """Autoreleased version of CFSTR"""
55 | return ObjCInstance(send_message(CFSTR(string), 'autorelease'))
56 |
57 | def to_str(cfstring):
58 | length = cf.CFStringGetLength(cfstring)
59 | size = cf.CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8)
60 | buffer = c_buffer(size + 1)
61 | result = cf.CFStringGetCString(cfstring, buffer, len(buffer), kCFStringEncodingUTF8)
62 | if result:
63 | return text(buffer.value, 'utf-8')
64 |
65 | cf.CFDataCreate.restype = c_void_p
66 | cf.CFDataCreate.argtypes = [c_void_p, c_void_p, CFIndex]
67 |
68 | cf.CFDataGetBytes.restype = None
69 | cf.CFDataGetBytes.argtypes = [c_void_p, CFRange, c_void_p]
70 |
71 | cf.CFDataGetLength.restype = CFIndex
72 | cf.CFDataGetLength.argtypes = [c_void_p]
73 |
74 | cf.CFDictionaryGetValue.restype = c_void_p
75 | cf.CFDictionaryGetValue.argtypes = [c_void_p, c_void_p]
76 |
77 | cf.CFDictionaryAddValue.restype = None
78 | cf.CFDictionaryAddValue.argtypes = [c_void_p, c_void_p, c_void_p]
79 |
80 | cf.CFDictionaryCreateMutable.restype = c_void_p
81 | cf.CFDictionaryCreateMutable.argtypes = [CFAllocatorRef, CFIndex, c_void_p, c_void_p]
82 |
83 | cf.CFNumberCreate.restype = c_void_p
84 | cf.CFNumberCreate.argtypes = [CFAllocatorRef, CFNumberType, c_void_p]
85 |
86 | cf.CFNumberGetType.restype = CFNumberType
87 | cf.CFNumberGetType.argtypes = [c_void_p]
88 |
89 | cf.CFNumberGetValue.restype = c_ubyte
90 | cf.CFNumberGetValue.argtypes = [c_void_p, CFNumberType, c_void_p]
91 |
92 | cf.CFNumberGetTypeID.restype = CFTypeID
93 | cf.CFNumberGetTypeID.argtypes = []
94 |
95 | cf.CFGetTypeID.restype = CFTypeID
96 | cf.CFGetTypeID.argtypes = [c_void_p]
97 |
98 | # CFNumber.h
99 | kCFNumberSInt8Type = 1
100 | kCFNumberSInt16Type = 2
101 | kCFNumberSInt32Type = 3
102 | kCFNumberSInt64Type = 4
103 | kCFNumberFloat32Type = 5
104 | kCFNumberFloat64Type = 6
105 | kCFNumberCharType = 7
106 | kCFNumberShortType = 8
107 | kCFNumberIntType = 9
108 | kCFNumberLongType = 10
109 | kCFNumberLongLongType = 11
110 | kCFNumberFloatType = 12
111 | kCFNumberDoubleType = 13
112 | kCFNumberCFIndexType = 14
113 | kCFNumberNSIntegerType = 15
114 | kCFNumberCGFloatType = 16
115 | kCFNumberMaxType = 16
116 |
117 |
118 | def to_number(cfnumber):
119 | """Convert CFNumber to python int or float."""
120 | numeric_type = cf.CFNumberGetType(cfnumber)
121 | cfnum_to_ctype = {
122 | kCFNumberSInt8Type: c_int8,
123 | kCFNumberSInt16Type: c_int16,
124 | kCFNumberSInt32Type: c_int32,
125 | kCFNumberSInt64Type: c_int64,
126 | kCFNumberFloat32Type: c_float,
127 | kCFNumberFloat64Type: c_double,
128 | kCFNumberCharType: c_byte,
129 | kCFNumberShortType: c_short,
130 | kCFNumberIntType: c_int,
131 | kCFNumberLongType: c_long,
132 | kCFNumberLongLongType: c_longlong,
133 | kCFNumberFloatType: c_float,
134 | kCFNumberDoubleType: c_double,
135 | kCFNumberCFIndexType: CFIndex,
136 | kCFNumberCGFloatType: CGFloat
137 | }
138 |
139 | # NSDecimalNumber reports as a double. So does an NSNumber of type double.
140 | # In the case of NSDecimalNumber, convert to a Python decimal.
141 | if numeric_type == kCFNumberDoubleType \
142 | and isinstance(cfnumber, ObjCInstance) \
143 | and cfnumber.__dict__['objc_class'].name == 'NSDecimalNumber':
144 | return Decimal(cfnumber.stringValue)
145 |
146 | # Otherwise, just do the conversion.
147 | try:
148 | t = cfnum_to_ctype[numeric_type]
149 | result = t()
150 | if cf.CFNumberGetValue(cfnumber, numeric_type, byref(result)):
151 | return result.value
152 | except KeyError:
153 | raise Exception('to_number: unhandled CFNumber type %d' % numeric_type)
154 |
155 |
156 | # We need to be able to create raw NSDecimalNumber objects; if we use an
157 | # normal ObjCClass() wrapper, the return values of constructors will be
158 | # auto-converted back into Python Decimals. However, we want to cache
159 | # class/selector/method lookups so that we don't have the overhead
160 | # every time we use a decimal.
161 | class NSDecimalNumber(object):
162 | objc_class = None
163 |
164 | @classmethod
165 | def from_decimal(cls, value):
166 | if cls.objc_class is None:
167 | cls.objc_class = get_class('NSDecimalNumber')
168 | cls.selector = get_selector('decimalNumberWithString:')
169 | method = c_void_p(objc.class_getClassMethod(cls.objc_class, cls.selector))
170 | impl = c_void_p(objc.method_getImplementation(method))
171 | cls.constructor = cast(impl, CFUNCTYPE(c_void_p, c_void_p))
172 |
173 | return ObjCInstance(cls.constructor(cls.objc_class, cls.selector, at(value.to_eng_string())))
174 |
175 |
176 | def from_value(value):
177 | """Convert a Python type into an equivalent CFType type.
178 | """
179 | if isinstance(value, text):
180 | return at(value)
181 | elif isinstance(value, bytes):
182 | return at(value.decode('utf-8'))
183 | elif isinstance(value, Decimal):
184 | return NSDecimalNumber.from_decimal(value)
185 | else:
186 | return value
187 |
188 |
189 | # Dictionary of cftypes matched to the method converting them to python values.
190 | known_cftypes = {
191 | cf.CFStringGetTypeID(): to_str,
192 | cf.CFNumberGetTypeID(): to_number
193 | }
194 |
195 |
196 | def to_value(cftype):
197 | """Convert a CFType into an equivalent python type.
198 | The convertible CFTypes are taken from the known_cftypes
199 | dictionary, which may be added to if another library implements
200 | its own conversion methods."""
201 | if not cftype:
202 | return None
203 | typeID = cf.CFGetTypeID(cftype)
204 | try:
205 | convert_function = known_cftypes[typeID]
206 | return convert_function(cftype)
207 | except KeyError:
208 | return cftype
209 |
210 | cf.CFSetGetCount.restype = CFIndex
211 | cf.CFSetGetCount.argtypes = [c_void_p]
212 |
213 | cf.CFSetGetValues.restype = None
214 | # PyPy 1.7 is fine with 2nd arg as POINTER(c_void_p),
215 | # but CPython ctypes 1.1.0 complains, so just use c_void_p.
216 | cf.CFSetGetValues.argtypes = [c_void_p, c_void_p]
217 |
218 | def to_set(cfset):
219 | """Convert CFSet to python set."""
220 | count = cf.CFSetGetCount(cfset)
221 | buffer = (c_void_p * count)()
222 | cf.CFSetGetValues(cfset, byref(buffer))
223 | return set(to_value(c_void_p(buffer[i])) for i in range(count))
224 |
225 | cf.CFArrayGetCount.restype = CFIndex
226 | cf.CFArrayGetCount.argtypes = [c_void_p]
227 |
228 | cf.CFArrayGetValueAtIndex.restype = c_void_p
229 | cf.CFArrayGetValueAtIndex.argtypes = [c_void_p, CFIndex]
230 |
231 | def to_list(cfarray):
232 | """Convert CFArray to python list."""
233 | count = cf.CFArrayGetCount(cfarray)
234 | return [
235 | to_value(c_void_p(cf.CFArrayGetValueAtIndex(cfarray, i)))
236 | for i in range(count)
237 | ]
238 |
239 |
240 | kCFRunLoopDefaultMode = c_void_p.in_dll(cf, 'kCFRunLoopDefaultMode')
241 |
242 | cf.CFRunLoopGetCurrent.restype = c_void_p
243 | cf.CFRunLoopGetCurrent.argtypes = []
244 |
245 | cf.CFRunLoopGetMain.restype = c_void_p
246 | cf.CFRunLoopGetMain.argtypes = []
247 |
--------------------------------------------------------------------------------
/apptest.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 6023B23D1C27D20F006F2562 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B23C1C27D20F006F2562 /* libsqlite3.tbd */; };
11 | 6023B23F1C27D218006F2562 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B23E1C27D218006F2562 /* libz.tbd */; };
12 | 608C6CC91B3FF01D00BD512E /* OpenSSL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 608C6CC81B3FF01D00BD512E /* OpenSSL.framework */; };
13 | 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; };
14 | 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; };
15 | 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; };
16 | 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; };
17 | 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; };
18 | 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; };
19 | 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; };
20 | 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; };
21 | /* End PBXBuildFile section */
22 |
23 | /* Begin PBXFileReference section */
24 | 6023B23C1C27D20F006F2562 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
25 | 6023B23E1C27D218006F2562 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
26 | 608C6CC81B3FF01D00BD512E /* OpenSSL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OpenSSL.framework; sourceTree = ""; };
27 | 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; };
28 | 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
29 | 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
30 | 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
31 | 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
32 | 608C6CCC1B3FF4DF00BD512E /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; };
33 | 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; };
34 | 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
35 | 60796EE219190F4100A9926B /* apptest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "apptest.app"; sourceTree = BUILT_PRODUCTS_DIR; };
36 | 60796EED19190F4100A9926B /* apptest-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "apptest-Info.plist"; sourceTree = ""; };
37 | 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
38 | 60796EF319190F4100A9926B /* apptest-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "apptest-Prefix.pch"; sourceTree = ""; };
39 | 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
40 | /* End PBXFileReference section */
41 |
42 | /* Begin PBXFrameworksBuildPhase section */
43 | 60796EDF19190F4100A9926B /* Frameworks */ = {
44 | isa = PBXFrameworksBuildPhase;
45 | buildActionMask = 2147483647;
46 | files = (
47 | 6023B23F1C27D218006F2562 /* libz.tbd in Frameworks */,
48 | 6023B23D1C27D20F006F2562 /* libsqlite3.tbd in Frameworks */,
49 | 608C6CC91B3FF01D00BD512E /* OpenSSL.framework in Frameworks */,
50 | 60796F2C1919C70800A9926B /* Python.framework in Frameworks */,
51 | 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */,
52 | 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */,
53 | 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */,
54 | 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */,
55 | );
56 | runOnlyForDeploymentPostprocessing = 0;
57 | };
58 | /* End PBXFrameworksBuildPhase section */
59 |
60 | /* Begin PBXGroup section */
61 | 60796ED919190F4100A9926B = {
62 | isa = PBXGroup;
63 | children = (
64 | 60796EEB19190F4100A9926B /* apptest */,
65 | 60796EE419190F4100A9926B /* Frameworks */,
66 | 60796EE319190F4100A9926B /* Products */,
67 | );
68 | sourceTree = "";
69 | };
70 | 60796EE319190F4100A9926B /* Products */ = {
71 | isa = PBXGroup;
72 | children = (
73 | 60796EE219190F4100A9926B /* apptest.app */,
74 | );
75 | name = Products;
76 | sourceTree = "";
77 | };
78 | 60796EE419190F4100A9926B /* Frameworks */ = {
79 | isa = PBXGroup;
80 | children = (
81 | 6023B23C1C27D20F006F2562 /* libsqlite3.tbd */,
82 | 6023B23E1C27D218006F2562 /* libz.tbd */,
83 | 608C6CC81B3FF01D00BD512E /* OpenSSL.framework */,
84 | 60796F2B1919C70800A9926B /* Python.framework */,
85 | 60796F38191CDBBA00A9926B /* CoreFoundation.framework */,
86 | 60796EE719190F4100A9926B /* CoreGraphics.framework */,
87 | 60796EE519190F4100A9926B /* Foundation.framework */,
88 | 60796EE919190F4100A9926B /* UIKit.framework */,
89 | );
90 | name = Frameworks;
91 | sourceTree = "";
92 | };
93 | 60796EEB19190F4100A9926B /* apptest */ = {
94 | isa = PBXGroup;
95 | children = (
96 | 608C6CCC1B3FF4DF00BD512E /* app */,
97 | 60F0BABF191FC868006EC268 /* app_packages */,
98 | 60796EF719190F4100A9926B /* Images.xcassets */,
99 | 60796EEC19190F4100A9926B /* Supporting Files */,
100 | );
101 | path = apptest;
102 | sourceTree = "";
103 | };
104 | 60796EEC19190F4100A9926B /* Supporting Files */ = {
105 | isa = PBXGroup;
106 | children = (
107 | 60796EED19190F4100A9926B /* apptest-Info.plist */,
108 | 60796EEE19190F4100A9926B /* InfoPlist.strings */,
109 | 60796EF119190F4100A9926B /* main.m */,
110 | 60796EF319190F4100A9926B /* apptest-Prefix.pch */,
111 | );
112 | name = "Supporting Files";
113 | sourceTree = "";
114 | };
115 | /* End PBXGroup section */
116 |
117 | /* Begin PBXNativeTarget section */
118 | 60796EE119190F4100A9926B /* apptest */ = {
119 | isa = PBXNativeTarget;
120 | buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "apptest" */;
121 | buildPhases = (
122 | 60796F2F1919C7E700A9926B /* Refresh Python source */,
123 | 60796EDE19190F4100A9926B /* Sources */,
124 | 60796EDF19190F4100A9926B /* Frameworks */,
125 | 60796EE019190F4100A9926B /* Resources */,
126 | );
127 | buildRules = (
128 | );
129 | dependencies = (
130 | );
131 | name = "apptest";
132 | productName = apptest;
133 | productReference = 60796EE219190F4100A9926B /* apptest.app */;
134 | productType = "com.apple.product-type.application";
135 | };
136 | /* End PBXNativeTarget section */
137 |
138 | /* Begin PBXProject section */
139 | 60796EDA19190F4100A9926B /* Project object */ = {
140 | isa = PBXProject;
141 | attributes = {
142 | LastUpgradeCheck = 0720;
143 | ORGANIZATIONNAME = "apptest";
144 | };
145 | buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "apptest" */;
146 | compatibilityVersion = "Xcode 3.2";
147 | developmentRegion = English;
148 | hasScannedForEncodings = 0;
149 | knownRegions = (
150 | en,
151 | );
152 | mainGroup = 60796ED919190F4100A9926B;
153 | productRefGroup = 60796EE319190F4100A9926B /* Products */;
154 | projectDirPath = "";
155 | projectRoot = "";
156 | targets = (
157 | 60796EE119190F4100A9926B /* */,
158 | );
159 | };
160 | /* End PBXProject section */
161 |
162 | /* Begin PBXResourcesBuildPhase section */
163 | 60796EE019190F4100A9926B /* Resources */ = {
164 | isa = PBXResourcesBuildPhase;
165 | buildActionMask = 2147483647;
166 | files = (
167 | 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */,
168 | 60796EF819190F4100A9926B /* Images.xcassets in Resources */,
169 | );
170 | runOnlyForDeploymentPostprocessing = 0;
171 | };
172 | /* End PBXResourcesBuildPhase section */
173 |
174 | /* Begin PBXShellScriptBuildPhase section */
175 | 60796F2F1919C7E700A9926B /* Refresh Python source */ = {
176 | isa = PBXShellScriptBuildPhase;
177 | buildActionMask = 2147483647;
178 | files = (
179 | );
180 | inputPaths = (
181 | );
182 | name = "Refresh Python source";
183 | outputPaths = (
184 | );
185 | runOnlyForDeploymentPostprocessing = 0;
186 | shellPath = /bin/sh;
187 | shellScript = "mkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nmkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application Support/com.ifujun.apptest.apptest\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/Python.framework/Resources/lib\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/Python.framework/Resources/include\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/app\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application Support/com.ifujun.apptest.apptest\"\nmkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/app_packages/\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\"\n";
188 | };
189 | /* End PBXShellScriptBuildPhase section */
190 |
191 | /* Begin PBXSourcesBuildPhase section */
192 | 60796EDE19190F4100A9926B /* Sources */ = {
193 | isa = PBXSourcesBuildPhase;
194 | buildActionMask = 2147483647;
195 | files = (
196 | 60796EF219190F4100A9926B /* main.m in Sources */,
197 | );
198 | runOnlyForDeploymentPostprocessing = 0;
199 | };
200 | /* End PBXSourcesBuildPhase section */
201 |
202 | /* Begin PBXVariantGroup section */
203 | 60796EEE19190F4100A9926B /* InfoPlist.strings */ = {
204 | isa = PBXVariantGroup;
205 | children = (
206 | 60796EEF19190F4100A9926B /* en */,
207 | );
208 | name = InfoPlist.strings;
209 | sourceTree = "";
210 | };
211 | /* End PBXVariantGroup section */
212 |
213 | /* Begin XCBuildConfiguration section */
214 | 60796F0C19190F4100A9926B /* Debug */ = {
215 | isa = XCBuildConfiguration;
216 | buildSettings = {
217 | ALWAYS_SEARCH_USER_PATHS = NO;
218 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
219 | CLANG_CXX_LIBRARY = "libc++";
220 | CLANG_ENABLE_MODULES = YES;
221 | CLANG_ENABLE_OBJC_ARC = YES;
222 | CLANG_WARN_BOOL_CONVERSION = YES;
223 | CLANG_WARN_CONSTANT_CONVERSION = YES;
224 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
225 | CLANG_WARN_EMPTY_BODY = YES;
226 | CLANG_WARN_ENUM_CONVERSION = YES;
227 | CLANG_WARN_INT_CONVERSION = YES;
228 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
229 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
230 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
231 | COPY_PHASE_STRIP = NO;
232 | ENABLE_TESTABILITY = YES;
233 | GCC_C_LANGUAGE_STANDARD = gnu99;
234 | GCC_DYNAMIC_NO_PIC = NO;
235 | GCC_OPTIMIZATION_LEVEL = 0;
236 | GCC_PREPROCESSOR_DEFINITIONS = (
237 | "DEBUG=1",
238 | "$(inherited)",
239 | );
240 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
241 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
242 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
243 | GCC_WARN_UNDECLARED_SELECTOR = YES;
244 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
245 | GCC_WARN_UNUSED_FUNCTION = YES;
246 | GCC_WARN_UNUSED_VARIABLE = YES;
247 | IPHONEOS_DEPLOYMENT_TARGET = 7.1;
248 | ONLY_ACTIVE_ARCH = YES;
249 | SDKROOT = iphoneos;
250 | TARGETED_DEVICE_FAMILY = "1,2";
251 | };
252 | name = Debug;
253 | };
254 | 60796F0D19190F4100A9926B /* Release */ = {
255 | isa = XCBuildConfiguration;
256 | buildSettings = {
257 | ALWAYS_SEARCH_USER_PATHS = NO;
258 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
259 | CLANG_CXX_LIBRARY = "libc++";
260 | CLANG_ENABLE_MODULES = YES;
261 | CLANG_ENABLE_OBJC_ARC = YES;
262 | CLANG_WARN_BOOL_CONVERSION = YES;
263 | CLANG_WARN_CONSTANT_CONVERSION = YES;
264 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
265 | CLANG_WARN_EMPTY_BODY = YES;
266 | CLANG_WARN_ENUM_CONVERSION = YES;
267 | CLANG_WARN_INT_CONVERSION = YES;
268 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
269 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
270 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
271 | COPY_PHASE_STRIP = YES;
272 | ENABLE_NS_ASSERTIONS = NO;
273 | GCC_C_LANGUAGE_STANDARD = gnu99;
274 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
275 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
276 | GCC_WARN_UNDECLARED_SELECTOR = YES;
277 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
278 | GCC_WARN_UNUSED_FUNCTION = YES;
279 | GCC_WARN_UNUSED_VARIABLE = YES;
280 | IPHONEOS_DEPLOYMENT_TARGET = 7.1;
281 | SDKROOT = iphoneos;
282 | TARGETED_DEVICE_FAMILY = "1,2";
283 | VALIDATE_PRODUCT = YES;
284 | };
285 | name = Release;
286 | };
287 | 60796F0F19190F4100A9926B /* Debug */ = {
288 | isa = XCBuildConfiguration;
289 | buildSettings = {
290 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
291 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
292 | FRAMEWORK_SEARCH_PATHS = (
293 | "$(inherited)",
294 | "$(PROJECT_DIR)",
295 | );
296 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
297 | GCC_PREFIX_HEADER = "apptest/apptest-Prefix.pch";
298 | HEADER_SEARCH_PATHS = (
299 | "$(inherited)",
300 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
301 | "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"",
302 | );
303 | INFOPLIST_FILE = "apptest/apptest-Info.plist";
304 | PRODUCT_BUNDLE_IDENTIFIER = "com.ifujun.apptest.${PRODUCT_NAME:rfc1034identifier}";
305 | PRODUCT_NAME = "$(TARGET_NAME)";
306 | USER_HEADER_SEARCH_PATHS = include/python2.7;
307 | WRAPPER_EXTENSION = app;
308 | };
309 | name = Debug;
310 | };
311 | 60796F1019190F4100A9926B /* Release */ = {
312 | isa = XCBuildConfiguration;
313 | buildSettings = {
314 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
315 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
316 | FRAMEWORK_SEARCH_PATHS = (
317 | "$(inherited)",
318 | "$(PROJECT_DIR)",
319 | );
320 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
321 | GCC_PREFIX_HEADER = "apptest/apptest-Prefix.pch";
322 | HEADER_SEARCH_PATHS = (
323 | "$(inherited)",
324 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
325 | "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"",
326 | );
327 | INFOPLIST_FILE = "apptest/apptest-Info.plist";
328 | PRODUCT_BUNDLE_IDENTIFIER = "com.ifujun.apptest.${PRODUCT_NAME:rfc1034identifier}";
329 | PRODUCT_NAME = "$(TARGET_NAME)";
330 | WRAPPER_EXTENSION = app;
331 | };
332 | name = Release;
333 | };
334 | /* End XCBuildConfiguration section */
335 |
336 | /* Begin XCConfigurationList section */
337 | 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "apptest" */ = {
338 | isa = XCConfigurationList;
339 | buildConfigurations = (
340 | 60796F0C19190F4100A9926B /* Debug */,
341 | 60796F0D19190F4100A9926B /* Release */,
342 | );
343 | defaultConfigurationIsVisible = 0;
344 | defaultConfigurationName = Release;
345 | };
346 | 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "apptest" */ = {
347 | isa = XCConfigurationList;
348 | buildConfigurations = (
349 | 60796F0F19190F4100A9926B /* Debug */,
350 | 60796F1019190F4100A9926B /* Release */,
351 | );
352 | defaultConfigurationIsVisible = 0;
353 | defaultConfigurationName = Release;
354 | };
355 | /* End XCConfigurationList section */
356 | };
357 | rootObject = 60796EDA19190F4100A9926B /* Project object */;
358 | }
359 |
--------------------------------------------------------------------------------
/app_packages/rubicon/objc/objc.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function, absolute_import, division, unicode_literals
2 |
3 | import platform
4 | import struct
5 |
6 | from ctypes import *
7 | from ctypes import util
8 |
9 | from .types import *
10 |
11 | __LP64__ = (8*struct.calcsize("P") == 64)
12 | __i386__ = (platform.machine() == 'i386')
13 | __x86_64__ = (platform.machine() == 'x86_64')
14 |
15 | if sizeof(c_void_p) == 4:
16 | c_ptrdiff_t = c_int32
17 | elif sizeof(c_void_p) == 8:
18 | c_ptrdiff_t = c_int64
19 |
20 | ######################################################################
21 |
22 | objc = cdll.LoadLibrary(util.find_library(b'objc'))
23 |
24 | ######################################################################
25 |
26 | # BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, const char *types)
27 | objc.class_addIvar.restype = c_bool
28 | objc.class_addIvar.argtypes = [c_void_p, c_char_p, c_size_t, c_uint8, c_char_p]
29 |
30 | # BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
31 | objc.class_addMethod.restype = c_bool
32 |
33 | # BOOL class_addProtocol(Class cls, Protocol *protocol)
34 | objc.class_addProtocol.restype = c_bool
35 | objc.class_addProtocol.argtypes = [c_void_p, c_void_p]
36 |
37 | # BOOL class_conformsToProtocol(Class cls, Protocol *protocol)
38 | objc.class_conformsToProtocol.restype = c_bool
39 | objc.class_conformsToProtocol.argtypes = [c_void_p, c_void_p]
40 |
41 | # Ivar * class_copyIvarList(Class cls, unsigned int *outCount)
42 | # Returns an array of pointers of type Ivar describing instance variables.
43 | # The array has *outCount pointers followed by a NULL terminator.
44 | # You must free() the returned array.
45 | objc.class_copyIvarList.restype = POINTER(c_void_p)
46 | objc.class_copyIvarList.argtypes = [c_void_p, POINTER(c_uint)]
47 |
48 | # Method * class_copyMethodList(Class cls, unsigned int *outCount)
49 | # Returns an array of pointers of type Method describing instance methods.
50 | # The array has *outCount pointers followed by a NULL terminator.
51 | # You must free() the returned array.
52 | objc.class_copyMethodList.restype = POINTER(c_void_p)
53 | objc.class_copyMethodList.argtypes = [c_void_p, POINTER(c_uint)]
54 |
55 | # objc_property_t * class_copyPropertyList(Class cls, unsigned int *outCount)
56 | # Returns an array of pointers of type objc_property_t describing properties.
57 | # The array has *outCount pointers followed by a NULL terminator.
58 | # You must free() the returned array.
59 | objc.class_copyPropertyList.restype = POINTER(c_void_p)
60 | objc.class_copyPropertyList.argtypes = [c_void_p, POINTER(c_uint)]
61 |
62 | # Protocol ** class_copyProtocolList(Class cls, unsigned int *outCount)
63 | # Returns an array of pointers of type Protocol* describing protocols.
64 | # The array has *outCount pointers followed by a NULL terminator.
65 | # You must free() the returned array.
66 | objc.class_copyProtocolList.restype = POINTER(c_void_p)
67 | objc.class_copyProtocolList.argtypes = [c_void_p, POINTER(c_uint)]
68 |
69 | # id class_createInstance(Class cls, size_t extraBytes)
70 | objc.class_createInstance.restype = c_void_p
71 | objc.class_createInstance.argtypes = [c_void_p, c_size_t]
72 |
73 | # Method class_getClassMethod(Class aClass, SEL aSelector)
74 | # Will also search superclass for implementations.
75 | objc.class_getClassMethod.restype = c_void_p
76 | objc.class_getClassMethod.argtypes = [c_void_p, c_void_p]
77 |
78 | # Ivar class_getClassVariable(Class cls, const char* name)
79 | objc.class_getClassVariable.restype = c_void_p
80 | objc.class_getClassVariable.argtypes = [c_void_p, c_char_p]
81 |
82 | # Method class_getInstanceMethod(Class aClass, SEL aSelector)
83 | # Will also search superclass for implementations.
84 | objc.class_getInstanceMethod.restype = c_void_p
85 | objc.class_getInstanceMethod.argtypes = [c_void_p, c_void_p]
86 |
87 | # size_t class_getInstanceSize(Class cls)
88 | objc.class_getInstanceSize.restype = c_size_t
89 | objc.class_getInstanceSize.argtypes = [c_void_p]
90 |
91 | # Ivar class_getInstanceVariable(Class cls, const char* name)
92 | objc.class_getInstanceVariable.restype = c_void_p
93 | objc.class_getInstanceVariable.argtypes = [c_void_p, c_char_p]
94 |
95 | # const char *class_getIvarLayout(Class cls)
96 | objc.class_getIvarLayout.restype = c_char_p
97 | objc.class_getIvarLayout.argtypes = [c_void_p]
98 |
99 | # IMP class_getMethodImplementation(Class cls, SEL name)
100 | objc.class_getMethodImplementation.restype = c_void_p
101 | objc.class_getMethodImplementation.argtypes = [c_void_p, c_void_p]
102 |
103 | # IMP class_getMethodImplementation_stret(Class cls, SEL name)
104 | #objc.class_getMethodImplementation_stret.restype = c_void_p
105 | #objc.class_getMethodImplementation_stret.argtypes = [c_void_p, c_void_p]
106 |
107 | # const char * class_getName(Class cls)
108 | objc.class_getName.restype = c_char_p
109 | objc.class_getName.argtypes = [c_void_p]
110 |
111 | # objc_property_t class_getProperty(Class cls, const char *name)
112 | objc.class_getProperty.restype = c_void_p
113 | objc.class_getProperty.argtypes = [c_void_p, c_char_p]
114 |
115 | # Class class_getSuperclass(Class cls)
116 | objc.class_getSuperclass.restype = c_void_p
117 | objc.class_getSuperclass.argtypes = [c_void_p]
118 |
119 | # int class_getVersion(Class theClass)
120 | objc.class_getVersion.restype = c_int
121 | objc.class_getVersion.argtypes = [c_void_p]
122 |
123 | # const char *class_getWeakIvarLayout(Class cls)
124 | objc.class_getWeakIvarLayout.restype = c_char_p
125 | objc.class_getWeakIvarLayout.argtypes = [c_void_p]
126 |
127 | # BOOL class_isMetaClass(Class cls)
128 | objc.class_isMetaClass.restype = c_bool
129 | objc.class_isMetaClass.argtypes = [c_void_p]
130 |
131 | # IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
132 | objc.class_replaceMethod.restype = c_void_p
133 | objc.class_replaceMethod.argtypes = [c_void_p, c_void_p, c_void_p, c_char_p]
134 |
135 | # BOOL class_respondsToSelector(Class cls, SEL sel)
136 | objc.class_respondsToSelector.restype = c_bool
137 | objc.class_respondsToSelector.argtypes = [c_void_p, c_void_p]
138 |
139 | # void class_setIvarLayout(Class cls, const char *layout)
140 | objc.class_setIvarLayout.restype = None
141 | objc.class_setIvarLayout.argtypes = [c_void_p, c_char_p]
142 |
143 | # Class class_setSuperclass(Class cls, Class newSuper)
144 | objc.class_setSuperclass.restype = c_void_p
145 | objc.class_setSuperclass.argtypes = [c_void_p, c_void_p]
146 |
147 | # void class_setVersion(Class theClass, int version)
148 | objc.class_setVersion.restype = None
149 | objc.class_setVersion.argtypes = [c_void_p, c_int]
150 |
151 | # void class_setWeakIvarLayout(Class cls, const char *layout)
152 | objc.class_setWeakIvarLayout.restype = None
153 | objc.class_setWeakIvarLayout.argtypes = [c_void_p, c_char_p]
154 |
155 | ######################################################################
156 |
157 | # const char * ivar_getName(Ivar ivar)
158 | objc.ivar_getName.restype = c_char_p
159 | objc.ivar_getName.argtypes = [c_void_p]
160 |
161 | # ptrdiff_t ivar_getOffset(Ivar ivar)
162 | objc.ivar_getOffset.restype = c_ptrdiff_t
163 | objc.ivar_getOffset.argtypes = [c_void_p]
164 |
165 | # const char * ivar_getTypeEncoding(Ivar ivar)
166 | objc.ivar_getTypeEncoding.restype = c_char_p
167 | objc.ivar_getTypeEncoding.argtypes = [c_void_p]
168 |
169 | ######################################################################
170 |
171 | # char * method_copyArgumentType(Method method, unsigned int index)
172 | # You must free() the returned string.
173 | objc.method_copyArgumentType.restype = c_char_p
174 | objc.method_copyArgumentType.argtypes = [c_void_p, c_uint]
175 |
176 | # char * method_copyReturnType(Method method)
177 | # You must free() the returned string.
178 | objc.method_copyReturnType.restype = c_char_p
179 | objc.method_copyReturnType.argtypes = [c_void_p]
180 |
181 | # void method_exchangeImplementations(Method m1, Method m2)
182 | objc.method_exchangeImplementations.restype = None
183 | objc.method_exchangeImplementations.argtypes = [c_void_p, c_void_p]
184 |
185 | # void method_getArgumentType(Method method, unsigned int index, char *dst, size_t dst_len)
186 | # Functionally similar to strncpy(dst, parameter_type, dst_len).
187 | objc.method_getArgumentType.restype = None
188 | objc.method_getArgumentType.argtypes = [c_void_p, c_uint, c_char_p, c_size_t]
189 |
190 | # IMP method_getImplementation(Method method)
191 | objc.method_getImplementation.restype = c_void_p
192 | objc.method_getImplementation.argtypes = [c_void_p]
193 |
194 | # SEL method_getName(Method method)
195 | objc.method_getName.restype = c_void_p
196 | objc.method_getName.argtypes = [c_void_p]
197 |
198 | # unsigned method_getNumberOfArguments(Method method)
199 | objc.method_getNumberOfArguments.restype = c_uint
200 | objc.method_getNumberOfArguments.argtypes = [c_void_p]
201 |
202 | # void method_getReturnType(Method method, char *dst, size_t dst_len)
203 | # Functionally similar to strncpy(dst, return_type, dst_len)
204 | objc.method_getReturnType.restype = None
205 | objc.method_getReturnType.argtypes = [c_void_p, c_char_p, c_size_t]
206 |
207 | # const char * method_getTypeEncoding(Method method)
208 | objc.method_getTypeEncoding.restype = c_char_p
209 | objc.method_getTypeEncoding.argtypes = [c_void_p]
210 |
211 | # IMP method_setImplementation(Method method, IMP imp)
212 | objc.method_setImplementation.restype = c_void_p
213 | objc.method_setImplementation.argtypes = [c_void_p, c_void_p]
214 |
215 | ######################################################################
216 |
217 | # Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
218 | objc.objc_allocateClassPair.restype = c_void_p
219 | objc.objc_allocateClassPair.argtypes = [c_void_p, c_char_p, c_size_t]
220 |
221 | # Protocol **objc_copyProtocolList(unsigned int *outCount)
222 | # Returns an array of *outcount pointers followed by NULL terminator.
223 | # You must free() the array.
224 | objc.objc_copyProtocolList.restype = POINTER(c_void_p)
225 | objc.objc_copyProtocolList.argtypes = [POINTER(c_int)]
226 |
227 | # id objc_getAssociatedObject(id object, void *key)
228 | objc.objc_getAssociatedObject.restype = c_void_p
229 | objc.objc_getAssociatedObject.argtypes = [c_void_p, c_void_p]
230 |
231 | # id objc_getClass(const char *name)
232 | objc.objc_getClass.restype = c_void_p
233 | objc.objc_getClass.argtypes = [c_char_p]
234 |
235 | # int objc_getClassList(Class *buffer, int bufferLen)
236 | # Pass None for buffer to obtain just the total number of classes.
237 | objc.objc_getClassList.restype = c_int
238 | objc.objc_getClassList.argtypes = [c_void_p, c_int]
239 |
240 | # id objc_getMetaClass(const char *name)
241 | objc.objc_getMetaClass.restype = c_void_p
242 | objc.objc_getMetaClass.argtypes = [c_char_p]
243 |
244 | # Protocol *objc_getProtocol(const char *name)
245 | objc.objc_getProtocol.restype = c_void_p
246 | objc.objc_getProtocol.argtypes = [c_char_p]
247 |
248 | # You should set return and argument types depending on context.
249 | # id objc_msgSend(id theReceiver, SEL theSelector, ...)
250 | # id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
251 |
252 | # The _stret and _fpret variants only exist on x86-based architectures.
253 | if __i386__ or __x86_64__:
254 | # void objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...)
255 | objc.objc_msgSendSuper_stret.restype = None
256 |
257 | # double objc_msgSend_fpret(id self, SEL op, ...)
258 | objc.objc_msgSend_fpret.restype = c_double
259 |
260 | # void objc_msgSend_stret(void * stretAddr, id theReceiver, SEL theSelector, ...)
261 | objc.objc_msgSend_stret.restype = None
262 |
263 | # void objc_registerClassPair(Class cls)
264 | objc.objc_registerClassPair.restype = None
265 | objc.objc_registerClassPair.argtypes = [c_void_p]
266 |
267 | # void objc_removeAssociatedObjects(id object)
268 | objc.objc_removeAssociatedObjects.restype = None
269 | objc.objc_removeAssociatedObjects.argtypes = [c_void_p]
270 |
271 | # void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
272 | objc.objc_setAssociatedObject.restype = None
273 | objc.objc_setAssociatedObject.argtypes = [c_void_p, c_void_p, c_void_p, c_int]
274 |
275 | ######################################################################
276 |
277 | # id object_copy(id obj, size_t size)
278 | objc.object_copy.restype = c_void_p
279 | objc.object_copy.argtypes = [c_void_p, c_size_t]
280 |
281 | # id object_dispose(id obj)
282 | objc.object_dispose.restype = c_void_p
283 | objc.object_dispose.argtypes = [c_void_p]
284 |
285 | # Class object_getClass(id object)
286 | objc.object_getClass.restype = c_void_p
287 | objc.object_getClass.argtypes = [c_void_p]
288 |
289 | # const char *object_getClassName(id obj)
290 | objc.object_getClassName.restype = c_char_p
291 | objc.object_getClassName.argtypes = [c_void_p]
292 |
293 | # Ivar object_getInstanceVariable(id obj, const char *name, void **outValue)
294 | objc.object_getInstanceVariable.restype = c_void_p
295 | objc.object_getInstanceVariable.argtypes=[c_void_p, c_char_p, c_void_p]
296 |
297 | # id object_getIvar(id object, Ivar ivar)
298 | objc.object_getIvar.restype = c_void_p
299 | objc.object_getIvar.argtypes = [c_void_p, c_void_p]
300 |
301 | # Class object_setClass(id object, Class cls)
302 | objc.object_setClass.restype = c_void_p
303 | objc.object_setClass.argtypes = [c_void_p, c_void_p]
304 |
305 | # Ivar object_setInstanceVariable(id obj, const char *name, void *value)
306 | # Set argtypes based on the data type of the instance variable.
307 | objc.object_setInstanceVariable.restype = c_void_p
308 |
309 | # void object_setIvar(id object, Ivar ivar, id value)
310 | objc.object_setIvar.restype = None
311 | objc.object_setIvar.argtypes = [c_void_p, c_void_p, c_void_p]
312 |
313 | ######################################################################
314 |
315 | # const char *property_getAttributes(objc_property_t property)
316 | objc.property_getAttributes.restype = c_char_p
317 | objc.property_getAttributes.argtypes = [c_void_p]
318 |
319 | # const char *property_getName(objc_property_t property)
320 | objc.property_getName.restype = c_char_p
321 | objc.property_getName.argtypes = [c_void_p]
322 |
323 | ######################################################################
324 |
325 | # BOOL protocol_conformsToProtocol(Protocol *proto, Protocol *other)
326 | objc.protocol_conformsToProtocol.restype = c_bool
327 | objc.protocol_conformsToProtocol.argtypes = [c_void_p, c_void_p]
328 |
329 | class OBJC_METHOD_DESCRIPTION(Structure):
330 | _fields_ = [ ("name", c_void_p), ("types", c_char_p) ]
331 |
332 | # struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount)
333 | # You must free() the returned array.
334 | objc.protocol_copyMethodDescriptionList.restype = POINTER(OBJC_METHOD_DESCRIPTION)
335 | objc.protocol_copyMethodDescriptionList.argtypes = [c_void_p, c_bool, c_bool, POINTER(c_uint)]
336 |
337 | # objc_property_t * protocol_copyPropertyList(Protocol *protocol, unsigned int *outCount)
338 | objc.protocol_copyPropertyList.restype = c_void_p
339 | objc.protocol_copyPropertyList.argtypes = [c_void_p, POINTER(c_uint)]
340 |
341 | # Protocol **protocol_copyProtocolList(Protocol *proto, unsigned int *outCount)
342 | objc.protocol_copyProtocolList = POINTER(c_void_p)
343 | objc.protocol_copyProtocolList.argtypes = [c_void_p, POINTER(c_uint)]
344 |
345 | # struct objc_method_description protocol_getMethodDescription(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod)
346 | objc.protocol_getMethodDescription.restype = OBJC_METHOD_DESCRIPTION
347 | objc.protocol_getMethodDescription.argtypes = [c_void_p, c_void_p, c_bool, c_bool]
348 |
349 | # const char *protocol_getName(Protocol *p)
350 | objc.protocol_getName.restype = c_char_p
351 | objc.protocol_getName.argtypes = [c_void_p]
352 |
353 | ######################################################################
354 |
355 | # const char* sel_getName(SEL aSelector)
356 | objc.sel_getName.restype = c_char_p
357 | objc.sel_getName.argtypes = [c_void_p]
358 |
359 | # SEL sel_getUid(const char *str)
360 | # Use sel_registerName instead.
361 |
362 | # BOOL sel_isEqual(SEL lhs, SEL rhs)
363 | objc.sel_isEqual.restype = c_bool
364 | objc.sel_isEqual.argtypes = [c_void_p, c_void_p]
365 |
366 | # SEL sel_registerName(const char *str)
367 | objc.sel_registerName.restype = c_void_p
368 | objc.sel_registerName.argtypes = [c_char_p]
369 |
370 | ######################################################################
371 |
372 | def ensure_bytes(x):
373 | if isinstance(x, bytes):
374 | return x
375 | return x.encode('ascii')
376 |
377 | ######################################################################
378 |
379 | def get_selector(name):
380 | "Return a reference to the selector with the given name."
381 | return c_void_p(objc.sel_registerName(ensure_bytes(name)))
382 |
383 | def get_class(name):
384 | "Return a reference to the class with the given name."
385 | return c_void_p(objc.objc_getClass(ensure_bytes(name)))
386 |
387 | def get_metaclass(name):
388 | "Return a reference to the metaclass for the given name."
389 | return c_void_p(objc.objc_getMetaClass(ensure_bytes(name)))
390 |
391 | def get_superclass_of_object(obj):
392 | "Return a reference to the superclass of the given object."
393 | cls = c_void_p(objc.object_getClass(obj))
394 | return c_void_p(objc.class_getSuperclass(cls))
395 |
396 |
397 | # http://www.sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html
398 | # http://www.x86-64.org/documentation/abi-0.99.pdf (pp.17-23)
399 | # executive summary: on x86-64, who knows?
400 | def should_use_stret(restype):
401 | """Try to figure out when a return type will be passed on stack."""
402 | if type(restype) != type(Structure):
403 | return False
404 | if not __LP64__ and sizeof(restype) <= 8:
405 | return False
406 | if __LP64__ and sizeof(restype) <= 16: # maybe? I don't know?
407 | return False
408 | return True
409 |
410 | # http://www.sealiesoftware.com/blog/archive/2008/11/16/objc_explain_objc_msgSend_fpret.html
411 | def should_use_fpret(restype):
412 | """Determine if objc_msgSend_fpret is required to return a floating point type."""
413 | if not (__i386__ or __x86_64__):
414 | # Unneeded on non-intel processors
415 | return False
416 | if __LP64__ and restype == c_longdouble:
417 | # Use only for long double on x86_64
418 | return True
419 | if not __LP64__ and restype in (c_float, c_double, c_longdouble):
420 | return True
421 | return False
422 |
423 | def send_message(receiver, selName, *args, **kwargs):
424 | """Send a mesage named selName to the receiver with the provided arguments.
425 |
426 | This is the equivalen of [receiver selname:args]
427 |
428 | By default, assumes that return type for the message is c_void_p,
429 | and that all arguments are wrapped inside c_void_p. Use the restype
430 | and argtypes keyword arguments to change these values. restype should
431 | be a ctypes type and argtypes should be a list of ctypes types for
432 | the arguments of the message only.
433 | """
434 | if isinstance(receiver, text):
435 | receiver = get_class(receiver)
436 | selector = get_selector(selName)
437 | restype = kwargs.get('restype', c_void_p)
438 | argtypes = kwargs.get('argtypes', [])
439 | # Choose the correct version of objc_msgSend based on return type.
440 | if should_use_fpret(restype):
441 | objc.objc_msgSend_fpret.restype = restype
442 | objc.objc_msgSend_fpret.argtypes = [c_void_p, c_void_p] + argtypes
443 | result = objc.objc_msgSend_fpret(receiver, selector, *args)
444 | elif should_use_stret(restype):
445 | objc.objc_msgSend_stret.argtypes = [POINTER(restype), c_void_p, c_void_p] + argtypes
446 | result = restype()
447 | objc.objc_msgSend_stret(byref(result), receiver, selector, *args)
448 | else:
449 | objc.objc_msgSend.restype = restype
450 | objc.objc_msgSend.argtypes = [c_void_p, c_void_p] + argtypes
451 | result = objc.objc_msgSend(receiver, selector, *args)
452 | if restype == c_void_p:
453 | result = c_void_p(result)
454 | return result
455 |
456 |
457 | class OBJC_SUPER(Structure):
458 | _fields_ = [ ('receiver', c_void_p), ('class', c_void_p) ]
459 |
460 | OBJC_SUPER_PTR = POINTER(OBJC_SUPER)
461 |
462 |
463 | #http://stackoverflow.com/questions/3095360/what-exactly-is-super-in-objective-c
464 | def send_super(receiver, selName, *args, **kwargs):
465 | """Send a message named selName to the super of the receiver.
466 |
467 | This is the equivalent of [super selname:args].
468 | """
469 | if hasattr(receiver, '_as_parameter_'):
470 | receiver = receiver._as_parameter_
471 | superclass = get_superclass_of_object(receiver)
472 | super_struct = OBJC_SUPER(receiver, superclass)
473 | selector = get_selector(selName)
474 | restype = kwargs.get('restype', c_void_p)
475 | argtypes = kwargs.get('argtypes', None)
476 | objc.objc_msgSendSuper.restype = restype
477 | if argtypes:
478 | objc.objc_msgSendSuper.argtypes = [OBJC_SUPER_PTR, c_void_p] + argtypes
479 | else:
480 | objc.objc_msgSendSuper.argtypes = None
481 | result = objc.objc_msgSendSuper(byref(super_struct), selector, *args)
482 | if restype == c_void_p:
483 | result = c_void_p(result)
484 | return result
485 |
486 | ######################################################################
487 |
488 | cfunctype_table = {}
489 |
490 | def parse_type_encoding(encoding):
491 | """Takes a type encoding string and outputs a list of the separated type codes.
492 | Currently does not handle unions or bitfields and strips out any field width
493 | specifiers or type specifiers from the encoding. For Python 3.2+, encoding is
494 | assumed to be a bytes object and not unicode.
495 |
496 | Examples:
497 | parse_type_encoding('^v16@0:8') --> ['^v', '@', ':']
498 | parse_type_encoding('{CGSize=dd}40@0:8{CGSize=dd}16Q32') --> ['{CGSize=dd}', '@', ':', '{CGSize=dd}', 'Q']
499 | """
500 | type_encodings = []
501 | brace_count = 0 # number of unclosed curly braces
502 | bracket_count = 0 # number of unclosed square brackets
503 | typecode = b''
504 | for c in encoding:
505 | # In Python 3, c comes out as an integer in the range 0-255. In Python 2, c is a single character string.
506 | # To fix the disparity, we convert c to a bytes object if necessary.
507 | if isinstance(c, int):
508 | c = bytes([c])
509 |
510 | if c == b'{':
511 | # Check if this marked the end of previous type code.
512 | if typecode and typecode[-1:] != b'^' and brace_count == 0 and bracket_count == 0:
513 | type_encodings.append(typecode)
514 | typecode = b''
515 | typecode += c
516 | brace_count += 1
517 | elif c == b'}':
518 | typecode += c
519 | brace_count -= 1
520 | assert(brace_count >= 0)
521 | elif c == b'[':
522 | # Check if this marked the end of previous type code.
523 | if typecode and typecode[-1:] != b'^' and brace_count == 0 and bracket_count == 0:
524 | type_encodings.append(typecode)
525 | typecode = b''
526 | typecode += c
527 | bracket_count += 1
528 | elif c == b']':
529 | typecode += c
530 | bracket_count -= 1
531 | assert(bracket_count >= 0)
532 | elif brace_count or bracket_count:
533 | # Anything encountered while inside braces or brackets gets stuck on.
534 | typecode += c
535 | elif c in b'0123456789':
536 | # Ignore field width specifiers for now.
537 | pass
538 | elif c in b'rnNoORV':
539 | # Also ignore type specifiers.
540 | pass
541 | elif c in b'^cislqCISLQfdBv*@#:b?':
542 | if typecode and typecode[-1:] == b'^':
543 | # Previous char was pointer specifier, so keep going.
544 | typecode += c
545 | else:
546 | # Add previous type code to the list.
547 | if typecode:
548 | type_encodings.append(typecode)
549 | # Start a new type code.
550 | typecode = c
551 |
552 | # Add the last type code to the list
553 | if typecode:
554 | type_encodings.append(typecode)
555 |
556 | return type_encodings
557 |
558 |
559 | # Limited to basic types and pointers to basic types.
560 | # Does not try to handle arrays, arbitrary structs, unions, or bitfields.
561 | # Assume that encoding is a bytes object and not unicode.
562 | def cfunctype_for_encoding(encoding):
563 | # Check if we've already created a CFUNCTYPE for this encoding.
564 | # If so, then return the cached CFUNCTYPE.
565 | if encoding in cfunctype_table:
566 | return cfunctype_table[encoding]
567 |
568 | # Otherwise, create a new CFUNCTYPE for the encoding.
569 | typecodes = {
570 | b'c': c_char,
571 | b'i': c_int,
572 | b's': c_short,
573 | b'l': c_long,
574 | b'q': c_longlong,
575 | b'C': c_ubyte,
576 | b'I': c_uint,
577 | b'S': c_ushort,
578 | b'L': c_ulong,
579 | b'Q': c_ulonglong,
580 | b'f': c_float,
581 | b'd': c_double,
582 | b'B': c_bool,
583 | b'v': None,
584 | b'*': c_char_p,
585 | b'@': c_void_p,
586 | b'#': c_void_p,
587 | b':': c_void_p,
588 | NSPointEncoding: NSPoint,
589 | NSSizeEncoding: NSSize,
590 | NSRectEncoding: NSRect,
591 | NSRangeEncoding: NSRange,
592 | PyObjectEncoding: py_object
593 | }
594 | argtypes = []
595 | for code in parse_type_encoding(encoding):
596 | if code in typecodes:
597 | argtypes.append(typecodes[code])
598 | elif code[0:1] == b'^' and code[1:] in typecodes:
599 | argtypes.append(POINTER(typecodes[code[1:]]))
600 | else:
601 | raise Exception('unknown type encoding: ' + code)
602 |
603 | cfunctype = CFUNCTYPE(*argtypes)
604 |
605 | # Cache the new CFUNCTYPE in the cfunctype_table.
606 | # We do this mainly because it prevents the CFUNCTYPE
607 | # from being garbage-collected while we need it.
608 | cfunctype_table[encoding] = cfunctype
609 | return cfunctype
610 |
611 | ######################################################################
612 |
613 | def add_method(cls, selName, method, types):
614 | """Add a new instance method named selName to cls.
615 |
616 | method should be a Python method that does all necessary type conversions.
617 |
618 | types is a string encoding the argument types of the method.
619 | The first type code of types is the return type (e.g. 'v' if void)
620 | The second type code must be '@' for id self.
621 | The third type code must be ':' for SEL cmd.
622 | Additional type codes are for types of other arguments if any.
623 | """
624 | type_encodings = parse_type_encoding(types)
625 | assert(type_encodings[1] == b'@') # ensure id self typecode
626 | assert(type_encodings[2] == b':') # ensure SEL cmd typecode
627 | selector = get_selector(selName)
628 | cfunctype = cfunctype_for_encoding(types)
629 | imp = cfunctype(method)
630 | objc.class_addMethod.argtypes = [c_void_p, c_void_p, cfunctype, c_char_p]
631 | objc.class_addMethod(cls, selector, imp, types)
632 | return imp
633 |
634 |
635 | def add_ivar(cls, name, vartype):
636 | "Add a new instance variable of type vartype to cls."
637 | return objc.class_addIvar(cls, ensure_bytes(name), sizeof(vartype), alignment(vartype), encoding_for_ctype(vartype))
638 |
639 |
640 | def set_instance_variable(obj, varname, value, vartype):
641 | "Do the equivalent of `obj.varname = value`, where value is of type vartype."
642 | objc.object_setInstanceVariable.argtypes = [c_void_p, c_char_p, vartype]
643 | objc.object_setInstanceVariable(obj, ensure_bytes(varname), value)
644 |
645 |
646 | def get_instance_variable(obj, varname, vartype):
647 | "Return the value of `obj.varname`, where the value is of type vartype."
648 | variable = vartype()
649 | objc.object_getInstanceVariable(obj, ensure_bytes(varname), byref(variable))
650 | return variable.value
651 |
652 | ######################################################################
653 |
654 | class ObjCMethod(object):
655 | """This represents an unbound Objective-C method (really an IMP)."""
656 |
657 | # Note, need to map 'c' to c_byte rather than c_char, because otherwise
658 | # ctypes converts the value into a one-character string which is generally
659 | # not what we want at all, especially when the 'c' represents a bool var.
660 | typecodes = {
661 | b'c': c_byte,
662 | b'i': c_int,
663 | b's': c_short,
664 | b'l': c_long,
665 | b'q': c_longlong,
666 | b'C': c_ubyte,
667 | b'I': c_uint,
668 | b'S': c_ushort,
669 | b'L': c_ulong,
670 | b'Q': c_ulonglong,
671 | b'f': c_float,
672 | b'd': c_double,
673 | b'B': c_bool,
674 | b'v': None,
675 | b'Vv': None,
676 | b'*': c_char_p,
677 | b'@': c_void_p,
678 | b'#': c_void_p,
679 | b':': c_void_p,
680 | b'^v': c_void_p,
681 | b'?': c_void_p,
682 | NSPointEncoding: NSPoint,
683 | NSSizeEncoding: NSSize,
684 | NSRectEncoding: NSRect,
685 | NSRangeEncoding: NSRange,
686 | PyObjectEncoding: py_object
687 | }
688 |
689 | cfunctype_table = {}
690 |
691 | def __init__(self, method):
692 | """Initialize with an Objective-C Method pointer. We then determine
693 | the return type and argument type information of the method."""
694 | self.selector = c_void_p(objc.method_getName(method))
695 | self.name = objc.sel_getName(self.selector)
696 | self.pyname = self.name.replace(b':', b'_')
697 | self.encoding = objc.method_getTypeEncoding(method)
698 | self.return_type = objc.method_copyReturnType(method)
699 | self.nargs = objc.method_getNumberOfArguments(method)
700 | self.imp = c_void_p(objc.method_getImplementation(method))
701 | self.argument_types = []
702 |
703 | for i in range(self.nargs):
704 | buffer = c_buffer(512)
705 | objc.method_getArgumentType(method, i, buffer, len(buffer))
706 | self.argument_types.append(buffer.value)
707 | # Get types for all the arguments.
708 | try:
709 | self.argtypes = [self.ctype_for_encoding(t) for t in self.argument_types]
710 | except:
711 | print('No argtypes encoding for %s (%s)' % (self.name, self.argument_types))
712 | self.argtypes = None
713 | # Get types for the return type.
714 | try:
715 | if self.return_type == b'@':
716 | self.restype = ObjCInstance
717 | elif self.return_type == b'#':
718 | self.restype = ObjCClass
719 | else:
720 | self.restype = self.ctype_for_encoding(self.return_type)
721 | except:
722 | print('No restype encoding for %s (%s)' % (self.name, self.return_type))
723 | self.restype = None
724 | self.func = None
725 |
726 | def ctype_for_encoding(self, encoding):
727 | """Return ctypes type for an encoded Objective-C type."""
728 | if encoding in self.typecodes:
729 | return self.typecodes[encoding]
730 | elif encoding[0:1] == b'^' and encoding[1:] in self.typecodes:
731 | return POINTER(self.typecodes[encoding[1:]])
732 | elif encoding[0:1] == b'^' and encoding[1:] in [CGImageEncoding, NSZoneEncoding]:
733 | # special cases
734 | return c_void_p
735 | elif encoding[0:1] == b'r' and encoding[1:] in self.typecodes:
736 | # const decorator, don't care
737 | return self.typecodes[encoding[1:]]
738 | elif encoding[0:2] == b'r^' and encoding[2:] in self.typecodes:
739 | # const pointer, also don't care
740 | return POINTER(self.typecodes[encoding[2:]])
741 | else:
742 | raise Exception('unknown encoding for %s: %s' % (self.name, encoding))
743 |
744 | def get_prototype(self):
745 | """Returns a ctypes CFUNCTYPE for the method."""
746 | if self.restype == ObjCInstance or self.restype == ObjCClass:
747 | # Some hacky stuff to get around ctypes issues on 64-bit. Can't let
748 | # ctypes convert the return value itself, because it truncates the pointer
749 | # along the way. So instead, we must do set the return type to c_void_p to
750 | # ensure we get 64-bit addresses and then convert the return value manually.
751 | self.prototype = CFUNCTYPE(c_void_p, *self.argtypes)
752 | else:
753 | self.prototype = CFUNCTYPE(self.restype, *self.argtypes)
754 | return self.prototype
755 |
756 | def __repr__(self):
757 | return "" % (self.name, self.encoding)
758 |
759 | def get_callable(self):
760 | """Returns a python-callable version of the method's IMP."""
761 | if not self.func:
762 | prototype = self.get_prototype()
763 | self.func = cast(self.imp, prototype)
764 | if self.restype == ObjCInstance or self.restype == ObjCClass:
765 | self.func.restype = c_void_p
766 | else:
767 | self.func.restype = self.restype
768 | self.func.argtypes = self.argtypes
769 | return self.func
770 |
771 | def __call__(self, objc_id, *args):
772 | """Call the method with the given id and arguments. You do not need
773 | to pass in the selector as an argument since it will be automatically
774 | provided."""
775 | f = self.get_callable()
776 | try:
777 | # Automatically convert Python strings into ObjC strings
778 | from .core_foundation import from_value, to_value
779 | result = f(objc_id, self.selector, *(from_value(arg) for arg in args))
780 | # Convert result to python type if it is a instance or class pointer.
781 | if self.restype == ObjCInstance:
782 | result = to_value(ObjCInstance(result))
783 | elif self.restype == ObjCClass:
784 | result = ObjCClass(result)
785 | return result
786 | except ArgumentError as error:
787 | # Add more useful info to argument error exceptions, then reraise.
788 | error.args += ('selector = ' + self.name.decode('utf-8'),
789 | 'argtypes =' + text(self.argtypes),
790 | 'encoding = ' + self.encoding.decode('utf-8'))
791 | raise
792 |
793 |
794 | ######################################################################
795 |
796 | class ObjCBoundMethod(object):
797 | """This represents an Objective-C method (an IMP) which has been bound
798 | to some id which will be passed as the first parameter to the method."""
799 |
800 | def __init__(self, method, objc_id):
801 | """Initialize with a method and ObjCInstance or ObjCClass object."""
802 | self.method = method
803 | self.objc_id = objc_id
804 |
805 | def __repr__(self):
806 | return '' % (self.method.name, self.objc_id)
807 |
808 | def __call__(self, *args):
809 | """Call the method with the given arguments."""
810 | return self.method(self.objc_id, *args)
811 |
812 | ######################################################################
813 |
814 |
815 | def cache_instance_method(self, name):
816 | """Returns a python representation of the named instance method,
817 | either by looking it up in the cached list of methods or by searching
818 | for and creating a new method object."""
819 | try:
820 | return self.__dict__['instance_methods'][name]
821 | except KeyError:
822 | selector = get_selector(name.replace('_', ':'))
823 | method = c_void_p(objc.class_getInstanceMethod(self.__dict__['ptr'], selector))
824 | if method.value:
825 | objc_method = ObjCMethod(method)
826 | self.__dict__['instance_methods'][name] = objc_method
827 | return objc_method
828 | return None
829 |
830 |
831 | def cache_class_method(self, name):
832 | """Returns a python representation of the named class method,
833 | either by looking it up in the cached list of methods or by searching
834 | for and creating a new method object."""
835 | try:
836 | return self.__dict__['class_methods'][name]
837 | except KeyError:
838 | selector = get_selector(name.replace('_', ':'))
839 | method = c_void_p(objc.class_getClassMethod(self.__dict__['ptr'], selector))
840 |
841 | if method.value:
842 | objc_method = ObjCMethod(method)
843 | self.__dict__['class_methods'][name] = objc_method
844 | return objc_method
845 | return None
846 |
847 |
848 | def cache_instance_property_methods(self, name):
849 | """Return the accessor and mutator for the named property.
850 | """
851 | if name.endswith('_'):
852 | # If the requested name ends with _, that's a marker that we're
853 | # dealing with a method call, not a property, so we can shortcut
854 | # the process.
855 | methods = None
856 | else:
857 | # Check 1: Does the class respond to the property?
858 | responds = objc.class_getProperty(self.__dict__['ptr'], name.encode('utf-8'))
859 |
860 | # Check 2: Does the class have an instance method to retrieve the given name
861 | accessor_selector = get_selector(name)
862 | accessor = objc.class_getInstanceMethod(self.__dict__['ptr'], accessor_selector)
863 |
864 | # Check 3: Is there a setName: method to set the property with the given name
865 | mutator_selector = get_selector('set' + name[0].title() + name[1:] + ':')
866 | mutator = objc.class_getInstanceMethod(self.__dict__['ptr'], mutator_selector)
867 |
868 | # If the class responds as a property, or it has both an accessor *and*
869 | # and mutator, then treat it as a property in Python.
870 | if responds or (accessor and mutator):
871 | if accessor:
872 | objc_accessor = ObjCMethod(c_void_p(accessor))
873 | else:
874 | objc_accessor = None
875 |
876 | if mutator:
877 | objc_mutator = ObjCMethod(c_void_p(mutator))
878 | else:
879 | objc_mutator = None
880 |
881 | methods = (objc_accessor, objc_mutator)
882 | else:
883 | methods = None
884 | return methods
885 |
886 |
887 | def cache_instance_property_accessor(self, name):
888 | """Returns a python representation of an accessor for the named
889 | property. Existence of a property is done by looking for the write
890 | selector (set:).
891 | """
892 | try:
893 | methods = self.__dict__['instance_properties'][name]
894 | except KeyError:
895 | methods = cache_instance_property_methods(self, name)
896 | self.__dict__['instance_properties'][name] = methods
897 | if methods:
898 | return methods[0]
899 | return None
900 |
901 |
902 | def cache_instance_property_mutator(self, name):
903 | """Returns a python representation of an accessor for the named
904 | property. Existence of a property is done by looking for the write
905 | selector (set:).
906 | """
907 | try:
908 | methods = self.__dict__['instance_properties'][name]
909 | except KeyError:
910 | methods = cache_instance_property_methods(self, name)
911 | self.__dict__['instance_properties'][name] = methods
912 | if methods:
913 | return methods[1]
914 | return None
915 |
916 |
917 | def cache_class_property_methods(self, name):
918 | """Return the accessor and mutator for the named property. Existence
919 | of a property is done by looking for the pair of selectors "name" and
920 | "set:". If both exist, we assume this is a static property.
921 | """
922 | if name.endswith('_'):
923 | # If the requested name ends with _, that's a marker that we're
924 | # dealing with a method call, not a property, so we can shortcut
925 | # the process.
926 | methods = None
927 | else:
928 | accessor_selector = get_selector(name)
929 | accessor = c_void_p(objc.class_getClassMethod(self.__dict__['ptr'], accessor_selector))
930 | if accessor.value:
931 | objc_accessor = ObjCMethod(accessor)
932 | else:
933 | objc_accessor = None
934 |
935 | mutator_selector = get_selector('set' + name[0].title() + name[1:] + ':')
936 | mutator = c_void_p(objc.class_getClassMethod(self.__dict__['ptr'], mutator_selector))
937 | if mutator.value:
938 | objc_mutator = ObjCMethod(mutator)
939 | else:
940 | objc_mutator = None
941 |
942 | if objc_accessor and objc_mutator:
943 | methods = (objc_accessor, objc_mutator)
944 | else:
945 | methods = None
946 | return methods
947 |
948 | def cache_class_property_accessor(self, name):
949 | """Returns a python representation of an accessor for the named
950 | property. Existence of a property is done by looking for the write
951 | selector (set:).
952 | """
953 | try:
954 | methods = self.__dict__['class_properties'][name]
955 | except KeyError:
956 | methods = cache_class_property_methods(self, name)
957 | self.__dict__['class_properties'][name] = methods
958 | if methods:
959 | return methods[0]
960 | return None
961 |
962 |
963 | def cache_class_property_mutator(self, name):
964 | """Returns a python representation of an accessor for the named
965 | property. Existence of a property is done by looking for the write
966 | selector (set:).
967 | """
968 | try:
969 | methods = self.__dict__['class_properties'][name]
970 | except KeyError:
971 | methods = cache_class_property_methods(self, name)
972 | self.__dict__['class_properties'][name] = methods
973 | if methods:
974 | return methods[1]
975 | return None
976 |
977 | ######################################################################
978 |
979 | def convert_method_arguments(encoding, args):
980 | """Used to convert Objective-C method arguments to Python values
981 | before passing them on to the Python-defined method.
982 | """
983 | from .core_foundation import to_value
984 | new_args = []
985 | arg_encodings = parse_type_encoding(encoding)[3:]
986 | for e, a in zip(arg_encodings, args):
987 | if e == b'@':
988 | new_args.append(to_value(ObjCInstance(a)))
989 | elif e == b'#':
990 | new_args.append(ObjCClass(a))
991 | else:
992 | new_args.append(a)
993 | return new_args
994 |
995 |
996 | def objc_method(encoding):
997 | """Function decorator for instance methods."""
998 | # Add encodings for hidden self and cmd arguments.
999 | encoding = ensure_bytes(encoding)
1000 | typecodes = parse_type_encoding(encoding)
1001 | typecodes.insert(1, b'@:')
1002 | encoding = b''.join(typecodes)
1003 | def decorator(f):
1004 | def _objc_method(objc_self, objc_cmd, *args):
1005 | from .core_foundation import at
1006 | py_self = ObjCInstance(objc_self)
1007 | args = convert_method_arguments(encoding, args)
1008 | result = f(py_self, *args)
1009 | if isinstance(result, ObjCClass):
1010 | result = result.ptr.value
1011 | elif isinstance(result, ObjCInstance):
1012 | result = result.ptr.value
1013 | elif isinstance(result, text):
1014 | result = at(result).ptr.value
1015 | return result
1016 |
1017 | def register(cls):
1018 | return add_method(cls.__dict__['ptr'], f.__name__.replace('_', ':'), _objc_method, encoding)
1019 |
1020 | _objc_method.register = register
1021 |
1022 | return _objc_method
1023 | return decorator
1024 |
1025 |
1026 | def objc_classmethod(encoding):
1027 | """Function decorator for class methods."""
1028 | # Add encodings for hidden self and cmd arguments.
1029 | encoding = ensure_bytes(encoding)
1030 | typecodes = parse_type_encoding(encoding)
1031 | typecodes.insert(1, b'@:')
1032 | encoding = b''.join(typecodes)
1033 | def decorator(f):
1034 | def _objc_classmethod(objc_cls, objc_cmd, *args):
1035 | from .core_foundation import at
1036 | py_cls = ObjCClass(objc_cls)
1037 | args = convert_method_arguments(encoding, args)
1038 | result = f(py_cls, *args)
1039 | if isinstance(result, ObjCClass):
1040 | result = result.ptr.value
1041 | elif isinstance(result, ObjCInstance):
1042 | result = result.ptr.value
1043 | elif isinstance(result, text):
1044 | result = at(result).ptr.value
1045 | return result
1046 |
1047 | def register(cls):
1048 | return add_method(cls.__dict__['metaclass'], f.__name__.replace('_', ':'), _objc_classmethod, encoding)
1049 |
1050 | _objc_classmethod.register = register
1051 |
1052 | return _objc_classmethod
1053 | return decorator
1054 |
1055 |
1056 | class objc_ivar(object):
1057 | """Add instance variable named varname to the subclass.
1058 | varname should be a string.
1059 | vartype is a ctypes type.
1060 | The class must be registered AFTER adding instance variables.
1061 | """
1062 | def __init__(self, vartype):
1063 | self.vartype = vartype
1064 |
1065 | def pre_register(self, ptr, name):
1066 | return add_ivar(ptr, name, self.vartype)
1067 |
1068 |
1069 | def objc_rawmethod(encoding):
1070 | """Decorator for instance methods without any fancy shenanigans.
1071 | The function must have the signature f(self, cmd, *args)
1072 | where both self and cmd are just pointers to objc objects.
1073 | """
1074 | # Add encodings for hidden self and cmd arguments.
1075 | encoding = ensure_bytes(encoding)
1076 | typecodes = parse_type_encoding(encoding)
1077 | typecodes.insert(1, b'@:')
1078 | encoding = b''.join(typecodes)
1079 | def decorator(f):
1080 | name = f.__name__.replace('_', ':')
1081 |
1082 | def register(cls):
1083 | return add_method(cls, name, f, encoding)
1084 | f.register = register
1085 | return f
1086 | return decorator
1087 |
1088 | ######################################################################
1089 |
1090 |
1091 | class ObjCClass(type):
1092 | """Python wrapper for an Objective-C class."""
1093 |
1094 | # We only create one Python object for each Objective-C class.
1095 | # Any future calls with the same class will return the previously
1096 | # created Python object. Note that these aren't weak references.
1097 | # After you create an ObjCClass, it will exist until the end of the
1098 | # program.
1099 | _registered_classes = {}
1100 |
1101 | def __new__(cls, *args):
1102 | """Create a new ObjCClass instance or return a previously created
1103 | instance for the given Objective-C class. The argument may be either
1104 | the name of the class to retrieve, a pointer to the class, or the
1105 | usual (name, bases, attrs) triple that is provided when called as
1106 | a subclass."""
1107 |
1108 | if len(args) == 1:
1109 | # A single argument provided. If it's a string, treat it as
1110 | # a class name. Anything else treat as a class pointer.
1111 |
1112 | # Determine name and ptr values from passed in argument.
1113 | class_name_or_ptr = args[0]
1114 | attrs = {}
1115 |
1116 | if isinstance(class_name_or_ptr, (bytes, text)):
1117 | name = ensure_bytes(class_name_or_ptr)
1118 | ptr = get_class(name)
1119 | if ptr.value is None:
1120 | raise NameError("ObjC Class '%s' couldn't be found." % class_name_or_ptr)
1121 | else:
1122 | ptr = class_name_or_ptr
1123 | # Make sure that ptr value is wrapped in c_void_p object
1124 | # for safety when passing as ctypes argument.
1125 | if not isinstance(ptr, c_void_p):
1126 | ptr = c_void_p(ptr)
1127 | name = objc.class_getName(ptr)
1128 | # "nil" is an ObjC answer confirming the ptr didn't work.
1129 | if name == b'nil':
1130 | raise RuntimeError("Couldn't create ObjC class for pointer '%s'." % class_name_or_ptr)
1131 |
1132 | else:
1133 | name, bases, attrs = args
1134 | name = ensure_bytes(name)
1135 | if not isinstance(bases[0], ObjCClass):
1136 | raise RuntimeError("Base class isn't an ObjCClass.")
1137 |
1138 | ptr = get_class(name)
1139 | if ptr.value is None:
1140 | # Create the ObjC class description
1141 | ptr = c_void_p(objc.objc_allocateClassPair(bases[0].__dict__['ptr'], name, 0))
1142 |
1143 | # Pre-Register all the instance variables
1144 | for attr, obj in attrs.items():
1145 | try:
1146 | obj.pre_register(ptr, attr)
1147 | except AttributeError:
1148 | # The class attribute doesn't have a pre_register method.
1149 | pass
1150 |
1151 | # Register the ObjC class
1152 | objc.objc_registerClassPair(ptr)
1153 | else:
1154 | raise RuntimeError("ObjC runtime already contains a registered class named '%s'." % name.decode('utf-8'))
1155 |
1156 | # Check if we've already created a Python object for this class
1157 | # and if so, return it rather than making a new one.
1158 | try:
1159 | objc_class = cls._registered_classes[name]
1160 | except KeyError:
1161 |
1162 | # We can get the metaclass only after the class is registered.
1163 | metaclass = get_metaclass(name)
1164 |
1165 | # Py2/3 compatibility; the class name must be "str".
1166 | # If the unicode class exists, we're in Python 2.
1167 | try:
1168 | unicode
1169 | objc_class_name = name
1170 | except NameError:
1171 | objc_class_name = name.decode('utf-8')
1172 |
1173 | # Otherwise create a new Python object and then initialize it.
1174 | objc_class = super(ObjCClass, cls).__new__(cls, objc_class_name, (ObjCInstance,), {
1175 | 'ptr': ptr,
1176 | 'metaclass': metaclass,
1177 | 'name': objc_class_name,
1178 | 'instance_methods': {}, # mapping of name -> instance method
1179 | 'class_methods': {}, # mapping of name -> class method
1180 | 'instance_properties': {}, # mapping of name -> (accessor method, mutator method)
1181 | 'class_properties': {}, # mapping of name -> (accessor method, mutator method)
1182 | 'imp_table': {}, # Mapping of name -> Native method references
1183 | '_as_parameter_': ptr, # for ctypes argument passing
1184 | })
1185 |
1186 | # Store the new class in dictionary of registered classes.
1187 | cls._registered_classes[name] = objc_class
1188 |
1189 | # Register all the methods, class methods, etc
1190 | for attr, obj in attrs.items():
1191 | try:
1192 | objc_class.__dict__['imp_table'][attr] = obj.register(objc_class)
1193 | except AttributeError:
1194 | # The class attribute doesn't have a register method.
1195 | pass
1196 |
1197 | return objc_class
1198 |
1199 | def __repr__(self):
1200 | return "" % (self.__dict__['name'], text(self.__dict__['ptr'].value))
1201 |
1202 | def __getattr__(self, name):
1203 | """Returns a callable method object with the given name."""
1204 | # If name refers to a class method, then return a callable object
1205 | # for the class method with self.__dict__['ptr'] as hidden first parameter.
1206 | if not name.endswith('_'):
1207 | method = cache_class_property_accessor(self, name)
1208 | if method:
1209 | return ObjCBoundMethod(method, self.__dict__['ptr'])()
1210 |
1211 | method = cache_class_method(self, name)
1212 | if method:
1213 | return ObjCBoundMethod(method, self.__dict__['ptr'])
1214 |
1215 | # Otherwise, raise an exception.
1216 | raise AttributeError('ObjCClass %s has no attribute %s' % (self.name, name))
1217 |
1218 | def __setattr__(self, name, value):
1219 | # Set the value of an attribute.
1220 | method = cache_class_property_mutator(self, name)
1221 | if method:
1222 | ObjCBoundMethod(method, self.__dict__['ptr'])(value)
1223 | return
1224 |
1225 | raise AttributeError('ObjCClass %s cannot set attribute %s' % (self.__dict__['name'], name))
1226 |
1227 | ######################################################################
1228 |
1229 | class ObjCInstance(object):
1230 | """Python wrapper for an Objective-C instance."""
1231 |
1232 | _cached_objects = {}
1233 |
1234 | def __new__(cls, object_ptr):
1235 | """Create a new ObjCInstance or return a previously created one
1236 | for the given object_ptr which should be an Objective-C id."""
1237 | # Make sure that object_ptr is wrapped in a c_void_p.
1238 | if not isinstance(object_ptr, c_void_p):
1239 | object_ptr = c_void_p(object_ptr)
1240 |
1241 | # If given a nil pointer, return None.
1242 | if not object_ptr.value:
1243 | return None
1244 |
1245 | # Check if we've already created an python ObjCInstance for this
1246 | # object_ptr id and if so, then return it. A single ObjCInstance will
1247 | # be created for any object pointer when it is first encountered.
1248 | # This same ObjCInstance will then persist until the object is
1249 | # deallocated.
1250 | if object_ptr.value in cls._cached_objects:
1251 | return cls._cached_objects[object_ptr.value]
1252 |
1253 | # Otherwise, create a new ObjCInstance.
1254 | objc_instance = super(ObjCInstance, cls).__new__(cls)
1255 | objc_instance.__dict__['ptr'] = object_ptr
1256 | objc_instance.__dict__['_as_parameter_'] = object_ptr
1257 | # Determine class of this object.
1258 | class_ptr = c_void_p(objc.object_getClass(object_ptr))
1259 | objc_instance.__dict__['objc_class'] = ObjCClass(class_ptr)
1260 |
1261 | # Store new object in the dictionary of cached objects, keyed
1262 | # by the (integer) memory address pointed to by the object_ptr.
1263 | cls._cached_objects[object_ptr.value] = objc_instance
1264 |
1265 | # Create a DeallocationObserver and associate it with this object.
1266 | # When the Objective-C object is deallocated, the observer will remove
1267 | # the ObjCInstance corresponding to the object from the cached objects
1268 | # dictionary, effectively destroying the ObjCInstance.
1269 | observer = send_message(send_message('DeallocationObserver', 'alloc'), 'initWithObject:', objc_instance)
1270 | objc.objc_setAssociatedObject(objc_instance, observer, observer, 0x301)
1271 |
1272 | # The observer is retained by the object we associate it to. We release
1273 | # the observer now so that it will be deallocated when the associated
1274 | # object is deallocated.
1275 | send_message(observer, 'release')
1276 |
1277 | return objc_instance
1278 |
1279 | def __repr__(self):
1280 | if self.__dict__['objc_class'].__dict__['name'] == '__NSCFString':
1281 | # Display contents of NSString objects
1282 | from .core_foundation import to_str
1283 | string = to_str(self)
1284 | return "" % (id(self), self.__dict__['objc_class'].name, string, text(self.__dict__['ptr'].value))
1285 |
1286 | return "" % (id(self), self.__dict__['objc_class'].name, text(self.__dict__['ptr'].value))
1287 |
1288 | def __getattr__(self, name):
1289 | """Returns a callable method object with the given name."""
1290 | # Search for named instance method in the class object and if it
1291 | # exists, return callable object with self as hidden argument.
1292 | # Note: you should give self and not self.__dict__['ptr'] as a parameter to
1293 | # ObjCBoundMethod, so that it will be able to keep the ObjCInstance
1294 | # alive for chained calls like MyClass.alloc().init() where the
1295 | # object created by alloc() is not assigned to a variable.
1296 |
1297 | # If there's a property with this name; return the value directly.
1298 | # If the name ends with _, we can shortcut this step, because it's
1299 | # clear that we're dealing with a method call.
1300 | if not name.endswith('_'):
1301 | method = cache_instance_property_accessor(self.__dict__['objc_class'], name)
1302 | if method:
1303 | return ObjCBoundMethod(method, self)()
1304 |
1305 | method = cache_instance_method(self.__dict__['objc_class'], name)
1306 | if method:
1307 | return ObjCBoundMethod(method, self)
1308 |
1309 | # Otherwise raise an exception.
1310 | raise AttributeError('ObjCInstance %s has no attribute %s' % (self.__dict__['objc_class'].name, name))
1311 |
1312 | def __setattr__(self, name, value):
1313 | # Set the value of an attribute.
1314 | method = cache_instance_property_mutator(self.__dict__['objc_class'], name)
1315 | if method:
1316 | ObjCBoundMethod(method, self)(value)
1317 | return
1318 |
1319 | raise AttributeError('ObjCInstance %s cannot set attribute %s' % (self.__dict__['objc_class'].name, name))
1320 |
1321 | ######################################################################
1322 |
1323 | # Instances of DeallocationObserver are associated with every
1324 | # Objective-C object that gets wrapped inside an ObjCInstance.
1325 | # Their sole purpose is to watch for when the Objective-C object
1326 | # is deallocated, and then remove the object from the dictionary
1327 | # of cached ObjCInstance objects kept by the ObjCInstance class.
1328 | #
1329 | # The methods of the class defined below are decorated with
1330 | # rawmethod() instead of method() because DeallocationObservers
1331 | # are created inside of ObjCInstance's __new__ method and we have
1332 | # to be careful to not create another ObjCInstance here (which
1333 | # happens when the usual method decorator turns the self argument
1334 | # into an ObjCInstance), or else get trapped in an infinite recursion.
1335 |
1336 | NSObject = ObjCClass('NSObject')
1337 |
1338 | class DeallocationObserver(NSObject):
1339 |
1340 | observed_object = objc_ivar(c_void_p)
1341 |
1342 | @objc_rawmethod('@@')
1343 | def initWithObject_(self, cmd, anObject):
1344 | self = send_super(self, 'init')
1345 | self = self.value
1346 | set_instance_variable(self, 'observed_object', anObject, c_void_p)
1347 | return self
1348 |
1349 | @objc_rawmethod('v')
1350 | def dealloc(self, cmd):
1351 | anObject = get_instance_variable(self, 'observed_object', c_void_p)
1352 | ObjCInstance._cached_objects.pop(anObject, None)
1353 | send_super(self, 'dealloc')
1354 |
1355 | @objc_rawmethod('v')
1356 | def finalize(self, cmd):
1357 | # Called instead of dealloc if using garbage collection.
1358 | # (which would have to be explicitly started with
1359 | # objc_startCollectorThread(), so probably not too much reason
1360 | # to have this here, but I guess it can't hurt.)
1361 | anObject = get_instance_variable(self, 'observed_object', c_void_p)
1362 | ObjCInstance._cached_objects.pop(anObject, None)
1363 | send_super(self, 'finalize')
1364 |
--------------------------------------------------------------------------------